SQLite.CodeFirstによるデータベース作成時に初期データをINSERTする

社内で野良ナレッジマネジメントサイトを立ち上げようと令和にASP.NETに挑戦しています。IISは使えるっぽいのでASP.NETならファイルダウンロードしてすぐ動かせるかなと。

環境の移築に強いサイトを目指して、データベースはSQLiteを採用しようと考えています。アプリ起動時にデータベースを作成するのですが、作成と同時に初期データをINSERTするのに数日間悩んでいたため記録を残しておく。

前提

たぶんこのあたり。

やる

一部実コードから書き換えているので動かなかったらすまん。

Web.configに接続情報を追加する。

  <connectionStrings>
    <add name="MyApp" connectionString="Data Source=|DataDirectory|\data.sqlite;" providerName="System.Data.SQLite" />
  </connectionStrings>

エンティティクラスを作る。

    [Table("Settings")]
    public class Settings
    {
        [Key]
        public string Key { set; get; }

        [Required]
        public string Value { set; get; }
    }

DbContextを継承したクラスを作る。

    public class MyAppDbContext : DbContext
    {

        public MyAppDbContext() : base("MyApp")
        {
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            var sqliteConnectionInitializer = new MyAppSqliteCreator(modelBuilder);
            Database.SetInitializer(sqliteConnectionInitializer);
        }

        public DbSet<Settings> Settings { get; set; }
    }

SetInitializerに渡すインスタンスを独自クラスにするのがミソ。
このクラスはSqliteCreateDatabaseIfNotExistsを継承したクラスで、Seedメソッドをオーバーライドしたもの。

    public class MyAppSqliteCreator : SqliteCreateDatabaseIfNotExists<MyAppDbContext>
    {
        public MyAppSqliteCreator(DbModelBuilder modelBuilder) : base(modelBuilder)
        {
        }

        protected override void Seed(MyAppDbContext context)
        {
            context.Settings.Add(new Settings { Key = "AppName", Value = "MyApp" });
        }
    }

あとはGlobal.asax.csあたりでデータベース参照すれば勝手に作られるはず。おそらく。

Twitterのブロックを一括で解除する。(JavaScriptがある程度わかる人向け)

注意

本記事記載の内容を実行したことによる損害等に対して一切の責任を負いません。自己責任で実行をお願いします。

事前準備

事前にデータのアーカイブをダウンロードしておく必要があります。
全ツイート履歴とツイートをダウンロードする方法 | Xヘルプ

やり方

TwitterXを開いたタブで開発者ツールを開きます。
開発者ツールのコンソールで以下のコードを実行します。

window.YTD = {};
window.YTD.block = {};

これはダウンロードしたブロックアカウントの情報がwindow.YTD.block内のpart0配列に格納されているため、あらかじめ上位構造を空のオブジェクトを作成しておくものです。

次にブロックアカウントの一覧を読み込みます。ダウンロードしたアーカイブデータ内のdataフォルダにblock.jsというファイルがあるため、この中身を全部コンソールにコピペ、実行します。

最後に、ブロック解除のリクエストを送信します。以下のコードをコンソールで実行します。

function sendBlockDestroyRequest(id) {
  const xhr = new XMLHttpRequest();
  xhr.open("POST", "https://x.com/i/api/1.1/blocks/destroy.json");
  xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  xhr.setRequestHeader("authorization", "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA");
  xhr.setRequestHeader("x-twitter-auth-type", "OAuth2Session");
  xhr.setRequestHeader("x-csrf-token", document.cookie.split("; ").find((row) => row.startsWith("ct0="))?.split("=")[1]);
  const body = `user_id=${id}`;
  xhr.send(body);
}

let i = 0;

let timerId = window.setInterval(function() {
  sendBlockDestroyRequest(window.YTD.block.part0[i].blocking.accountId);
  i++;
  if (i == window.YTD.block.part0.length) {
    window.clearInterval(timerId);
  }
}, 500);

やっていることは正規のブロック解除リクエストを模倣したリクエストを0.5秒ごとに送信しているだけです。あまりブロックアカウントの数が多いとレート制限に引っかかってしまうかもしれません。もし引っかかったらブロックリストの配列長を調整するなどいい感じにやってください。
404エラーが返ってくることがありますが、これは削除済みアカウントに対してリクエストを飛ばすと返ってきます。

アカウント数にもよりますが時間はかかりますのでコーヒーなり紅茶なり飲んで待ちましょう。

これをするに至った背景

