nikkie-ftnextの日記

イベントレポートや読書メモを発信

好きな映画の鑑賞ログをGitHubのContributionのような形式で表示する(前編:Cal-heatmapを試す)

はじめに

いま、幸せ? nikkieです1

アイの歌声を聴かせて × エンジニアリングで楽しく手を動かしています。
今回は、『アイの歌声を聴かせて』の鑑賞ログをGitHubのContributionのように表示してみる中で知ったことを記事にします。
以下のツイートのような感じになりました。

目次

前提:GitHubのContribution

エンジニアが設計図やソースコードを公開・共有するのに使うGitHub
GitHubアカウントのプロフィールには、コントリビューションカレンダーがあります。
これは日毎のコントリビューションを、数が多いほど濃い色で示すというものです。
緑色でカレンダーが塗りつぶされていくので、「草」🌱という名前でも親しまれていますね2

このコントリビューションカレンダーと同様に、好きな映画(今回は、アイの歌声を聴かせて)の鑑賞回数を表せないかと思ったわけです。
なお、多数派ではないことは承知の上で、私は好きな映画は映画館で何回も見る勢です。

手を動かす中で知ったのですが、「カレンダーで表現したヒートマップ」とも言うようです。

ライブラリ選定(Cal-heatmapを選択)

そもそもどう実現するかというところから分からなかったので、それっぽいワードでGoogle検索するところからスタートです。
調べていくうちに、大変有用なまとめに出会いました。

Github Contributions Graphのようなheatmapを作成したいので、ライブラリを探している。
これといった定番は見つからず、メンテナンスされていないものばかりだった。

リストアップされたものは、ほとんどが「メンテされていない」のですが、私のケースは、サービスとしてリリースしたいわけでなく、一人で楽しめればいいのでメンテされていないライブラリでも試すことにしました。
VueもReactも入門レベルには達していないので、これら向けではないライブラリからCal-heatmapを選びました。
試している記事 cal-heatmapを使ってGithubの草を再現してみる - ソースコードから理解する技術-UnderSourceCode を調査の早い段階で見つけていたのも選択理由です。

※私のフロントエンドのスキルには広大な伸びしろが広がっていることは自覚しているので、お気づきの点あれば知らせてもらえるとありがたいです。

実装:ドキュメントに沿って

Cal-heatmapのドキュメントに沿って、アイの歌声を聴かせての鑑賞回数を示すカレンダーが表示できるように設定していきます。

でき上がったコードはこんな感じ(じゃーん!)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
    <script
      type="text/javascript"
      src="https://cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.min.js"
    ></script>
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.css"
    />
  </head>
  <body>
    <h1>『アイの歌声を聴かせて』鑑賞履歴</h1>
    <div id="cal-heatmap" style="margin: auto; width: 30%;"></div>

    <script type="text/javascript">
      const cal = new CalHeatMap();
      const data = {
        "1635519600.0": 1,
        "1635865200.0": 1,
      };
      const config = {
        domain: "month",
        subDomain: "day",
        range: 4,
        tooltip: true,
        start: new Date(2021, 9), // start October, 1st 2021
        data: data,
        legend: [0.0, 0.5, 1.0, 1.5],
        cellSize: 15,
        subDomainTextFormat: "%d",
      };
      cal.init(config);
    </script>
  </body>
</html>

設定ポイント

cal.init({})と空のオブジェクトを渡すだけで、空のカレンダーが表示されます。
カレンダーに渡すデータ(いつに何回)やカレンダーの表示形式は、initメソッドに渡すオブジェクトで設定できます。
ポイントと思ったところを書き出します。

itemSelector

https://cal-heatmap.com/#itemSelector

デフォルトでは#cal-heatmap(つまりid="cal-heatmap")のノードにカレンダーが表示されます。
<div id="cal-heatmap">というタグを作っていますよね。

以上、魔法に見える部分の種明かしでした。

