Qiita殿堂入りアプリを作りました（二番煎じ）

## はじめに

Qiita殿堂入りアプリを作りました。
暇な時にご活用ください。

## Qiita殿堂入りのホームページ

■URL
https://matt-note.github.io/qden/
![スクリーンショット 2020-12-17 1.38.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288331/10b3fa99-4d0b-0e02-39c0-f8ccc051e8ce.png)


デザインは[阿部寛のホームページ](http://abehiroshi.la.coocan.jp/)を参考に作成しています。
デザインをAbehiroshize（アベヒロシャイズ）することにより、実行速度と体感速度の向上を実現しています。

PWAに対応しているので、スマホのホーム画面に追加することができます。
![Screenshot_20201218-020658.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288331/43ce7eff-764c-6c22-ab40-95c0506f6931.jpeg)


## もうあるやん？

Qiitaの殿堂入りについては、すでに[Qiitaの殿堂](http://youwht.ga/)というサイトがあります。

このサイトがあると初めて知った時にアクセスしたのですが、数日間アクセスできなかったので、「サービス終了してしまったのか。じゃあ自分で作ろう。」と考えたのが、Qiita殿堂入りアプリを作成するキッカケでした。（後になってから上記サイトが復旧してアクセスできることを知りました…。）

## 技術構成

とにかく運用コストを抑えたかったので 「GitHub Pagesを使用 + GitHub Actionsの`cron`でQiitaの月ごとのランキングを毎日自動取得してJSONファイルに書き出す」構成にしました。JSONファイルは`curl`と`jq`で作成しています。

アプリでは作成済みJSONファイルをJavaScriptで取得してHTMLに表示しています。フロントエンドもライブラリのバージョン対応をしたくなかったので、Vanilla JavaScriptで作成しています。



## 仕様

### 1. 殿堂入りの定義
[Qiita殿堂入りのホームページ](https://matt-note.github.io/qden/)では、殿堂入りの定義を「**ストック数が262以上のもの**」としました。これはQiita APIにクエリを投げる時に、ストック数で絞り込みができるための都合です。ストック数なので、**LGTM数ではありません**（LGTM数では絞り込みができないため）。そのため、262より少ないのLGTM数であってもストック数が262以上であれば殿堂入りとしてカウントします。


262という数字については、**イチローが2004年に達成したシーズン262安打を基準**としています。イチローだったら米国野球殿堂入りするだろうということで、この数字をQiita殿堂入りの基準にしています。

### 2. 表示LGTM数
作成済みのJSONファイルを表示しているので、リアルタイムのLGTM数を表示しているわけではありません。あくまで**参考**としてご活用ください。

### 3. 月の記事上限数
月の記事上限数は**100件まで**です。これは１回のリクエストで100件まで取得しているためです。（Qiita APIの仕様。このため、ストック数が262以上でも100件より先は表示されません）

### 4. Qiita APIの仕様

表示についてはデータ取得元のQiita APIの仕様に基づいています。とにかく、Qiita APIが返した値を表示しているので、「11月に投稿した記事が10月に表示されている」ということがありえます。これは11月1日の0:15に投稿した場合など、月の境界部分についてはこういうことがありえます。（細かいことは不明）

### 5. 作成日時
殿堂入りの年月は**作成日時**を基に表示しています。限定公開してから公開した場合は、限定公開した最初の時が作成日時となります。

### 6. 更新日時

殿堂入りした記事の中で、作成日時と更新日時の**年月が違う場合**に、更新日時を表示する仕様としています。同じ年月で日にちが違う時に更新した場合、更新日時は表示されない仕様です。

### 7. 取得期間
[Wikipedia](https://ja.wikipedia.org/wiki/Qiita)によると、Qiitaが公開されたのは2011年9月16日とのことなので、2011年9月から条件を満たす記事を取得しています。

## こういう事さ！

ここまでをザックリまとめると下記の図となります。
GitHub Actionsの`cron`で`curl`を実行し、Qiita APIにアクセスします。ストック数が262以上のものを取得し、`jq`で整形してJSONという[気味の悪い拡張子](https://www.itmedia.co.jp/news/articles/1806/08/news065.html)で書き出します。その後は`commit`・`push`してリポジトリを更新しています。これを毎日深夜に自動で実行しています。
アプリからは作成済みJSONファイルを取得して描画しています。

![B5CA7MyCIAI_O2K.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288331/2445b033-0c2e-079c-1f84-410c9fe170a5.jpeg)


## 開発にまつわるエトセトラ🦀

ここからはアプリ作成での学びを記載します。

### 1. quicklink

Qiita殿堂入りのホームページで唯一使用しているJSライブラリが[quicklink](https://github.com/GoogleChromeLabs/quicklink)です。
これは画面表示領域のリンクをprefetchしてくれるライブラリで、ユーザがリンクをクリックした場合に、prefetch済みデータを読み込むので処理速度が向上します。これはAbehiroshize（アベヒロシャイズ）するための必須ライブラリでした。使い方はREADME.mdに書かれている通りです。

```JavaScript
// 'load'を指定するのがポイント。リンクのprefetchは画像などページのすべてが読み込まれた後で良いため、
// DOMContentLoaded（HTML文書の解析完了時）でない方が良い。
// 細かいカスタマイズはREADME.mdを参照。
window.addEventListener('load', () =>{
  quicklink.listen();
});
```

### 2. Qiita APIで「以上」「以下」を指定できない
Qiita APIで指定するクエリでは`>=`の指定ができません。そのため、262以上のストックがある記事を絞り込みたい場合は**261より多い**を指定します。なおかつ、11月1日からデータを取得したい場合、開始日付は10月31日のように**月末**を指定します。（`query=created:>2020-11-01`だと、11月1日の記事が含まれない。）

```bash
# 例
curl -G \
  --data-urlencode "query=created:>2020-10-31 created:<2020-12-01 stocks:>261" \
  --data-urlencode "page=1" \
  --data-urlencode "per_page=10" \
https://qiita.com/api/v2/items | \
jq '. | map({ title: .title?, url: .url?, likes_count: .likes_count?, created_at: .created_at?, updated_at: .updated_at?, id: .user.id?}) | sort_by(.likes_count) | reverse'
```

なお、上記コマンドの実行結果に2020年11月01日 06時13分に作成された[Pythonのオブジェクト指向プログラミングを完全理解](https://qiita.com/kaitolucifer/items/926ed9bc08426ad8e835)の記事が含まれていません。この記事は2020年10月の記事と判定されています。このあたりはQiita APIの仕様です。（**細かいことは不明**）

### 3. curlの`--data-urlencode`オプション
上記の例のとおり、curlの`--data-urlencode`を指定すると、URLエンコードした文字を指定する必要がないので、コードの可読性が向上します。直書きでURLエンコードする場合は、下記のように記述することになります。(下記リンク押下でアクセス可能。10件のみ取得)

https://qiita.com/api/v2/items?query=created%3A%3E2020-10-31%20created%3A%3C2020-12-01%20stocks%3A%3E261&page=1&per_page=10

### 4. Macの`date`コマンド
Macの`date`コマンドはBSD Unixベースのため、GNU Linuxの`date`コマンドと差異があります。GitHub ActionsではUbuntuを使いたかったので、Macで作成したスクリプトをUbuntuで実行できず、困りました。（ローカルで確認できず、GitHub Actionsを動かして動作確認するハメに…。パソコンの買い替え時ですかね…。）


### 5. GitHub ActionsのcronはUTC

JST(Japan Standard Time: 日本標準時)で`cron`を実行したい場合は、UTCから9時間差し引いた時間を指定します。環境変数でタイムゾーンを設定すれば、そのままJSTで指定できるのではと思いましたが、できませんでした。

ちなみにUTC(協定世界時)は英語で"[Coordinated Universal Time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time)"なので、頭文字はCUT。フランスが"Temps Universel Coordonné"の頭文字であるTUCを提案してきたので、なんやかんやあってみんなで仲良くUTCを使うようになった。
・参考: [UTC(協定世界時)は何の頭文字か](https://cpplover.blogspot.com/2012/11/utc.html)

```yaml
# 例 JSTの0:00に実行する場合、UTCの15:00を指定する
on:
  schedule:
    - cron: '0 15 * * *'
```


### 6. classList
JavaScriptでHTML要素の表示・非表示を切り替えるためにclassを操作したい時は[classList](https://developer.mozilla.org/ja/docs/Web/API/Element/classList)が大活躍してくれました。`toggle()`で指定したclassの切り替えができて、`contains()`で指定したclassを持つか判定することができます。

<p class="codepen" data-height="344" data-theme-id="light" data-default-tab="js,result" data-user="matt-note" data-slug-hash="yLaMzVm" style="height: 344px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="yLaMzVm">
  <span>See the Pen <a href="https://codepen.io/matt-note/pen/yLaMzVm">
  yLaMzVm</a> by Shinichi Matsumoto (<a href="https://codepen.io/matt-note">@matt-note</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


### 7. QiitaにCodePenを貼り付けできる

QiitaにCodePenを貼り付けできますが、ちょっとしたコツが必要です。
上のCodePenは下記コードを貼り付けたものです。

```html
<p class="codepen" data-height="344" data-theme-id="light" data-default-tab="js,result" data-user="matt-note" data-slug-hash="yLaMzVm" style="height: 344px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="yLaMzVm">
  <span>See the Pen <a href="https://codepen.io/matt-note/pen/yLaMzVm">
  yLaMzVm</a> by Shinichi Matsumoto (<a href="https://codepen.io/matt-note">@matt-note</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<!-- デフォルトの下記scriptタグではQiitaで表示できない -->
<!-- <script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script> -->
<!-- 下記コードに修正することでQiitaでCodePenを表示できる （細かいことは不明） -->
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
```

- 参考: [https://qiita.com/Qiita/items/edae7417214c8e957f54#comment-075abca98e42be84dada](https://qiita.com/Qiita/items/edae7417214c8e957f54#comment-075abca98e42be84dada)


## さいごに

Qiita殿堂入りのホームページ（Q殿）のソースコードは下記の[設計図共有サイト](https://www.itmedia.co.jp/news/articles/1806/05/news069.html)で管理しています。スターを付けてもらえると励みになります💫

- https://github.com/matt-note/qden