完全な私的アカウントを別用途に転用するために一度ブロック全解除しようとしたのですが、公式Webのブロックリストではブロック済みアカウントはあるのに表示されず……アーカイブにブロックリストが運よく含まれていたため、それを基にブロック解除のリクエストを送信することにしました。

備忘録: Three.jsで軸方向を変える(注意点あり)

Three.jsでは座標軸の垂直方向がBlenderのz軸とは異なりy軸になっています。
それに伴い、LookAtメソッドなどで視点を変えるとy軸を垂直方向として回転などは計算されます。
これは

THREE.Object3D.DEFAULT_UP = new THREE.Vector3(0, 0, 1);

などをしてObject3D.DEFAULT_UPプロパティの値を変えればだいたいのオブジェクトについては動作します。
ただし、Three.js派生のアドオンなど(FirstPersonControlsとか)は内部的にy軸を垂直方向とした計算をしているようなので、特段の事情がない限りはy軸を垂直方向として各種設定をするのが無難のようです。

これを調べるに至ったのは、過去のFirstPersonControlsにてlon, latプロパティを設定して見ている方向変えられるようだったのですが、いつの間にか廃止されていたためコード読んで色々調べたところ、カメラ方向計算がLookAtメソッド依存、ただそのほかにも操作系のコード見るとポインタ位置に対する操作軸がどうにも固定そうだったのでこのような結論に至りました。

初めてのThree.jsを読む。あとドーナツ大回転。

読んだもの

www.oreilly.co.jp

読んでみて

使いどころが難しいですね。映像作品作るならBlenderなりで直接作ればいいわけであって、Webで遊べるゲーム作りたいならUnityなりでいいんじゃないかという気もします。
動的にコンテンツをロードして連携するとか、そういうあたりになるのでしょうか。それでもインタラクティブに動かすというのは結構難しそうな印象。作ろうかなと思っているものはありますけどね。
ひとまずこれを読んでおけばだいたいのものは作れるんじゃないかなーと思います。

ドーナツ大回転

Blenderの某チュートリアルでおなじみのドーナツを回転させるやつをせっかくなのでThree.jsで作ってみました。
これ。
rutilicus.github.io

Google スプレッドシートでYouTubeの埋め込みプレーヤーを表示させる。でもあまり使えない。

まとめ

  • Google Apps Scriptを使ってスプレッドシートのサイドバーにYouTubeの埋め込みプレーヤーを表示させるよ!
  • でもGoogle Apps Scriptの実行は編集権限がないとできないよ!
  • つまり閲覧権限だけしかない場合は実行できないよ!

実行イメージ

www.youtube.com
ちなみにサーモン爆破は10周年らしいです。マジか……

やったこと

以下の2ファイルを作成。

code.gs

function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('Player').addItem('Show Player', 'showPlayer').addToUi();
}

function showPlayer() {
  const ui = SpreadsheetApp.getUi();
  const html = HtmlService.createTemplateFromFile('player').evaluate();
  html.setTitle('Player');
  ui.showSidebar(html);
}

function getCurrentRowData() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const range = sheet.getActiveCell();
  const id = sheet.getRange(range.getRow(), 1).getValue();
  const start = sheet.getRange(range.getRow(), 3).getValue();
  return {'id': id, 'start': start};
}

player.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <div id="ytplayer"></div>
    <div>
      <input type="button" value="Start"
        onClick="google.script.run.withSuccessHandler(playVideo).getCurrentRowData()">
    </div>
    <script>
      var tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('ytplayer', {
          height: '200',
          width: '200',
          videoId: '',
          events: {}
        });
      }

      function playVideo(videoInfo) {
        player.loadVideoById({'videoId': videoInfo.id,
                              'startSeconds': videoInfo.start});
      }
    </script>
  </body>
</html>

これを拡張機能のApps Scriptで設定後、再度スプレッドシートを開くとメニューバーに「Player」が表示、その中の「Show Player」を押下するとサイドバーにYouTubeの埋め込みプレーヤーが表示されます。
サイドバーの「Start」ボタンを押下すると、スプレッドシート中で現在選択中の行の情報で再生を開始します。

問題点

  • サイドバーの幅は300px固定のため、映像は見えないものと思ってください。音を聞く用途なら使えると思います。最初これやろうと思ったのもその用途が目的でした。
  • Apps Scriptが編集権限がないと実行できないらしく、閲覧権限のみでスプレッドシートを開いてもメニューバーに「Player」が表示されません。

結論

