Githubでのプログラミング言語利用状況を集計するサービス

GitHubで各言語がどの程度利用されているのか集計するサービスを公開しました。

namari
http://namaristats.com

## サービスの概要

1日1回（UTCの0時1分から）各言語の利用状況を、[GitHub API v3](https://developer.github.com/v3/)で取得したデータを元に算出しています。
ほぼ全てが[Search API](https://developer.github.com/v3/search/)を利用しているため、公開リポジトリのみが対象です。

言語ごとに、リポジトリ数やユーザー数などAPIから標準で取得可能な値と、増加率や更新率など算出した値、スターの多いリポジトリやフォロワーの多いユーザーなどをまとめています。

他に、今のところ以下の機能を用意しています。

* [Rankings](http://namaristats.com/rankings)　わかりやすそうな幾つかの基準で、Top20までのランキングを掲載
* [Top100 Map](http://namaristats.com/top100)　リポジトリのスター数トップ100の、言語の割合をツリーマップで表示
* [Top3 Repos](http://namaristats.com/top3s)　各言語のトップ3リポジトリの一覧
* [DataTable](http://namaristats.com/datatable)　その日の全データをソート可能なテーブルと、CSVのダウンロード


## 対応言語

GitHubの[Advanced Code Search](https://github.com/search/advanced)で選択できる言語を元にしました。ただし、該当するリポジトリが無いものや、少数且つ活動も停止しているようなものを省いて、現時点で158言語です。
なおプログラミング言語と言っても、CSSやHTML、XML、XSLTなども含んだ、プログラミングに関連する特定の言語という程度の基準にしています。

* [JavaScript](http://namaristats.com/langs/JavaScript)
* [Java](http://namaristats.com/langs/Java)
* [Ruby](http://namaristats.com/langs/Ruby)
* [Python](http://namaristats.com/langs/Python)
* [PHP](http://namaristats.com/langs/PHP)
* [CSS](http://namaristats.com/langs/CSS)
* [C++](http://namaristats.com/langs/C++)
* [C](http://namaristats.com/langs/C)
* [C#](http://namaristats.com/langs/C#)
* [Objective-C](http://namaristats.com/langs/Objective-C)
* [Shell](http://namaristats.com/langs/Shell)
* [Perl](http://namaristats.com/langs/Perl)
* [R](http://namaristats.com/langs/R)
* [VimL](http://namaristats.com/langs/VimL)
* [Go](http://namaristats.com/langs/Go)
* [CoffeeScript](http://namaristats.com/langs/CoffeeScript)
* [Scala](http://namaristats.com/langs/Scala)
* [Haskell](http://namaristats.com/langs/Haskell)
* [HTML](http://namaristats.com/langs/HTML)
* [TeX](http://namaristats.com/langs/TeX)
* [Lua](http://namaristats.com/langs/Lua)
* [Clojure](http://namaristats.com/langs/Clojure)
* [Swift](http://namaristats.com/langs/Swift)
* [Emacs Lisp](http://namaristats.com/langs/Emacs_Lisp)
* [Matlab](http://namaristats.com/langs/Matlab)
* [Arduino](http://namaristats.com/langs/Arduino)
* [Groovy](http://namaristats.com/langs/Groovy)
* [ActionScript](http://namaristats.com/langs/ActionScript)
* [Puppet](http://namaristats.com/langs/Puppet)
* [Erlang](http://namaristats.com/langs/Erlang)
* [Visual Basic](http://namaristats.com/langs/Visual_Basic)
* [Processing](http://namaristats.com/langs/Processing)
* [Assembly](http://namaristats.com/langs/Assembly)
* [PowerShell](http://namaristats.com/langs/PowerShell)
* [Common Lisp](http://namaristats.com/langs/Common_Lisp)
* [XML](http://namaristats.com/langs/XML)
* [ASP](http://namaristats.com/langs/ASP)
* [D](http://namaristats.com/langs/D)
* [Scheme](http://namaristats.com/langs/Scheme)
* [Rust](http://namaristats.com/langs/Rust)
* [Dart](http://namaristats.com/langs/Dart)
* [OCaml](http://namaristats.com/langs/OCaml)
* [TypeScript](http://namaristats.com/langs/TypeScript)
* [Pascal](http://namaristats.com/langs/Pascal)
* [XSLT](http://namaristats.com/langs/XSLT)
* [FORTRAN](http://namaristats.com/langs/FORTRAN)
* [Prolog](http://namaristats.com/langs/Prolog)
* [Racket](http://namaristats.com/langs/Racket)
* [F#](http://namaristats.com/langs/F#)
* [Haxe](http://namaristats.com/langs/Haxe)
* [VHDL](http://namaristats.com/langs/VHDL)
* [Verilog](http://namaristats.com/langs/Verilog)
* [Elixir](http://namaristats.com/langs/Elixir)
* [Julia](http://namaristats.com/langs/Julia)
* [ColdFusion](http://namaristats.com/langs/ColdFusion)
* [Tcl](http://namaristats.com/langs/Tcl)
* [Apex](http://namaristats.com/langs/Apex)
* [AutoHotkey](http://namaristats.com/langs/AutoHotkey)
* [IDL](http://namaristats.com/langs/IDL)
* [AppleScript](http://namaristats.com/langs/AppleScript)
* [Vala](http://namaristats.com/langs/Vala)
* [Standard ML](http://namaristats.com/langs/Standard_ML)
* [Mathematica](http://namaristats.com/langs/Mathematica)
* [Objective-C++](http://namaristats.com/langs/Objective-C++)
* [Pure Data](http://namaristats.com/langs/Pure_Data)
* [Cuda](http://namaristats.com/langs/Cuda)
* [OpenEdge ABL](http://namaristats.com/langs/OpenEdge_ABL)
* [LiveScript](http://namaristats.com/langs/LiveScript)
* [SuperCollider](http://namaristats.com/langs/SuperCollider)
* [Ada](http://namaristats.com/langs/Ada)
* [Coq](http://namaristats.com/langs/Coq)
* [Max](http://namaristats.com/langs/Max)
* [Smalltalk](http://namaristats.com/langs/Smalltalk)
* [SQL](http://namaristats.com/langs/SQL)
* [Game Maker Language](http://namaristats.com/langs/Game_Maker_Language)
* [Gosu](http://namaristats.com/langs/Gosu)
* [AutoIt](http://namaristats.com/langs/AutoIt)
* [XQuery](http://namaristats.com/langs/XQuery)
* [Perl6](http://namaristats.com/langs/Perl6)
* [Objective-J](http://namaristats.com/langs/Objective-J)
* [Elm](http://namaristats.com/langs/Elm)
* [Kotlin](http://namaristats.com/langs/Kotlin)
* [Awk](http://namaristats.com/langs/Awk)
* [SourcePawn](http://namaristats.com/langs/SourcePawn)
* [Nimrod](http://namaristats.com/langs/Nimrod)
* [Io](http://namaristats.com/langs/Io)
* [Scilab](http://namaristats.com/langs/Scilab)
* [BlitzBasic](http://namaristats.com/langs/BlitzBasic)
* [Eiffel](http://namaristats.com/langs/Eiffel)
* [Stata](http://namaristats.com/langs/Stata)
* [nesC](http://namaristats.com/langs/nesC)
* [PureScript](http://namaristats.com/langs/PureScript)
* [Agda](http://namaristats.com/langs/Agda)
* [NetLogo](http://namaristats.com/langs/NetLogo)
* [Squirrel](http://namaristats.com/langs/Squirrel)
* [Xtend](http://namaristats.com/langs/Xtend)
* [Forth](http://namaristats.com/langs/Forth)
* [SAS](http://namaristats.com/langs/SAS)
* [DM](http://namaristats.com/langs/DM)
* [ooc](http://namaristats.com/langs/ooc)
* [SystemVerilog](http://namaristats.com/langs/SystemVerilog)
* [UnrealScript](http://namaristats.com/langs/UnrealScript)
* [Mercury](http://namaristats.com/langs/Mercury)
* [Rebol](http://namaristats.com/langs/Rebol)
* [Idris](http://namaristats.com/langs/Idris)
* [Nix](http://namaristats.com/langs/Nix)
* [Gnuplot](http://namaristats.com/langs/Gnuplot)
* [AspectJ](http://namaristats.com/langs/AspectJ)
* [Nemerle](http://namaristats.com/langs/Nemerle)
* [PAWN](http://namaristats.com/langs/PAWN)
* [XC](http://namaristats.com/langs/XC)
* [xBase](http://namaristats.com/langs/xBase)
* [Boo](http://namaristats.com/langs/Boo)
* [CLIPS](http://namaristats.com/langs/CLIPS)
* [GAP](http://namaristats.com/langs/GAP)
* [MoonScript](http://namaristats.com/langs/MoonScript)
* [Ceylon](http://namaristats.com/langs/Ceylon)
* [ANTLR](http://namaristats.com/langs/ANTLR)
* [VCL](http://namaristats.com/langs/VCL)
* [Monkey](http://namaristats.com/langs/Monkey)
* [Arc](http://namaristats.com/langs/Arc)
* [Dylan](http://namaristats.com/langs/Dylan)
* [Slash](http://namaristats.com/langs/Slash)
* [Bro](http://namaristats.com/langs/Bro)
* [Factor](http://namaristats.com/langs/Factor)
* [COBOL](http://namaristats.com/langs/COBOL)
* [Inform 7](http://namaristats.com/langs/Inform_7)
* [Propeller Spin](http://namaristats.com/langs/Propeller_Spin)
* [Oxygene](http://namaristats.com/langs/Oxygene)
* [Lasso](http://namaristats.com/langs/Lasso)
* [Brightscript](http://namaristats.com/langs/Brightscript)
* [Nu](http://namaristats.com/langs/Nu)
* [Pike](http://namaristats.com/langs/Pike)
* [Opa](http://namaristats.com/langs/Opa)
* [Hy](http://namaristats.com/langs/Hy)
* [Mirah](http://namaristats.com/langs/Mirah)
* [J](http://namaristats.com/langs/J)
* [KRL](http://namaristats.com/langs/KRL)
* [Parrot](http://namaristats.com/langs/Parrot)
* [Component Pascal](http://namaristats.com/langs/Component_Pascal)
* [Xojo](http://namaristats.com/langs/Xojo)
* [ABAP](http://namaristats.com/langs/ABAP)
* [Crystal](http://namaristats.com/langs/Crystal)
* [Alloy](http://namaristats.com/langs/Alloy)
* [Bluespec](http://namaristats.com/langs/Bluespec)
* [Frege](http://namaristats.com/langs/Frege)
* [Isabelle](http://namaristats.com/langs/Isabelle)
* [RobotFramework](http://namaristats.com/langs/RobotFramework)
* [Fantom](http://namaristats.com/langs/Fantom)
* [PogoScript](http://namaristats.com/langs/PogoScript)
* [Augeas](http://namaristats.com/langs/Augeas)
* [Clean](http://namaristats.com/langs/Clean)
* [XProc](http://namaristats.com/langs/XProc)
* [Fancy](http://namaristats.com/langs/Fancy)
* [Volt](http://namaristats.com/langs/Volt)
* [Red](http://namaristats.com/langs/Red)
* [ATS](http://namaristats.com/langs/ATS)
* [wisp](http://namaristats.com/langs/wisp)


## 経緯

プログラミング言語の利用状況やシェア、影響力などをまとめて定期的に配信しているものでは、以下が有名です。

* [TIOBE Index](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)
* [PYPL](http://pypl.github.io/PYPL.html)
* [RedMonk](http://redmonk.com/sogrady/2015/01/14/language-rankings-1-15/)
* [GitHut](http://githut.info/)

TIOBE、PYPL、RedMonkの3つは、幾つかの指標を組み合わせて独自のランキングを出すものです。Githutは[GitHub Archive](https://www.githubarchive.org/)を[Google BigQuery](https://cloud.google.com/bigquery/?hl=ja)で解析しています。
それぞれ個性があって面白いですが、マイナーな言語の扱いが小さく、個々の言語も大きな推移としてまとめられてしまうため、もう少し具体的な利用などが見えると良いのに、と感じていました。

その頃、Goの勉強用で作った http://go-gopher.appspot.com/go-on-github で、GitHub APIを使ってGo限定の利用状況を集めた所、想像より面白く、これをなるべく多くの言語に対応させようと始めました。

---

## GitHub API

[namari](http://namaristats.com)は、[GitHub API](https://developer.github.com/v3/)ありきのサービスです。GitHubがリポジトリやユーザーの公開情報をAPIとして提供し、しかもそれが無料で手軽に使えることで成り立っています。

### 制限とアクセストークン

無料のAPIですが、当然リミットが設けられています。APIの種類によってリミットは異なりますが、Search APIの場合、未認証のアクセスでは1分間あたり5アクセスまでとなっています。OAuth等で認証を受けたアクセスの場合、これが1分間あたり20アクセスまで増えます。
※ドキュメントの[Rate limit](https://developer.github.com/v3/search/#rate-limit)による。ただし実際に[リミット確認用のAPI](https://api.github.com/rate_limit)で確認すると、未認証で10、認証で30となっている。ドキュメントが古い？

APIへのアクセスにはOAuthによる認証が標準とされていますが、今回のようにサーバーサイドで単独でAPIを利用するだけなら、"Personal access token"が簡単です。GitHubのユーザー設定の[Authorized applications](https://github.com/settings/applications)から、"Personal access tokens"の"Generate new token"でトークンを生成できます。
このトークンはその名の通り、個人が自身のリポジトリ等にAPI経由でアクセスするためのものですが、今回の用途であるSearch APIでも有効です。


## GitHub APIの問題点

無償で手軽、制限も緩いGitHub APIですが、今回の目的にとっては幾つか問題があります。
なお、今回のサービスでは全くでたらめな結果が変えるわけではないため、これといって対処をしていませんが、GitHub APIを利用する場合は注意した方が良いです。

### 言語の誤判定

GitHubでは、ホストしているコードの言語を自動で判定しています。判定には[github/linguist](https://github.com/github/linguist)が使用されていますが、しばしば誤判定が起こります。[.gitattributesを使って判定を制御](https://github.com/github/linguist#using-gitattributes)できますが、多くのリポジトリでは全自動です。

### Search APIが返す言語

Search APIでリポジトリを検索した場合、結果に含まれる言語は、そのリポジトリで最も分量の多いもの1つになってしまいます。複数の言語が近い分量で使われていても1つですし、付随的なコードの分量が多ければ、そちらが返されてしまいます。

例えば[yahoo/pure](https://github.com/yahoo/pure)の場合、CSSのUIフレームワークなので言語もCSSとして判定してほしいのですが、テスト用の[HTMLがCSSより多く含まれるため](https://api.github.com/repos/yahoo/pure/languages)、Search APIでは使用言語がHTMLとなってしまいます。

### ユーザーの言語

Search APIでは言語によるユーザーの検索も可能ですが、これにも問題があります。リポジトリなどから、ある言語を明らかに使っているユーザーが、その言語で該当しない場合があります。

---

## Google App Engine / Go

サーバー側のプラットフォームには、GoogleのPaaS、[Google App Engine](https://cloud.google.com/appengine/docs)を使っています。クセも強いですが、慣れてしまうと他での開発が面倒でしょうがない、素晴らしいプラットフォームです。
Google App EngineではずっとPythonを使ってきましたが、このサービスから全面的にGoに切り替えました。

サービスの性質やCloudFlareを使っていることなどで、GAE/Goのメリットを充分活かせてはいません。ただ、トータルでとても低コスト且つ、Managed VMなど拡張性も揃ってきています。

今回のサービスの場合、定期更新の動的コンテンツ主体で、コードのほとんどがGitHub API及び取得したJSONの処理です。しかも規模も小さいため、以下のパッケージを使っている程度です。

* [mjibson/goon](http://github.com/mjibson/goon) : Datastoreのラッパ
* [gorilla/mux](http://github.com/gorilla/mux) : ルーティング
* [flosch/pongo2](http://github.com/flosch/pongo2) : テンプレートエンジン

goonもgorilla/muxもシンプルでそう迷いませんが、Twigやjinja2などDjangoライクなテンプレートエンジンに慣れている分、細かい点でpongo2には時々引っかかりました。そこで覚え書きとしてpongo2のひっかかった点。

### pongo2のひっかかった点

#### テンプレートでのプロパティアクセス

DjangoやJinja2では ```forloop.counter``` などと書いて、forのカウントを取得したりします。しかしGoの制限により ```forloop.Counter``` と、プロパティ名の先頭を大文字にする必要があります。

#### カスタムフィルターの登録

カスタムフィルターの登録はテンプレートエンジンごとで色々作法がありますが、pongo2はこの部分のドキュメントが微妙に分かりにくく、[mattn](http://mattn.kaoriya.net/)の[mattn/goji-wiki](https://github.com/mattn/goji-wiki)など参考にしました。

```go
package main

import (
	"fmt"

	"github.com/flosch/pongo2"
	_ "github.com/flosch/pongo2-addons"
)

func setPongoFilters() {
	pongo2.RegisterFilter("filterName", func(in *pongo2.Value, param *pongo2.Value) (out *pongo2.Value, err *pongo2.Error) {
		s, ok := in.Interface().(string)
		if !ok {
			return nil, &pongo2.Error{
				Sender:   "filterName",
				ErrorMsg: fmt.Sprintf("some error %T ('%v')", in, in),
			}
		}
		return pongo2.AsValue(MyFunc(s)), nil
	})
}
```
* github.com/flosch/pongo2-addons を _ でimport
* pongo2.RegisterFilter()に、フィルター名と無名関数による処理内容を渡す
* Interface()でテンプレートからの入力を受け取る
* pongo2.AsValue()で値を返す

という感じでした。

---

## 今後

次は週や月、年単位でのデータ提供と視覚化が目標です。

---

![gopher_moustache.png](https://qiita-image-store.s3.amazonaws.com/0/56480/833eda6a-d997-fdfd-036f-ca7a549b84f6.png)