スプーキーズのちょっとTech。

SPOOKIES社内のより技工的な、専門的なブログページです。

Jupyter notebookの布教

実験のグラフ書いたりするツールとしてJupyterがとっても便利だよ、コードが書けるならExcelなんて使うのやめようよ、という話をします。

あんまり業務には関係ないです。 Jupyter notebookを布教するためのステマ記事です。

Excelでグラフを描くのはつらい

Excelは、汎用性が高くて、とりあえず誰でも使える便利なツールであるが為に、多くの大学生はグラフを書くのにExcelを使っているようです。 しかし、Excelは科学技術分野で使うためのアプリケーションではないので、Excelのグラフ描画機能は微妙だと思います。

データを単に線形グラフに起こせばいい、とかだとまだマシなんですが、「一つの実験データに対して、線形グラフと片対数グラフを書く。あ、実験データ増えたから二つのグラフの範囲を変更して…」とかなると、だんだんつらい感じがしてきます。 対数グラフを書くのとか、控えめに言って大変すぎでは、と思ってしまいます。

また、学部生の実験だとあんまり無いですが、測定機器から吐き出される大量のtsvファイルから、似たようなグラフをめっちゃたくさん書く、ということもあります。この作業は単純作業であり、同じ処理をするだけなので自動化できますが、これをExcelでやるのは大変です。VBAで書くの…?

そこでJupyter notebookの出番、というわけです。

Jupyter notebookとは

この記事を読めば、Jupyter notebookのなんたるか、どう使うのか、というのはだいたい分かると思いますが、結局のところ、「インライン画像表示機能を備えた、超強力なREPL」ということになります。

次のリンクと画像は、私が実験中に書いたグラフ(とそのコード)になります:こんな感じ

f:id:sancha-san:20160523185928p:plain

見た感じでだいたい分かると思いますが、灰色の枠の中にコードを書いて実行すると、次の行から、出力が表示されていきます。

もちろん、コードを修正して、もう一度実行することもできます。

Jupyter notebookの便利なところは、標準出力だけではなく、グラフも同じ画面に出力できる、ということです。 これによって、グラフを描画→データを追加/修正→グラフを再描画の流れが容易になります。Excelとは比べ物にならないぐらい快適です。

また、Pythonで書けるので、繰り返しや分岐や関数などを駆使して、大量のデータを少ない行数で処理したり、mapやreduceを使って、カッコよくデータを加工したりできます。

外部のtsvファイルを読み込んで処理する、といった処理も簡単です。 また、PythonにはNumpyやSciPyといった科学技術計算用の優れたライブラリがあるので、重い計算でも比較的高速に行うことができます。