domain > subDomain

https://cal-heatmap.com/#domain

GitHubのコントリビューションカレンダーのように表示する設定を探したところ、

  • domain: "month"
  • subDomain: "day"

に落ち着きました。
GitHubとは違って、月の間に空白がある(連続しているように見えにくい)のですが、何月何日というのが分かるのでしっくり来ています3

ドキュメントのExampleにdomainとsubDomainの組合せが示されていて、参考にしました。
x_dayのように指定すると、左から右、上から下の順で並び、よく見るカレンダー表示になります (コントリビューションカレンダーは上から下、左から右の順だったということですね)。

月の数の表示(rangestart

domainの数をrangeで指定します。
2021年10月公開で今月まで表示したいので、4ヶ月(range=4)ですね。

https://cal-heatmap.com/#range

カレンダーの始まりをstartで指定します。 公開月の10月始まりにするには、new Date(2021, 9)ひと月前startに指定する必要がありました4

https://cal-heatmap.com/#start

data(データの形式)

いよいよ「いつに何回」というデータの指定です。

https://cal-heatmap.com/#data

dataTypeがデフォルトでは"json"に指定されています。
なのでdataにはオブジェクトを指定すれば、カレンダーにヒートマップとして反映されます。

オブジェクトの形式は以下のようになります。

{
    "timestamp(秒)": 値(今回は回数),
    "1635519600.0": 1,
}

https://cal-heatmap.com/#data-format

ここで、日付をtimestampに変換するためにPythonを使いました5

>>> datetime.strptime("2021/10/30", "%Y/%m/%d").timestamp()
1635519600.0

日付の文字列からdatetimeオブジェクトを作りtimestampメソッド6を呼び出すことで、floatのタイムスタンプが手に入ります7

legend

凡例(legend)はデフォルトで[10, 20, 30, 40]と指定されています。

https://cal-heatmap.com/#legend

映画の鑑賞回数は1日あたり数回なので、1回観たら緑、2回観ていたら濃い緑のように、指定する配列を変えて色がつく区間を調整する必要がありました。

ここまでで以下のように表示されました。
ブラウザでHTMLを開いています(file:///で始まるURLになっています)。

f:id:nikkie-ftnext:20220110120327p:plain

JSONファイルにまとめたデータを渡そうとしたら・・

ドキュメントのdataの項目には、オブジェクトの代わりに文字列も渡せるという記載があります。
実際ExampleでもJSONファイルの名前を渡しています。

キー:タイムスタンプ、値:鑑賞回数のJSONを作り、

$ head -n5 commits_ainouta.json
{
  "1635519600.0": 1,
  "1635865200.0": 1,
  "1636297200.0": 1,
  "1636556400.0": 1,

ファイル名をdataに指定してみたところ・・・

- data: data,
+ data: "commits_ainouta.json",

カレンダーは色づかなくなり、開発者ツールのコンソールに「Reason: CORS request not HTTP」と表示されました。
CORSエラーと呼ばれるもののようです。

前編はここまでとし、次回はこのエラーの解決をまとめます(手元で解決しており、後編は近日公開予定です)。


  1. コントリビューションの数え方については 近況報告:100DaysOfContribution 達成しました!💚 - nikkie-ftnextの日記 をどうぞ

  2. subDomainTextFormatで「何日」も示すようにしました。ref: https://cal-heatmap.com/#subDomainTextFormat

  3. 2000/01/15始まりにするには new Date(2000, 0, 15)ひと月前の同じ日を指定するようです

  4. 書いている中で気付いたのですが、Cal-heatmapのafterLoadData()コールバックでJavaScript側でタイムスタンプに加工することもできそうです

  5. https://docs.python.org/ja/3/library/datetime.html#datetime.datetime.timestamp

  6. https://stackoverflow.com/questions/9637838/convert-string-date-to-timestamp-in-python#comment34554753_9637908 で知りました