GitHub - rutilicus/youtube-custom-music-playerのいい代替手段にはならなかった。(ダイマ)

読んだ: 曖昧性とのたたかい / 曖昧性との共存

www.shoeisha.co.jp
www.shoeisha.co.jp

システム構築は常に曖昧性が伴い、仕様が膨張するものではあるが、だからと言って曖昧な状態であるまま先延ばししない、先を予見して起こるかもしれない事象に先手を打って対処する……という内容の本であったと思う。
受注段階から関係するマネージャ向けの要素が多いが、その属性に該当しない私にも役に立つ部分が多かったように思う。目下のところ新人の育成どうするか……目立ちやすいところをあえてやらせるとあったけど本当にやらせてよいものか悩む。

AtomフィードをCSVのテンプレートから作るWebアプリをゆるっと作ってみる。

導入

Twitter凍結騒ぎで一部で話題になった(?)RSSフィードAtomフィード。仕様を知らなかったのでAtom(特に配信フォーマット)を調べてみるついでに作る方もゆるっと実装してみました。

今回調べるにあたってRFC 4287 The Atom Syndication Format 日本語訳を拝見しました。この場を借りてお礼申し上げます。

つくるもの

出力内容文法

Atom配信フォーマットのサブセットをゆるっと定義します。厳密さはほぼほぼ考えていません。ゆるっと。
 \langle\mathit{atom}\rangle::=\langle\mathit{xmldecl}\rangle\langle\mathit{feed}\rangle
 \langle\mathit{xmldecl}\rangle::={\tt < ?xml\ version="1.0"\ encoding="utf-8"? >}
 \langle\mathit{feed}\rangle::={\tt < feed\ xmlns="http:// www .w3.org/2005/Atom" >}\langle\mathit{feedmeta}\rangle\langle\mathit{entries}\rangle{\tt < /feed >}
 \langle\mathit{feedmata}\rangle::=\langle\mathit{author}\rangle\langle\mathit{id}\rangle\langle\mathit{link}\rangle\langle\mathit{title}\rangle\langle\mathit{updated}\rangle
 \langle\mathit{author}\rangle::={\tt < author >< name >}\langle\mathit{string}\rangle{\tt < /name > < /author >}
 \langle\mathit{entries}\rangle::=\langle\mathit{entry}\rangle | \langle\mathit{entry}\rangle\langle\mathit{entries}\rangle
 \langle\mathit{entry}\rangle::={\tt < entry >}\langle\mathit{id}\rangle\langle\mathit{link}\rangle\langle\mathit{title}\rangle\langle\mathit{updated}\rangle\langle\mathit{summary}\rangle{\tt < /entry >}
 \langle\mathit{summary}\rangle::={\tt < summary\ type="}\langle\mathit{type}\rangle{\tt " >}\langle\mathit{string}\rangle{\tt < /summary >}
 \langle\mathit{id}\rangle::={\tt < id >}\langle\mathit{url}\rangle{\tt < /id >}
 \langle\mathit{link}\rangle::={\tt < link\ href="}\langle\mathit{url}\rangle{\tt "/>}
 \langle\mathit{title}\rangle::={\tt < title >}\langle\mathit{string}\rangle{\tt < /title >}
 \langle\mathit{updated}\rangle::= {\tt < updated >}\langle\mathit{date}\rangle{\tt < /updated >}
 \langle\mathit{type}\rangle::= {\tt text} | {\tt html}
 \langle\mathit{string}\rangle::=\mathrm{任意のエスケープ済文字列}
 \langle\mathit{url}\rangle::=\mathrm{URL文字列}
 \langle\mathit{date}\rangle::=\mathrm{YYYY-MM-DDThh:mm:ss+09:00形式の日付}
本来IDはリソースが移転しても同一であることが求められているためURL文字列をそのままIDとするのは不適切ですが……ゆるっと作るためここは無視します。

ユーザ入力検討

文法定義より、feed部の入力要素はauthor, URL(link), title, 日付(updated)の4要素、各entry部の入力要素はURL(link), title, 日付(updated), summary, type(summary)の5要素となりますが、feed部のupdatedはentry部の最新のupdatedをそのまま採用するものとしてユーザ入力はなしとします。

入出力定義

feed部のcsvとentry部のcsvの2入力とし、作成したAtomフィード(.atomファイル)を出力とします。
feed部のcsvはauthor, title, URLの3列からなる1行のcsvファイル、entry部のcsvはtitle, type, summary, URL, updatedの5列からなるn行(n > 0)のcsvファイルとします。

つくった

github.com