さらに、このJupyterのファイルは、githubやgistにupするだけで、公開することができます。 実際に、上記のリンクは、私のPCに保存していた.ipynbファイルをgistにアップロードしたものです(https://gist.github.com/chart-linux/51c64775d07658a4f63a2722eb82d630)。班員と実験結果を共有したりするのに便利です。

また、Excelしか読み書きできない班員がいたとしても、Pythonのライブラリpandasを使っていれば、to_excelメソッドを用いることで、実験データをExcelに出力することもできます。もう班員に合わせて無理にExcelを使う必要もないわけです。

まとめ

Excelでグラフを描くのはやめよう。Jupyter notebookを使おう。

Node.jsについて(紹介・導入編)

f:id:spookies_kuriyama:20160415094819p:plain

前置き(Node.jsの前に)

プログラミング言語について

エンジニアの皆さんは、どのプログラミング言語をお使いでしょうか?

よく挙げられるのがNativeコードを吐き出すCPP、Objective-C、バイナリコンパイル型のC#、Java、 LLのPython、PHP、Ruby、Perlあたりだと思います。 最近だと、SwiftやGo lang等も候補にあがりそうです。

それでは、JavaScriptはどうでしょうか?
がちがちのサーバサイドアプリケーションエンジニアや、 インフラ・ネットワーク・データベース・セキュリティ専業エンジニア以外のアプリケーションエンジニアの大多数の方は、この言語を使って、コードを書いた事があると思います。

「JavaScriptはプログラミング言語じゃないよ」という方もいらっしゃるかもしれませんが、 私は、JavaScriptは立派なプログラミング言語であり、非常に汎用的、かつ将来性のある言語だと思いますし、 一番好きなプログラミング言語でもあります。

最近では、JavaScriptを中心に組むWeb Front-end Engineerという職種もあり、 もりもりJavaScriptを駆使して、Webのフロント側を作っている専業の方もいる程です。

JavaScriptについて

元々のJavaScript

そもそもJavaScriptは、かなり昔にNetscape社がクライアントのWebブラウザ内で、 決められた動的な制御を実行するために、策定された言語です。

当初はクライアントマシンも非力で、実行エンジンも良いものではなく、 APIも少なく、使いにくかったので、かなり限定的な使い方しか出来ませんでした。

しかし、ブラウザ内で動くというのはかなり画期的で、Netscapeの動きを見て、 ライバルのMicrosoftがInternet Explorer(IE)に慌てて同様の機能を盛り込んでリリースしました。
※ 余談ですが、IEに盛り込んだJavaScriptは、正式名称はJavaScriptという名称ではなくJScriptという、JavaScriptの方言の1つです

しかし、IEのJScriptが独自拡張や、互換性のない記述をしていたり、 言語仕様が一致しない等、利用する上で、かなりの苦労がありました。

Mozilla主導による言語仕様の統一化の動き

そのようなカオスなJavaSciprtの世界に統一化の動きを見せたのが、Firefoxの開発元Mozillaです。 Mozillaは仕様化されていなかったJavaScriptの統一化を目指し、 JavaScriptの統一仕様として、GoogleやAdobe等とともにECMAScriptを主導していきました。

今日提供されているブラウザでは、ECMAScriptに準拠して作られており、 JavaScriptの記述方法やAPIにもかなりの互換性があります。
※採用しているECMAScriptのVersionの違いはあります

また、クライアントマシンのマシンパワー向上、JavaScript実行エンジンの最適化、 AJAXによる、非同期通信を用いた動的なページの更新も、JavaSciprtの地位を押し上げました。

Node.jsへの流れ

このようにJavaScriptは使いやすくなり、非常に汎用的、将来性のある言語となりました。

汎用的や将来性につきましては、別に機会がありましたら記載しようと思いますが、 今回は、クライアントサイドで動くJavaScriptではなく サーバサイドあるいは、スタンドアローンアプリとしても動くNode.jsについて、ご紹介しようと思います。

Node.js

Node.jsとは何か

https://nodejs.org/en/

Node.jsを一言で言うと、JavaSciprt言語を駆使してコードを動作させるスクリプティングエンジン環境です。要は、LLと非常に似ています。というよりも、ほぼLLです。
Node.jsならではのメリットを挙げますと、

  • 記述言語が、JavaScriptなので、Web Front-end EngineerやDesignerの人も修得が用意
  • 既存のJavaScriptが流用可能
    • 但し、条件ありで、元々の記述次第で、流用度がかなり変化します
    • デフォルトのAPIがかなり豊富
      http://nodejs.jp/nodejs.org_ja/api/
      ※ 暗号化(Crypto)、ネットワーク(Net)、圧縮伸長(Zlib)等、他のLLではオプションや拡張扱いのものもデフォルトで利用可能
      ※ Web APIの待ち受けも、Apache・IIS・nginx等のWebサーバなしで、簡単に作成可能です
  • サーバサイドでも動かせるが、クライアントサイドで、スタンドアローンアプリやスクリプトツールとしても動かせる
  • 公式純正のパッケージマネージャーがデフォルトで提供(Node Package Manager[npm])されていて、かなり高機能かつ使いやすい
    • 他のLLには公式純正かつデフォルトで付いてくるパッケージマネージャーは存在しない
      (デファクトスタンダードならある。Python pip、PHP Composer、Ruby Rubygems、Perl CPAN)
  • 豊富なライブラリやフレームワークがある
    • Electron(旧ATOM Shell)、nw.js(旧Node-Webkit)、Express、Meteor、moment.js(これはNode.js専用という訳ではない)、node-fs-extra(スタンドアローンアプリ作成に非常に使える)等、他多数

ライブラリやフレームワークについては、機会があれば、またご紹介したいと思います。

Node.jsダウンロード

以下の公式サイトにアクセスして下さい。

Download | Node.js

Download | Node.js

Windows・Macなら公式バイナリやインストールファイルを落として、実行するだけで簡単に動作可能です。
Linuxのバイナリは、最近のディストリビューションなら、大抵展開して、パスを通せば実行可能です。
ソースコードも提供されていますので、Mac以外のUnix環境、FreeBSDやNetBSD等でも、ビルドできます。
※ 公式からのダウンロード以外にも、Homebrew、yum、aptget、Nuget経由でのインストールする方法もあります

Node.jsインストール

前提

ここからは、Mac OS X環境前提で説明していきます。

インストール

上記でダウンロードしたMac用のインストールファイルをクリックして、インストール

Download | Node.js

Mac OS X Installer (.pkg) 64-bit
node-v5.10.1.pkg

長期運用に使う場合は、LTS版がおすすめですが、大抵は、最新のStable版で良いと思います。今回も最新のStable版 v5.10.1で試します。

後は、以下の順に画面を進めていけば導入可能です。

f:id:spookies_kuriyama:20160413095710p:plainf:id:spookies_kuriyama:20160413095715p:plainf:id:spookies_kuriyama:20160413095720p:plainf:id:spookies_kuriyama:20160413095724p:plainf:id:spookies_kuriyama:20160413095728p:plain

最近のバージョンは、インストール時に以下の場所にPATHを通してくれるので、そのままターミナルで実行できます。

Node.js was installed at

   /usr/local/bin/node

npm was installed at

   /usr/local/bin/npm

Make sure that /usr/local/bin is in your $PATH.

ターミナルで、以下コマンドを入力すると、バージョン情報が表示されると正しく導入されています。  

MacBook-Pro:~ spookies$ node -v
v5.10.1
MacBook-Pro:~ spookies$ npm -v
3.8.3

次に、http簡易サーバを作ってみます。

MacBook-Pro:~ spookies$ mkdir -p src/nodejs/
MacBook-Pro:~ spookies$ cd src/nodejs
MacBook-Pro:~ spookies$ vim http.js
MacBook-Pro:~ spookies$ cat http.js

const http = require('http’);

const hostname = '127.0.0.1’;
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hoge Nyanko\n’);
  console.log('Client request!');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

MacBook-Pro:~ spookies$ node http.js 
Server running at http://127.0.0.1:3000/

最後のコマンドで、作成したソースをNode.jsで実行すると、
「Server running at http://127.0.0.1:3000/」と表示されます。
これは、127.0.0.1のホスト名と、ポート3000番でサーバを待ち受けており(待機状態で)、
クライアント側(ブラウザ)からアクセスすると、server側の処理が実行されます。

f:id:spookies_kuriyama:20160413095732p:plain

クライアント側(ブラウザ側)には、 200番ステータス(リクエスト成功)+テキストのコンテンツ属性+「Hoge Nyanko」 という文字列が返されます。

また、今回は、ターミナルから、直に実行しましたが、Service化やDaemon化、 nginx等のWebサーバと連携し、 ポートやServer側での処理を駆使すると、同時に幾つものリクエストを捌く事も出来、
Node.jsのサーバから、Node.jsのサーバも初期で提供されているAPIでリクエスト・レスポンスが可能ですので、 まさしく名前の通り、ノードとして振る舞わせる事も出来ます。

サンプルソース2(スタンドアローンスクリプト)

上記では、サーバサイドの例を記載しましたが、次はターミナルでコマンドを実行すると、 クライアント内で、特定の処理を実行するコードを例としてあげてみます。

以下は、コマンドライン引数で、バックアップ元のディレクトリ・バックアップ先のディレクトリ・ファイル名の3つを指定して、zip圧縮する例です。

サードパーティ製のモジュールとして、zip圧縮するモジュールもありますが、 ここでは、簡略化の為、MacのOSコマンドzipで代用します。

ソースコードは以下となります。

$ cat backup.js

const fs = require('fs');
const path = require('path');

const process = require('process')
const child_process = require('child_process');

function isDirectory(pathName) {
    // ファイル存在チェック
    var isExist = fs.existsSync(pathName);
    if(isExist) {
        // ディレクトリかどうか
        var isDir = fs.statSync(pathName).isDirectory();
        if(isDir) {
            return true;
        } else {
            console.log(pathName + ' is not directory');
            return false;
        }
    } else {
        console.log(pathName +' is not directory or file.');
        return false;
    }
}

// from -> to/zipにzip圧縮する
function backup(from, to, zip){
    var fromDir = path.dirname(from);
    var fromBase= path.basename(from);
    console.log('cd ' + fromDir);
    process.chdir(fromDir);
    // MacでのOSコマンドzipのコマンド文を生成(多分、最近のLinuxでも使える)
    var exeCom = 'zip -r ' + to + '/' + zip + ' ' + fromBase;
    console.log('[Exec] ' + exeCom);
    var result = require('child_process').exec(exeCom, function(err, stdout, stderr) {
        if (!err) {
            console.log(stdout);
        } else {
            console.log(err);
        }
    });
    return true;
}

// 引数チェック
if (process.argv.length != 5) {
    console.log('Usage: ' + process.argv[0] + ' ' + process.argv[1] + ' from_directory_name to_directory_name zip_file_name');
    console.log('For example: node ' + process.argv[1] + ' ~/dat/from =/dat/to backup.zip');
    return;
}

// 引数の内容を受け取る
var from = process.argv[2];
var to = process.argv[3];
var zipName = process.argv[4];

if(isDirectory(from) && isDirectory(to)){
    backup(from, to, zipName);
} else {
    console.log('Error!');
}

実行は、以下のコマンドで実行して下さい。

MacBook-Pro:~ spookies$ node backup.js from_directory_name to_directory_name zip_file_name

実行すると、「from_directory_name」ディレクトリをzip圧縮し、 「to_directory_name」ディレクトリに「zip_file_name」の名称でzip圧縮します。

まとめ

このようにNode.jsは、今後も将来性があり、サーバサイド・クライアントサイド問わずプログラムが作れますので、 皆様も是非活用してみてください。
Let's enjoy Node.js!

Preztoでzshをいい感じに使う

Preztoとは

Github: Prezto — Instantly Awesome Zsh

Preztoはインストールするだけでzshがいい感じに使えるようになるフレームワークです。 prezto/modulesを見ると分かるのですが、様々なモジュールがありそれぞれ関連したコマンドがいい感じに使えるようになります。

例えばrailsのモジュールをロードした場合以下のようなエイリアスコマンドが利用できるようになります。

alias ror='bundle exec rails'
alias rorc='bundle exec rails console'
alias rordc='bundle exec rails dbconsole'
alias rordm='bundle exec rake db:migrate'
...

類似のフレームワークとしてoh-my-zshがありますが、Preztoの方が後発でより軽量でシンプルな仕様になっています。 oh-my-zshが重いと感じてPreztoに乗り換える人が散見されますが、Githubを見るとoh-my-zshの方が活発みたいですね。

f:id:ashida_spookies:20160408165726p:plain

インストール方法はprezto#installationにある通りです。

使い方

インストールが済んだら基本的なコマンドは既に使えるようになっています。 例えば、よく使うものでいうと

# ls系
alias ll='ls -lh'
alias la='ll -A'

# オプション無しでサブディレクトリも作成、さらに作成したディレクトリに移動する
function mkdcd {
  [[ -n "$1" ]] && mkdir -p "$1" && builtin cd "$1"
}

などなど... (詳しくはprezto/modules/utility)

カスタマイズ

モジュール

  1. prezto/modules を眺めて自分の必要なモジュールを確かめます。 (それぞれにREADME有)
  2. ~/.zpreztorc を編集してそのモジュールをロードします。

例: ruby, rails, homebrewモジュールをロード

zstyle ':prezto:load' pmodule \
  'environment' \
  'terminal' \
  'editor' \
  'history' \
  'directory' \
  'spectrum' \
  'utility' \
  'completion' \
  'prompt' \
  'ruby' \
  'rails' \
  'homebrew'

これでターミナルで新しいタブを開けばそれぞれのコマンドが使えるようになっています。 簡単!

テーマ

  1. prompt -l でテーマの一覧表示。
  2. prompt -p themename でサンプルを表示。
  3. 気に入ったものがあれば ~/.zpreztorc を編集
zstyle ':prezto:module:prompt' theme 'cloud'

GHCJSを使う

Haskellロゴ

先日、この記事を読む機会があり、GHCJSを使ってみたくなったので、セットアップ〜DOMを生成してaddEventListenerぐらいまでの流れを書きます。

GHCJSとは

GHCJSは、Haskellを書くとJavaScriptを吐いてくれるコンパイラです。

GHCJSのセットアップ

前提

  • OSが*nix
  • 以下のツールがインストールされていること
  • StackとHaskellを普通に使ったことがあること
    • これらの説明はしません

GHCJSのビルド

GHCJSをビルドするためのディレクトリを作成し、stack.yamlを作成します。

$ mkdir ghcjs-build
$ cd ghcjs-build
$ vim stack.yaml

stack.yaml

flags: {}
extra-package-dbs: []
packages:
- '.'
setup-info:
  ghcjs:
    source:
      ghcjs-0.2.0_ghc-7.10.3:
        url: https://github.com/nrolland/ghcjs/releases/download/v.0.2.0.20151230.3/ghcjs-0.2.0.20151230.3.tar.gz
extra-deps: []
compiler-check: match-exact
compiler: ghcjs-0.2.0_ghc-7.10.3
resolver: ghcjs-0.2.0_ghc-7.10.3

そして、GHCJSをビルドします。

$ stack build

結構時間がかかります。だいたい30分ぐらいです。

GHCJSにパスを通します。

$ export PATH=PATH:$HOME/.stack/programs/x86_64-osx/ghcjs-0.2.0_ghc-7.10.3/bin/

GHCJSで何か書く

GHCJSのセットアップ

まず、適当にプロジェクトを立ち上げます*1

$ stack new ghcjs-test

生成されたstack.yamlの、resolverとcompilerを、以下のように置換します。

compiler: ghcjs-0.2.0_ghc-7.10.3
resolver: ghcjs-0.2.0_ghc-7.10.3

このままstack buildしようとすると、stack setupを先に実行しろ、と怒られるので、言うとおりにします。

$ stack setup

非常に長い時間がかかります。だいたい1時間ぐらい掛かります。

これが終わると、HaskellをJavaScriptにコンパイルすることができるようになります。

$ stack build

生成されたJavaScriptファイルは、プロジェクトディレクトリ直下の、

.stack-work/install/x86_64-osx/ghcjs-0.2.0_ghc-7.10.3/ghcjs-0.2.0.20151230.3_ghc-7.10.2/bin/ghcjs-blog-exe.jsexe/

にあります。 ghcjs-blog-exe.jsexe以下には幾つか.jsファイルがありますが、all.jsに全てのコードが入っているようです。

これをhtmlファイル内で読み込めば、Haskellのプログラムをブラウザ上で動かすことができます。

DOM操作ライブラリ"ghcjs-dom"の導入

Haskellの標準ライブラリではDOMを操作することはできないので、GHCJS用のDOM操作ライブラリであるghcjs-domを導入します。

プロジェクトのcabalファイルに、以下のように追記します。

...省略...

library
  hs-source-dirs:      src
  exposed-modules:     Lib
  build-depends:       base >= 4.7 && < 5
                     , ghcjs-dom              <== ココ
  default-language:    Haskell2010

...省略...

このままstack buildしようとすると、

While constructing the BuildPlan the following exceptions were encountered:

--  While attempting to add dependency,
    Could not find package ghcjs-dom in known packages

--  Failure when adding dependencies:    
      ghcjs-dom: needed (-any), stack configuration has no specified version (latest applicable is 0.2.3.1)
    needed for package ghcjs-test-0.1.0.0

Recommended action: try adding the following to your extra-deps in /home/sangenya/.apps/spoo-blog/stack.yaml
- ghcjs-dom-0.2.3.1

You may also want to try the 'stack solver' command

と言われます。

これを解決するには、上記エラーに書いてあるように、stack.yamlextra-depsという項にghcjs-dom-0.2.3.1を加えるか、stack solverコマンドを叩く必要があります。 後者は便利ですが、メモリをめちゃくちゃ喰います。私の環境では2~3GBぐらい持って行かれました。 前者はその心配をしなくても良いですが、dependency treeが深いライブラリを使うときは大変です。お好きな方をお選びください。

コードの書き方

後述の「突然の死ジェネレーター」のソースを見てもらえばわかりますが、基本的にはMonadIOモナドを駆使する感じです。

私がコードを書いた時は、

  • 書きたい処理をJavaScriptで考える
  • →ghcjs-domのライブラリから似たような名前の関数がないか調べる(getElementByIdなど)
  • →見つからなければghcjs-domのgithubレポジトリを検索する
  • →型から使い方を推測してコードを書く

の流れでだいたい行けました。

作ったもの

突然の死ジェネレータを作ってみました。

実際に書いたコードは以下のとおりです。

Haskellのコードは両方合わせて60行強程度に収めることが出来ました。

GHCJSを使ったことがない人でも、HaskellとJavaScriptを触ったことがある人なら、だいたい何をやっているのか解ると思います。

感想

生JavaScriptを書いてるみたいで正直つらい。

確かに、(Haskellのサブセットとか、Haskell-likeな言語ではない)本物のHaskellでブラウザで動くものを作れるのは楽しいし、生JavaScriptを書くよりは幾分簡潔に出来てる気はしますが、やってることは手続き型言語です。 もっと規模が大きなものを作るのなら違うのかもしれませんが、正直このくらいの規模のものを作るなら、生JavaScriptを書いたほうがいいのでは、と思います(Haskell好きなら楽しいけどな!)。

とはいえ、react-fluxghcjs-vdomなど、GHCJSで動くライブラリなども出てはいるので、将来的にもっと楽になる可能性はあると思います。 特にreact-fluxの方は、ドキュメントもちゃんと書いてあるっぽいし、試してみたいですね。

あと、生成されたJavaScriptファイルのサイズがめっちゃでかいです。60行強のHaskellコードが1MBぐらいのJavaScriptになります。

*1:Stackのghcjs用のテンプレートもあるのですが、2016-03-27現在はうまく動かないみたいです。動かし方知ってる人がいたら教えてください。

ナレッジは、esa.io でしょ

人って難しいですよねー。だけど、一番おもしろいと感じる西塚です。

プログラムは、嘘つかない。

バグを憎んで、人を憎まず。

さて、

社内の情報を育てる

以前の記事でも書きましたが、うちは社内共有(ナレッジマネジメント)に esa.io を使っています!という話。

長く利用していると使う側のスキルも向上していくし、愛着が湧いてくる影響もあるかもしれませんが、 esa.io はかなり気に入ってます。

ちょうど1年前くらいから利用していて、無料体験からすぐに有料版で利用しています。

開発サービス変遷

Google Site → Backlog Wiki → GitLab Wiki → esa.io

Trac → Mantis → Redmine → Backlog

SVN → GitLab

MSN Messanger → Skype → Slack

Html → WordPress → GitHub Pages

思い出しながら書いているので、抜けがあたったりするかもしれませんが、だいたい上記のように移行してきています。 だいたいです。

サービスが変わる度に移行のエネルギーが必要なのですが、やはり新しいものは進化していて定着すると、移行して良かったと手放せなくなります。

esa.io も正に、それの1つです。 Backlog は気に入ったという話を以前書いたと思うのですが、それ以上に esa.io は私に刺さっています。

まず、コンセプト

f:id:spookies-nishizuka:20160226233515p:plain

最高ですね。完璧ですね。

MackerelのMeetupで、最近上場したはてなのCTOさんが掲げてた、「エンジニアをワクワクさせる」サービスも良いですが、しんぷるいずべすと。

かわいいデザインですが、使い勝手はかなり追求されていると感じます。

なので、記事を書くのが面倒にならない。

Google Site は WYSIWYG なので、簡単なはずですが、逆に細かいスタイルに惑わされて意外と面倒。

やはり開発のナレッジは、今は Markdown で記述する時代ですよね!? そうです。

Backlog / GitLab の Wiki 機能、Qiita Team も試しましたが、記事のカテゴライズ、リンクの強さが esa.io は際立ちました。

f:id:spookies-nishizuka:20160226234038p:plain

今後の esa.io にも期待!

esa.io を見つけた時は、開発メンバーが開発1名デザイナ1名の2名体制ということで、この体制で大丈夫か?という危惧もありましたが、問い合わせの返答の早いこと早いこと。

CSとしても素晴らしく、激感動しました。

そして、小規模の体制ではあまりない API が早々に実装されたのも、魅力の1つです。

以前、GitLab ⇔ Backlog を連携している記事を書きましたが、APIは社内の開発サービスを一体化できる重要な要素となります。 これは、また別に書きます。

はてなさんのサーバ管理サービス Mackerel でも感動しましたが、最近は開発の良いサービスがいっぱいありますねー。 うちも、そういうものを生み出さないといけないですねー。

ともあれ、 餌(esa) のおかげで、社内情報はひよっこエンジニアと共に、すくすくと健やかに育っております。

esaさんには、いつか Evernote までも凌駕して欲しいです。はい。

と、熱く esa.io 愛を語ってしまいましたが、今後の動きにも期待しております。

ノベルティ欲しいな~ チラッ と言ってみる。

おそまつ

GitLabとBacklogの連携

記事を書いて寝かしておいたら、忘れてしまって冬になってしまった西塚です。

最近、冷えますが、皆様いかがお過ごしでしょうか。

今回もスプーキーズで利用している開発サービスの話です。

GitLabとBacklogの連携

labs.spookies.co.jp

以前の記事でも紹介しましたが、スプーキーズではGitLabとBacklogを利用しています。

Backlogが提供しているGitリポジトリを利用していないので、そのメリットであるGitのCommitとタスクを関連付けるということが出来ません。

そこで、WebHookとAPIを利用してこれを独自実装しています。

GitLab ⇒ Backlog

GitLabのCommit情報やMergeRequest(PullRequest)から、対応するタスクを参照する機能は、GitLabの設定で実現することができます。

今まではその機能を知らずに、GitLabのViewをカスタマイズすることで実現してましたが、サーバ移行時に設定を見なおしていたら、この機能を発見しました。

GitLab設定

標準はJIRAまたはRedmine用の設定なのですが、Backlogも近い(URL)形式となっているので、設定を応用できます。

  ## External issues trackers
  issues_tracker:
    # redmine:
    #   title: "Redmine"
    #   ## If not nil, link 'Issues' on project page will be replaced with this
    #   ## Use placeholders:
    #   ##  :project_id        - GitLab project identifier
    #   ##  :issues_tracker_id - Project Name or Id in external issue tracker
    #   project_url: "http://redmine.sample/projects/:issues_tracker_id"
    redmine:
      title: "Backlog"
      project_url: "https://xxx.backlog.jp/projects/:issues_tracker_id"
      issues_url: "https://xxx.backlog.jp/view/:id"
      new_issue_url: "https://xxx.backlog.jp/add/:issues_tracker_id"

一時期、GitLabのソースを改変していた時期もあったのですが、マージやメンテが面倒だったりするので、設定だけの方式に切り替えてます。

上記設定により、GitLab の Issue タブを押した際、新規課題を押した際に、Backlogに遷移します。

また、コミットメッセージやプルリクエスト(GitLabではマージリクエスト)に、Backlogの課題IDをつけるように運用しています。

これにより、GitLabからそのBacklog課題へのリンクが自動的に生成されます。このコミットは、どのような課題を解決するために改修したのかがすぐに辿れるようになっています。

Backlog ⇒ GitLab

Backlogの課題からGitLabのCommit情報を参照する為に、Commitされた際にその内容をBacklogにコメントに追加することで実現しています。

GitLab にコミットメッセージもしくは、ブランチ名に課題IDを入れることで自動的に該当する課題にコメントが付加されます。

f:id:rhymester19:20151015171431p:plain

ちなみに、GitHub から Backlog はこんな感じ。

f:id:rhymester19:20151015171448p:plain

Ruby のプログラムで、GitLabのコミットをWebHookで拾って、Backlog API を利用して、該当課題にコメントつけています。

  def parse_json(json)
    event = JSON.parse(json, :object_class => HookBaseObject)
    event.extend(PushEvent)
    event.repository.extend(Repository)
    if event.commits
      event.commits.each { |commit| commit.extend(Commit) }
    end
    event
  end

  def git_push_to_backlog(event)
    description = event.repository.description
    return unless description

    # ex. [master]xxx@xxx.git.backlog.jp:/SANDBOX/sandbox.git
    matches = description.scan /(?:\[(\S+)\])?(\S+?@\S*git.backlog.jp:\S+\.git)/

    Dir.chdir(event.repository.local_path) do
      begin
        matches.each do |branch, url|
          if branch
            system('git', 'push', '-f', url, branch)
          else
            system('git', 'push', '-f', '--mirror', url)
          end
        end
      rescue => error
        log_error(error)
      end
    end
  end

  def build_comment(commit, event, signature)
    comment = ''
    branch_name = event.ref_name
    branch_url = event.repository.branch_page_url(branch_name)
    comment << "**Author:** #{commit.author.name}\n"
    comment << "**Branch:** [#{branch_name}](#{branch_url})\n"
    comment << "**Commit:** [#{commit.id[0,7]}](#{commit.url})\n"
    comment << "\n"
    comment << commit.message.gsub(/^/, '>') + "\n"
    comment << "\n"
    comment << signature
  end

@ つけることで、メンバーにお知らせしたり、 @+1h で時間をつけたり、 #fix で課題を完了させたり、 もしています。

ついでに、Slack にも連携してるので、誰が何の課題をしていて、どんなコミットしたのかは一目瞭然です。

いまは、HubotもBacklogに連携すべく、ゴニョゴニョしていますが、これはまた別の機会に。

それでは。もう少し寝てきます。

RaspberryPiと人感センサーで監視カメラを作ってみた

こんにちは。IoT開発部(仮)の西村です。
犬のしつけ教室に行ったら、先にあなたの子供をしつけてくださいと怒られました。他人任せはダメですね。とほほ。

先日、社内メンバーでランチに行った際に、IoT関連でなんかやってみたいよね。 という話になり、少し前からチラホラと名前を聞いてたRaspberryPiを使ってなんか作ってみようぜ! という事になりました。

何作んの?

さて何を作ろう?!という事で、
オフィスに誰か入ってきた時に、カメラで撮ってslackに流したら社内の動きがわかって便利だよね。
廊下に置いて、お客さま or 大家さんが来たらサッと扉を開けて、最高の笑顔で迎えてみようか?
なんて話になりました。

ということで、「誰かきたかもよ」君を作成しました。

ラズパイ本体と各種パーツ購入

amazonで以下の製品をポチりました。

だいたい次の日には届きました。便利ですね。
ちなみに、ラズパイをカートに入れたらamazonがレコメンドしてくれる [Transcend microSDHCカード 32GB]を買ったんですが、何度やり直してもOSのインストールがうまくいきませんでした。
東芝製のものを買い直したらあっさり成功したので、相性のようなものがあるのかもしれません。あまりお勧めを鵜呑みにするのはよくないですね。

手順1 SDカードにOSをダウンロード・インストール

まずSDカードを

SDカードフォーマッター

というツールを使ってフォーマットします。

次にraspberrypiサイトから、OSイメージをダウンロードしてきて、SDカードにコピーします。

raspberrypi本体にSDカードを刺して、キーボード・マウスを取り付け、電源としてのMicroUSBを挿したら起動し、インストールが始まります。
本体には電源ボタンとかないんですね。

手順2 各種パーツを取り付ける

OSのインストールが一通り終わったら、一旦電源を落として、センサー等各種パーツを取り付けます。

正直、どこに何をつけたらよいのか知識がなく困ったのですが、Qiitaのcigalecigalesさんの記事 を参考にして取り付けました。
人感センサー部分の設定に関しては、上記記事内でも紹介されてますこちらのサイト を参考にさせてもらいました。
カメラモジュールは本体に刺すだけです。

f:id:spookies-nishimura:20151209225356j:plain

手順3 無線LANの設定

参考サイトはたくさんあると思いますが、 lsusb コマンドで無線LANモジュール機器が確認できたら、

  • SSID, 暗号キーを設定
  • 固定IPの設定

を行い、ネットワーク再起動します。

手順4 プログラムを書く

今回のプログラムの流れとしては、以下のような感じです。

  • 1sec毎にループし、人感センサーの反応をチェック
  • センサーが反応したら1sec挟んで写真を2枚撮る
  • 2枚の画像の差分解析を行う
  • 一定以上の差分があれば、SCPで写真を画像公開用のサーバに送る
  • slackに、写真のURLとともに「誰かきたかもよ」とメッセージを送る

利用する各種moduleのimport

import os
import commands
import time
import RPi.GPIO as GPIO
import slackweb
import picamera
from paramiko import SSHClient, AutoAddPolicy
from datetime import datetime

画像の差分比較(ImageMagickを利用)

ImageMagickをインストール

sudo apt-get install libjpeg-dev imagemagick

os.system('composite -compose difference ' + filepath + ' ' + filepath_check + ' ' + filepath_diff)
diff_num = commands.getoutput("identify -format \"%[mean]\" " + filepath_diff)

画像が変化したと判断するための差分値は 1500 以上にしてますが、このあたりは環境等にもよると思います。

slackに通知

Qiitaのsatoshi03さんの記事を参考にWebHookURLを取得して、slackwebモジュールを利用して通知します。

slack.notify(text="誰か来たかもよ.")
slack.notify(text="WEBサーバに投稿した画像URL")

手順5 スクリプトをデーモン起動させる

常時起動するために supervisor をインストールします。

sudo apt-get install supervisor

human_sensor.conf を作成し、設定を行ったら

sudo supervisorctl start all

でデーモン起動させました。

設置

さて、この「誰かきたかもよ」君をオフィス内の扉の前に設置しました。
これで誰か来たとき、帰る時に、slackに通知されるようになりました。

f:id:spookies-nishimura:20151209225804p:plain

カメラの前にいろいろものを置きすぎて鬱陶しいですが、現状こんな感じ。

いたずら

同じものをもう1台作って東京オフィスにも設置してみました。
しばらくはうまく動いてたのですが、誰かがトイレに行く際に、マッハキックをかましたようで、「誰かきたかもよ」君が倒れ、延々と暗い画像を映し続けてました。

f:id:spookies-nishimura:20151209225826p:plain

そこで不意に、とてもとても怖い写真をslackに投稿してみました。
真夜中にです。

そしたら

f:id:spookies-nishimura:20151209225808p:plain

という反応を示してくれました。
ちょーびびってる。けけけ。

まとめ

SORACOM Airや、akerun などIoT製品が続々登場し、身近な物になってきました。 最近知った、MaBeee などは、見ててとてもワクワクします。
弊社でも、そんな製品・サービスづくりに積極的にチャレンジしたいと思います。

さてインフラは整ったので、お客さま・大家さんに最高の笑顔をご提供できるように 全メンバー手鏡を持ち、朝から夜中まで自主練をしております。

f:id:spookies-nishimura:20151209232127p:plain

うーん、もう少しですね。

それでは、私も自主練に戻りますので、失礼致します。