読者です 読者をやめる 読者になる 読者になる

sekaie engineers' blog

セカイエ株式会社が主催するエンジニア勉強会について

なんとなくこの色じゃあかん part1(基礎知識編)

どうも。最近は忘年会のことで頭がいっぱいの寺田です。

アドベントカレンダーの7日目を担当します。ちなみに関係ないですが、9月7日が誕生日です。

qiita.com

今まで何となくWebデザイナーという職種を捉えて、ひたすら納期に間に合うように業務していた感がありましたが、立派な上司や先輩方に囲まれてもう少し自分の職種に対して深掘りしていかなければならないと反省している今日この頃です。

そこで、まずはデザインをしていく上で避けては通れない「色」についてちょっとまとめてみました。

「色」って正直言葉じゃ説明難しいですよね〜 今まで漠然とこういうイメージだとか、こういう雰囲気とか、さらっとした感覚で説明することが多かったんですが、

やっぱプロとしてデザインをやって行くのであればもう少し深い説明ができないとあかんなって思ってます。

なんとなくこの色にしました。から→〇〇だからこの色にしました。

って言えるにはどうしたらいいんかな。。 そもそも「色」って言葉でうまく説明できんのかな。

色ってセンスじゃね?

とか色々考えてましたが、

一応配色にもセオリーがあるので、それをかなり基本的な部分から分析して考えながらデザインする方法に関して焦点をおいて記事を書いてみようと思います。

配色のセオリー

そもそも配色とは

感覚面での個性をふまえると同時に、色相・明度・彩度(色の三属性)の異なる色の無限の組み合わせに挑戦し、選択するという意味 by wikipedia

むむむ。。難しい。大変そう。

だが要するに色の組み合わせを考えるってことですよね。 その為にセオリーを学べば「色」を言語化して伝えるっていうことに繋がるかもしれない。

色の表現

色を言葉で表現するにはどういった方法があるのか。 ってことを色相・明度・彩度の色の三属性ごとに考えてみましょう。

【色相】

f:id:sekaie:20161207061128p:plain

このようなパターンでAとBは補色の関係にあります。 それと同時にAとBは色相という言葉を使って説明すると、 色相が遠いとか離れてるって表現になるわけです。 (逆に近いのは隣接する色になります。)

【明度】

f:id:sekaie:20161207062837p:plain

明度は高い低いっていう表現になります。 暗いとか明るいとかっていう表現は厳密にはあまりよろしくないみたいです。

【彩度】

f:id:sekaie:20161207064224p:plain

彩度は高い低いの他にもこういった表現があります。 確かに鮮やかとかはよく使ってますね。

※注意しておいてほしいのが、明度と彩度は必ずしも平行して高くなったり低くなったりしないってことです。

f:id:sekaie:20161207065424p:plain

あとは色を表現する方法ってないのかな。。 って考えた時に、名前がついてる色ってあったなって思いました。

例えば、桜色とかからし色とか黄金色とかです。 こういうのって赤黄色青とかと違って厳密に色を表現できるものですよね。 でもそこまで多くはないでしょって思ってたのですが、 思ってたよりかなり数が多くてびっくりしました。

www.colordic.org

これらをさらっと覚えておくだけでも結構プロっぽいですよね。 伝える時に色やイメージに深みが出てくるのではないでしょうか。

色の働き

ここが結構肝心な部分だと思います。

色って心理的にも様々な効果とか働きがあって、それが科学的にも証明されています。 その色の働き的な部分を紐解いて行くことが、 配色をする上でも説明する上でも重要になってくるわけです。

例えば色の効果や機能として代表的なものはこのような感じです。

【赤】

f:id:sekaie:20161207073058p:plain

赤はやっぱり暖かく感じる効果がありますよね。 あとは危険なイメージもあります。 気持ちを高ぶらせる効果や、 食欲増進効果もあるとされています。


【青】

f:id:sekaie:20161207073322p:plain

青は逆に寒く感じる効果がありますね。 あとは集中力を高めたり、冷静にさせる働きもあります。 食欲減退効果もあるのでダイエットグッズに活用されたりもしています。


【緑】

f:id:sekaie:20161207073629p:plain

緑は疲れを癒す効果があるとされています。 穏やかな気持ちになったり癒しや安心感を与えることができるので、 非常に様々なシーンでその効果を活用されていますね。


【白】

f:id:sekaie:20161207073934p:plain

白は純粋さや、清潔な印象を与えることができます。 また軽く見えるような効果があります。


【黒】

f:id:sekaie:20161207074243p:plain

黒は恐怖感や暗い気持ちになるといった効果はもちろんのこと、 重く見えたりする効果もあります。

他にも様々な効果や機能がありますので配色をする知識の一部として知っておくのもいいと思います。

それから先ほども少し出てきましたが、 暖色と寒色に関して少し考えてみましょう。

f:id:sekaie:20161207123301j:plain

このタコヤキの写真を見てもわかるように、 背景が暖色(赤)の方は美味しく見えて、 背景の寒色(青)の方はまずく見えるような効果があります。

厳密に言えば寒色の方が美味しく感じるような食材もありますが基本的には上の写真のような効果があります。

それから色は重さを持っているという点です。

下の画像をごらんください。

f:id:sekaie:20161207123737j:plain

白っぽい箱と、黒っぽい箱、2種類ありますが、どっちが重そうでしょうか。

黒ですよね。

これは単純に明度が高いと軽く見えて、低いと重く見えるという性質があるからです。

また、注意を引く色としては赤が非常に大きな効果を発揮します。

f:id:sekaie:20161207124244j:plain

こういったことが色の機能の基本的なことです。

視認性

こちらをごらんください。

f:id:sekaie:20161207124716j:plain

どちらが見やすいですか? 理由はなんだと思います?

これは文字の色が違うだけなのですが、 かなり見やすさに差が出ていますよね。

見やすさ、視認性は明度の差で決まります。 視認性はWEBデザインでは非常に重要な要素になります。

見やすいということはもちろんのこと、どこを目立たせて人に注目してほしいか。 そういった部分を明らかにさせる時には明度の差を意識しましょう。

ちなみに白黒の差が一番明度の差が大きいので、一番視認性が高いのは白と黒の組み合わせとなります。

誘目性

f:id:sekaie:20161207155502p:plain

誘目性の高い色の組み合わせは「赤、黄色、黒、白」等によって表現できます。 もちろん、あまり多用すると派手になってしまってダサくなってしまいます。

また、暖色、寒色は進出色、後退色とも言えます。 進出色は要素が飛び出て見えたりする効果があります。 逆に後退色は奥行きのある空間を演出したりする効果があります。

f:id:sekaie:20161207133832j:plain

与える印象が全然違いますね。

特定の目立たせたい部分で利用したり。 ページ全体に大胆に進出色を使って圧倒するようなイメージがいいのか、 後退色を活用して人を引き込むようなイメージかとか。

そういったことを考えながら色を使い分けるとクライアントやチーム内で説明ができて業務が円滑に進みそうですね。

part2(実践編)に続きます。。。

明日はナチュラルイケメンの中島さんが記事書きます!

ほな!

セカイエのアラート監視周りについての雑多書き

おはようございます。こんにちは。こんばんは。

佐々木です。

キリスト教ではありませんが、アドベントカレンダーの1日目を担当します。どうぞよろしくお願いします。

サンタさんに何買ってもらおうかなあ。。。

qiita.com

今日はセカイエのアラート周りについて書きます。

まあまずはじめに

セカイエでは AWS を使ってサービスを展開しています。主にEC2です(ほぼEC2です)

AWSには CloudWatch というサービスがありますね。

aws.amazon.com

RDSやEC2のメトリックも AWS CloudWatch を使って取得しています。

ただ EC2 で展開しているサービスのエラーログ (PHP LOG) とかの監視が難しいです。

エラーがあったよ。くらいのアラートを飛ばすことは簡単にできるんですが、何のアラートなのかを把握することが出来ません(多分)

この辺をどうにか CloudWatch + α な修正で解消したいと思ってまとめてみたのが今回の記事の内容になります。

CloudWatch のインストール

EC2を使っていればインストールは難しくありません。↓のドキュメントがそのままになります。

docs.aws.amazon.com

サービス (EC2 側) のログを整形する

サービス側のログは↓みたいな感じにテキストでガーガー出力されています。

F, [2016-12-01T16:23:29.405110 #2634] FATAL -- :
ActionController::RoutingError (No route matches [GET] "/apple-touch-icon.png"):
  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/rack/logger.rb:38:in `call_app'
  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/rack/logger.rb:22:in `call'
  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/request_id.rb:21:in `call'
  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:22:in `call'
  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/runtime.rb:18:in `call'
  vendor/budnle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'
  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/static.rb:116:in `call'
  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/sendfile.rb:113:in `call'
  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/engine.rb:518:in `call'
  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/application.rb:165:in `call'
  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:580:in `process_client'
  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:674:in `worker_loop'
  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:529:in `spawn_missing_workers'
  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:140:in `start'
  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/bin/unicorn_rails:209:in `<top (required)>'
  vendor/budnle/ruby/2.2.0/bin/unicorn_rails:23:in `load'
  vendor/budnle/ruby/2.2.0/bin/unicorn_rails:23:in `<main>'

CloudWatch では Log を json で出力しておくと何かと都合がいいです。

docs.aws.amazon.com

なので、このテキストログを td-agent で json に整形しましょう

<source>
  type tail
  path /var/log/hogehoge/production.log
  pos_file /var/log/td-agent/production.log.pos
  tag log.hogehoge
  format multiline
  format_firstline /^.,/
  format1 /^., \[(?<time>[^\.]+).+\][ ]+(?<severity>[^ ]+) -- :(?<message>.*)$/
  time_format %Y-%m-%dT%H:%M:%S
</source>

<match log.hogehoge>
  type file
  format json
  path /var/log/hogehoge/production.log.json
  symlink_path /var/log/hogehoge/production.log.json
  buffer_type file
  buffer_path /var/log/hogehoge/production.log.bufffer
  include_time_key true
</match>

こうすると先程のテキストログが↓のようになります

{"severity":"FATAL","message":" \nActionController::RoutingError (No route matches [GET] \"/apple-touch-icon.png\"):\n  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'\n  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'\n  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/rack/logger.rb:38:in `call_app'\n  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/rack/logger.rb:22:in `call'\n  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/request_id.rb:21:in `call'\n  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/methodoverride.rb:22:in `call'\n  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/runtime.rb:18:in `call'\n  vendor/budnle/ruby/2.2.0/gems/activesupport-4.2.3/lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call'\n  vendor/budnle/ruby/2.2.0/gems/actionpack-4.2.3/lib/action_dispatch/middleware/static.rb:116:in `call'\n  vendor/budnle/ruby/2.2.0/gems/rack-1.6.4/lib/rack/sendfile.rb:113:in `call'\n  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/engine.rb:518:in `call'\n  vendor/budnle/ruby/2.2.0/gems/railties-4.2.3/lib/rails/application.rb:165:in `call'\n  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:580:in `process_client'\n  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:674:in `worker_loop'\n  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:529:in `spawn_missing_workers'\n  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/lib/unicorn/http_server.rb:140:in `start'\n  vendor/budnle/ruby/2.2.0/gems/unicorn-4.9.0/bin/unicorn_rails:209:in `<top (required)>'\n  vendor/budnle/ruby/2.2.0/bin/unicorn_rails:23:in `load'\n  vendor/budnle/ruby/2.2.0/bin/unicorn_rails:23:in `<main>'\n\n","time":"2016-12-01T16:23:29+09:00"}

あとはこの出力した jsonaws logs agent で cloud watch に送ります。

log_group name とかは適宜設定してください。

[/var/log/hogehoge/production.log]
datetime_format = %Y-%m-%d %H:%M:%S
file = /var/log/hogehoge/production.log.json
buffer_duration = 5000
log_stream_name = {hostname}
initial_position = end_of_file
log_group_name = /var/log/hogehoge/production.log

cloud watch にはログが json で貯まるようになりました。 f:id:sekaie:20161201174610p:plain

CloudWatch でフィルタリング

Cloud Watch には WARNING / ERROR / FATAL など複数のレベルのログがたまります。

この中で、アラートとして監視したいログをフィルタリングすることが出来ます。

この辺も CloudWatch の標準機能なのでドキュメントをみながら設定しましょう。

今回は ERROR / FATAL をフィルタしました。

f:id:sekaie:20161201175454p:plain

CloudWatch でアラート設定

フィルタしたログを今度はアラートの設定を掛けます。

これも CloudWatch の機能なので説明は割愛します。

docs.aws.amazon.com

RDSなどのメトリックと違って、ログの行数でアラートをかけるのが大きな違いかと思います。

設定はそんな難しくありません。

アラートを設定すると、はじめにの項で説明した「エラーログが出力されたよ」っていうアラートが飛ぶようになります。

ログアラートを監視出来るようにする。

ここまでは AWS CloudWatch を使った設定でしたが、ここからは AWS APIRuby を使います。

rake task になっている部分を抜粋してみました。

AWSAPIで定義してあるアラームをチェックして、ALARM状態になっていたら、そのアラートのログフィルタされているログを出力してチャットワークに出力するスクリプトです。

  desc "Monitoring"
  task :check => :environment do
    chatwork = ::Chatwork.new
    aws = ::Aws.new

    aws.alarms.each do |alarm|
      next unless alarm.state_value == 'ALARM'
      alarm_name = alarm.alarm_name
      metric_name = alarm.metric_name
      log_alarm = ::Monitoring::LogAlarm.find_by({
        alarm_name: alarm_name,
        metric_name: metric_name,
      })

      log = aws.log_events({
        log_group_name: log_alarm.log_group_name,
        filter_pattern: log_alarm.filter_pattern,
        start_time: (Time.now - 300).to_i
      }).reverse.first

      chatwork.error(log.message)
    end
  end

これを定期的に動かしておけば、アラートがなったタイミングでチャットワークに↓のようにエラー文を出力してくれるようになりました。

f:id:sekaie:20161201183456p:plain

今後の展望とかまとめ

今回は CloudWatch + αでアラートを監視する仕組みについて紹介しました。

これは自分が担当している分析基盤ツールのアラート監視には取り入れていてテスト段階ですが、これを他のサービスにも展開していきたい所存です。

あわよくばOSSあたりで開発できればいいなと思っております。

セカイエアドベントカレンダー1日目でしたー。

ほな!

セカイエの立ち上げから関わってわかった10のこと

スタートアップ

私、増井は、セカイエ株式会社を卒業します!

2012年12月14日 セカイエ株式会社が設立してから早4年が経ちました。

サービス自体は4年半くらいですかね。

思い返すと4年半。いろいろありました。

  • 分社化してリフォーム事業の独立。

  • 2回の事務所引っ越し。

  • GREEの完全子会社化。

なかなか濃い4年半でした。

立ち上げ当初からエンジニアとして過ごして、いろいろ自分なりに気づいたことがあるので書きたいと思います。

1.デスクトップPC < ノートPC

引っ越しが大変。

2回目は引っ越し業者に頼んだからまだましだったけど、

1回目は自分たちだけでやったので、デスクトップを運ぶのはただただつらい。

社内の配線とかめちゃくちゃになる。

基本的には綺麗好き(A型)なんですけど、引っ越し作業もその日の営業終了後からで、

深夜だし、

しんどいし、

眠たいし、

で、

"えいっ"

ってやっちゃいます。

あとで綺麗にするし。→ 絶対やらない。

ルンバと喧嘩します。

LANの銅線むき出しになります。

2.大事なサーバーはちゃんと運ぶ

2回目の引っ越しの時、台車から大事なサーバーがこぼれ落ちるという事件。

案の定ぶっこわれました。

ミラーリングされてたから、なんとか無事だったけど、ただでさえ忙しい引っ越しの日に、まじかんべんです。

3.社内のレイアウトは最初からきちんと決めておく。

人数いなくても、最初から机といすはきっちり敷き詰めておく。

最初は人数すくないのでゆったりスペースとってましたが、

どんどん人が増えていくなかで何度もシマ増やしたりしました。

OAフロア剥がして配線するのは危険です。

電源系統とか、シマハブを連結するとか、そのうち事故りますね。

(サーバーと自販機を同じ電気系統につないでて落ちた経験あり)    

引っ越しの話ばっかり

ついでに

入居するビルを選ぶ時も注意が必要。

ビルによっては、入れたい回線をEPSに設置させてくれないところもあります。

要チェック。

4.求人は大変

エンジニアの採用もやってましたが、GREE子会社化の前と後では、求人希望者に変化を感じました。

名もなきスタートアップは、求人かけるより、知り合いベースで探すほうが良いかと思いました。

いまはセカイエでもWantedlyに掲載してますので、もしセカイエに興味がある方はぜひお話しだけでも来てください。

僕はいませんが。

www.wantedly.com

5.細かい数字は見ない

あくまで僕個人の意見ですが、

事業の前半では、細かい数字は見ないって決めた方がよいと思います。

(決めないとついつい見てしまうので)

このページの直帰率が、、、とか、

このボタンのクリック率が、、、とか

そういった細かいPDCAを回す時期とそうじゃない時期があるので

そういった大きな指針をしっかり決めることも大事だと思います。

ちなみに今のセカイエには分析基盤があります。

sekaie.hatenablog.com

6.仕事ばっかりしない

スタートアップなんで、ゴリゴリ仕事しまくらないといけないとは思いますが、ちゃんとメンバーと遊ぶ時間とか飲みに行く時間とかがすごい大事だと感じました。

あれしよう!とかこうやったらよさそう!とかは、結局思い返すと、ミーティングとか仕事中とかじゃなくて、それ以外の時間に出ることばかりだったような気がします。

f:id:sekaie:20161118155244p:plain

※写真は淡路島ハッカソンの時のチーム:ランチーズの一枚。

7.品質とスピードの間で

  • 昔:設計の質とか、コードの質とか、そんなもんは後回しじゃー。 

  • 今:なんでこんな設計なってるんや!なんじゃこのコードは!

設計の質とか、コードの質とか、当時はあまり考えてなかった。

それより、スピードとか量を重視してました。

(サービス終了したら元も子もないしなって気持ち。あとは、そこまで知識や経験もなかったってのも事実。)

今はその負の遺産にエンジニアメンバーは悩まされてることが多いかと思います。

例えば、データベース。

いろんな仕様変更によりどんどんカラムが追加され、

いま一番カラム数おおいテーブルで92カラム。

すごいな。。。 

たぶん半分くらいは使ってないんじゃないかな。。。

こうゆう状態を作り上げてきた過去の自分を呪います。

サービスのステージによって左右される部分は多大にありますが、それを分かったうえでやってるのか、なんとなくそうなってるのか、では全然違います。

8.属人化について

一般的には属人化ってよくないのですが、どうしようもないです。(メンバー少ないし)

でもメンバーが増えてきたらチャンスと思って、どんどんアウトプットしていくべきだったなと思います。

僕自身なんでも自分だけで完結させようとしてしまうタイプなので、もっと回りのメンバーと一緒に進めるってことがすごく大事だと感じました。

あと、非エンジニアをエンジニアにするってことも大事かなと思います。

何でもかんでもエンジニアにお願いする。と属人化ならぬ属エンジニア化しちゃうので、それもよくないなって。

資料つくったり、仕組み作ったりはエンジニアの仕事。というかわかる人が資料作ってみんなに展開すればいいし、そこにプログラムが必要ならエンジニアが作ればいいと思う。

9.勉強会には参加するべし

セカイエでは毎週水曜日、おもに開発部のメンバーのおもにエンジニアメンバーで社内勉強会をしています。

これが意外というかなんというか、すごくいい方向に働いています。

勉強会でやった主なこと

  • 勉強 (メンバーそれぞれがいま気になる技術とかを調べて、資料つくって発表)

  • LT

  • ハッカソン

  • アドベントカレンダー 2015

  • 輪読

  • sekaie tech talk (セカイエ主催のオープンな勉強会)

などなど

この勉強会をきっかけに、社外勉強会に参加する人も増えたり、お知り合いの方伝いで合同勉強会を開催したりと、活動の幅が広がっています。

そして、このエンジニアブログも社内勉強会の中で書こう!となり始まっています。

ちなみに今年もアドベントカレンダーやります。

qiita.com

10.ベンチャー企業っていいな

もともとは1ポンコツエンジニア(ここらへん知ってる人はもうほとんどいないですが)として入社した僕が、ここまで成長できたのは

ベンチャー企業だからなのかなって思いました。

いろんな部署のみなさまと関わらせていただいて、

なんでもかんでもやらされる。とりあえずやるしかない。って状態。

結構好きです。

そんな環境が、エンジニアとして、社会人として、人として、成長できた一番の理由だと思ってます。

他の会社なら、ここまでいろいろ任せてもらえることも無いだろうし、ほんとにセカイエで仕事できてよかったなーーーーって思ってます。

以上、エンジニアらしいことはそんなに書いてないけども、ぼくがセカイエの立ち上げから関わってわかった10のこと

です。

ほとんど誰の参考にもならないような情報ばっかりな気がしますね w

ほな。

集計テスト時の BigQuery のテストデータについて

はじめに

おはようございます。こんにちは。こんばんわ。佐々木です。

さっそくですが、皆さんテストしてますか?

自分もそんな得意ではないですが(むしろ苦手ですが)、サービスの規模も大きくなってきたので最近必死にテストを書いています。

サービス自体は Rails で書かれているので rspec を使ってコントローラやモデルなどのテストは例に倣ってそれっぽくテストが出来ています。

が、集計のテストがなかなか難しいです。

多分一番大変なのはテスト用のデータを用意したりするところだと思います。

特にログデータや MySQL snapshot のデータは BigQuery に入れていてそのデータを集計していて、BigQueryのデータを用意して rspec であれこれするのはなかなか大変です。

この辺をどういう感じでやるか迷っていましたが、なんとなくうまくいきそうなので今回はこの辺を紹介したいと思います。

集計のテストにおける今回の目標と今回のテスト対象

集計に関するテストが0だったので、今回は指定のデータから意図したデータを集計できているか。というところをゴールにしたいと思います。

多分他にも考えなくてはいけないところはあると思いますけど最初の一歩のゴールとしてはまあ良しとしましょう。

今回のテスト対象ですが、MySQL の snapshot データを集計しやすくする中間テーブルデータを生成するところを対象とします。

ので、複数のテーブルデータと中間テーブルのデータの整合性が保たれているかをチェック出来るようなテストを書いてみたいと思います。

テストデータのローダー

では、準備としてテストデータをローディングする所です。

rspec では FactoryGirl があるのでデータを簡単に用意することが出来ますが、 BigQuery には無いので自前で用意しました。

最初は BigQuery の load を使おうと思ったんですが、遅いです。結構遅いです。

なので schema, rows をもとに SQL を生成してその結果を指定のテーブルに挿入しています。

module FactoryBigquery

  def self.load(job)
    @_bigquery = GCP::BigQuery.new
    self.create_table(job[:dataset], job[:table], job[:schema], job[:rows])
  end

  def self.parallel_load(jobs)
    @_bigquery = GCP::BigQuery.new
    Parallel.map(jobs) do |job|
      self.create_table(job[:dataset], job[:table], job[:schema], job[:rows])
    end
  end

  def self.drop_table(dataset, table)
    @_bigquery = GCP::BigQuery.new
    res = @_bigquery.drop_table(dataset, table)
  end

  def self.create_table(dataset, table, schema, rows)
    @_bigquery.create_dataset(dataset)
    from_expr = rows.map{|row|
      select_expr = row.map{|k, v|
        s = schema.select{|s| s[:name] == k}.first
        if s[:type] == 'INTEGER'
          "#{s[:type]}(#{v}) AS #{s[:name]}"
        else
          "#{s[:type]}('#{v}') AS #{s[:name]}"
        end
      }.join(', ')
      "( SELECT #{select_expr} )"
    }.join(', ')

    sql = "SELECT #{schema.map{|s| s[:name] }.join(', ')} FROM #{from_expr}"
    res = @_bigquery.copy_table_by_query(sql, dataset, table, { 'query.writeDisposition': 'WRITE_TRUNCATE' })
    @_bigquery.wait_job(res[:job_id])
  end
end

テスト

上記のデータローダを使って実際にテストを行います

require 'rails_helper'
require 'importer/molding/user'

describe ::Importer::Molding::User do
  before :all do
    FactoryBigquery.parallel_load([
      {
        dataset: :snapshot,
        table: 'prefs_20160101',
        schema: [
          { name: 'id',               type: 'INTEGER' },
          { name: 'name',             type: 'STRING' },
        ],
        rows: [
          { 'id' => 1, 'name' => 'TEST_PREF' },
        ]
      }, {
        dataset: :snapshot,
        table: 'cities_20160101',
        schema: [
          { name: 'id',               type: 'INTEGER' },
          { name: 'name',             type: 'STRING' },
        ],
        rows: [
          { 'id' => 1, 'name' => 'TEST_CITY' },
        ]
      }, {
        dataset: :snapshot,
        table: 'users_20160101',
        schema: [
          { name: 'id',           type: 'INTEGER' },
          { name: 'family_name',  type: 'STRING' },
          { name: 'last_name',    type: 'STRING' },
          { name: 'pref_id',      type: 'INTEGER' },
          { name: 'city_id',      type: 'INTEGER' },
        ],
        rows: [
          {
            'id' => 1,
            'family_name' => 'TEST',
            'last_name' => 'NAME',
            'pref_id' => 1,
            'city_id' => 1,
          }
        ]
    ])
  end
    
  describe '#import' do
    before(:all) do
      FactoryBigquery.drop_table('moldings', 'users_20160101')
      importer = ::Importer::Molding::User.new
      importer.date = '20160101'
      importer.import
      bigquery = ::GCP::BigQuery.new
      res = bigquery.query("SELECT * FROM moldings.users_20160101 WHERE id = 1")
      @rows = bigquery.result_to_hash(res)
    end

    it do
      row = @rows.first
      expect(row['id'].to_i).to eq 1
      expect(row['name']).to eq 'TEST NAME'
      expect(row['pref_name']).to eq 'TEST_PREF'
      expect(row['city_name']).to eq 'TEST_CITY'
    end
  end

それでは実行してみましょう。

$ bundle exec rspec spec/importers/importer/molding/user_spec.rb
.
Finished in 42.02 seconds (files took 2.68 seconds to load)
1 examples, 0 failures

無事テストが通りました。

おわりに

今回はテスト化をする上で集計時のテストのデータを用意する方法と集計時のテストを行う方法について紹介しました。

まだ試行錯誤をしている段階です。

もっと良いテストの方法があればぜひ紹介していただければと思います。

ほな!

GCP の Natural Language API が公開されましたね

おはようございます。こんにちは。こんばんわ。佐々木です

今日 GCP の Natural Language API が公開されましたね。

早速使ってみたデモを載せておこうと思います

APIを有効にする

GCP の Console で Natural Language API を有効にしましょう

API キーを取得する

同じく GCP の Console で API キーを取得しましょう

demo script

さくっとこんな感じで API に POST するだけのスクリプトです。

require 'json'
require 'optparse'
require 'faraday'

require 'optparse'
opts = ARGV.getopts('', 'input:')
input = opts['input']

conn = Faraday::Connection.new(:url => 'https://language.googleapis.com') do |builder|
  builder.use Faraday::Request::UrlEncoded
  builder.use Faraday::Adapter::NetHttp
end


res = conn.post do |req|
  req.url '/v1beta1/documents:analyzeEntities?key=API_KEY'
  req.headers['Accept'] = 'application/json'
  req.headers['Content-Type'] = 'application/json'
  req.body = {
      document: {
        type: 'PLAIN_TEXT',
        language: 'ja',
        content: input
      }
  }.to_json
end

body = JSON.parse(res.body)
puts body['entities'].map{|m| "#{m["name"]}: #{m["type"]}"}.join("\n")

試す

こんな感じで input してあげれば返事が返ってきます。

まだ、ちょっと認識出来る単語が少ない気もしますけどとりあえずOKでしょう

$ bundle exec ruby nl.rb -i '田中さんはゴルフが下手くそです。'
田中: PERSON

終わりに

Speech API も出て Natural Language API と連携して音声解析とかも出来そうですね

GCPバンザイ!!!

ほな

UXデザインを意識しよう PART1

どうも!デザイナーの寺田です。最近初めてハッカソンに参加してとても楽しい思い出となりました。 最近UXデザインに関して少しずつ知識を深めていこうとしておりますので、ブログにまとめていきます。


f:id:sekaie:20160719101959j:plain

UXデザインとは

私たちはやりたいことを達成する為に製品やサービスを利用します。 そこで今までやれなかったことができたり、今までやっていたことがさらに便利になってテンションあがったり、 そういった嬉しい体験をするとさらに利用したいという気持ちになったりしますよね。

簡単にいうとそのうれしい体験のことがUX(利用のユーザー体験)であり、 それをいかに実現していくかを追求することがUXデザインです。

具体的にいうと、UXを目標にして、作り手とユーザーが共に嬉しくなる企画、設計、開発、デザインの過程のことです。

現在ますますこのUXデザインがインターネット業界で重要視されてきています。 理由としてはやはりインターネットの普及や、情報端末の高度化、多様化ですね。

引き金はiPhone

2007年1月にAppleiPhoneを発表してから現在まで、 凄まじいスピードで全世界に普及して、なくてはならないものになりました。

それと並行してスマートフォンは様々な形で進化を続け、 ユーザーそれぞれの利用目的に応じたアプリ等の機能を簡単に即入手することが可能となり、 インターネットを経由して世界中の優れたサービスを誰でも体験できるのが当たり前になってきています。

いわばiPhoneは優れたUXデザインの代表みたいなものですね。 これが世界中に普及していることで、より一層UXデザインのニーズも深まっているわけです。

ユーザビリティーを超えろ

私は業界で働き出してから今までユーザビリティという言葉は嫌という程耳にしてきました。 ユーザビリティーとはユーザーが目的を達成するまでのユーザーの目的を達成するまでのわかりやすさ、操作性の良し悪し等を重視して評価する考え方です。 私も業務をする中でそういった事に対して常に意識をかたむけてきました。

しかし、実際のユーザーの利用環境は物凄く多様化してきていて、比較的短期の視点で考えられているユーザビリティ評価では 製品とユーザーとの関係性を完全に捉えられないという気づきや新たな動きがが2000年代の前半頃から始まっていたようです。

その影響により、「プレジャラブルデザイン」(楽しみのためのデザイン)、「エモーショナルデザイン」(感情的なデザイン) といったキーワードが出現し、ユーザビリティ(操作性)だけにとらわれずに、 感性的側面にも視点を置き、製品やサービスをデザインしていく事が重要であるという流れが発生してきています。

当時は「Beyond usability」(ユーザビリティを超えろ)というスローガンもあったそうです。

そこから本格的にUXの研究がスタートしたと言われています。

うれしい体験

最近でこそ、スマホWebサービスが非常に注目されてきていてUXデザインはそれに属する内容だと思われがちですが、 実際はそういったソフトウェアに限ったことではありません。

UXデザインはどんな分野であっても共通して重要視されるべきものです。そして、いつの時代でも変わらない基準であるものだと思います。

技術というものは、ユーザーから見れば嬉しい体験を得るための必要な手段でしかないということです。 ユーザーからすると、別に技術や機能が欲しいわけではなく、技術の延長線上にある体験に関心があります。

UXデザインはそいういった体験をいかに生み出すかにフォーカスしたものです。

IoT(Internet of Things)

情報通信技術の進化は現在も留まる事を知りません。 みなさんも実感のあるものだとは思いますが、現在ありとあらゆるモノとインターネットとを組み合わせることで、 それらと相互に通信をしたり、制御できるように今後はなっていきます。

そして、そういった流れと共に「人工知能」も驚くほどのスピードで高度化して行っている為、 人が今までやっていた仕事や行動がコンピュータで事足りるようになってくる時代が来ます。

こういった技術発展によって、近い将来に産業構造や生活の変化が凄まじいものになることが予想されています。

ですが、やはり中心となるのはユーザーであり、技術が優れているだけではそのモノは開発する価値はないと思います。 どんな時代になろうと、ユーザーが心から必要とするものを目指して作っていく必要があるんです。

その為に様々な技術を束ね、きちんとユーザーに必要となる内容を統合していく方法として、 UXデザインを学習することは今後ますます必須となってくるのではないでしょうか。


今後ますます注目を集めてくるであろう「UXデザイン」 奥が深いことこの上なしですね。

ほな!

GCP の DataProc で MLlib を使う

はじめに

おはようございます。こんにちは。こんばんわ。

佐々木です。

Google Cloud Platform の DataProc で MLlib を使ってみたのでメモ書きします。

ちなみに機械学習とかは全然疎いです。

DataProc と MLLib

DataProc は Google Cloud Platform にある Spark / Hadoop が使えるマネージサービスです

cloud.google.com

MLLib は Spark に載ってる機械学習用のライブラリです

spark.apache.org

前提

  • Google Cloud Platform の DataProc API を有効にしておく
  • Google Cloud Storage も使っているので有効にしておく
  • gcloud command はインストール済

MLlib の学習用データを作成する

今回 MLlib で扱うデータは BigQuery のデータから抽出します。

今回は MLlib の決定木を使ってみます。

学習用データは csv で入力することにします。

SQLは擬似的なもので、アクセス数と購入の有無を取れるようなイメージです

SELECT
 IF(orders.user_id IS NOT NULL, 1, 0) AS result,
 COUNT(access_logs.user_id) AS cnt
FROM access_logs.access_20160711 access_logs
LEFT OUTER JOIN test.order_log_20160711 orders
  ON access_logs.user_id = orders.user_id
GROUP BY access_logs.user_id

こんな感じの SQL を書いてこれを Google Cloud Storage に export します

MLLib 用の python script を書く

DataProc で用意されていた Python Script をちょっと改修して書きました

#!/usr/bin/python
import pyspark
from pyspark.mllib.regression import LabeledPoint
from pyspark.mllib.tree import DecisionTree, DecisionTreeModel
from pyspark.mllib.util import MLUtils

def parseRow(row):
    tokens = row.split(',')
    label = tokens[0]
    features = tokens[1:]
    return LabeledPoint(label, features)

sc = pyspark.SparkContext()

data = sc.textFile("gs://test/orders.csv")
labeled_points = data.map(parseRow)

(trainingData, testData) = labeled_points.randomSplit([0.7, 0.3])
model = DecisionTree.trainClassifier(trainingData, numClasses=2, categoricalFeaturesInfo={}, impurity='gini', maxDepth=5, maxBins=32)
predictions = model.predict(testData.map(lambda x: x.features))
labelsAndPredictions = testData.map(lambda lp: lp.label).zip(predictions)
testErr = labelsAndPredictions.filter(lambda (v, p): v != p).count() / float(testData.count())
print('Test Error = ' + str(testErr))
print('Learned classification tree model:')
print(model.toDebugString())

DataProc のクラスタを作成する

これは GCP Console でも gcloud command でもどちらでもいいですが、クラスタを作成します

特に何も困らず作成出来ると思います

DataProc に JOB を投げる

作成したクラスタに上記の python script を投げます

$ gcloud dataproc jobs submit pyspark --cluster cluster-1 order.py

DecisionTreeModel classifier of depth 5 with 23 nodes
  If (feature 0 <= 18.0)
   If (feature 0 <= 10.0)
    If (feature 0 <= 4.0)
     If (feature 0 <= 3.0)
      Predict: 0.0
     Else (feature 0 > 3.0)
      Predict: 1.0
    Else (feature 0 > 4.0)
     Predict: 0.0
   Else (feature 0 > 10.0)
    If (feature 0 <= 11.0)
     Predict: 1.0
    Else (feature 0 > 11.0)
     If (feature 0 <= 16.0)
      Predict: 0.0
     Else (feature 0 > 16.0)
      If (feature 0 <= 17.0)
       Predict: 1.0
      Else (feature 0 > 17.0)
       Predict: 0.0
  Else (feature 0 > 18.0)
   If (feature 0 <= 68.0)
    If (feature 0 <= 44.0)
     If (feature 0 <= 37.0)
      If (feature 0 <= 20.0)
       Predict: 0.0
      Else (feature 0 > 20.0)
       Predict: 1.0
     Else (feature 0 > 37.0)
      Predict: 0.0
    Else (feature 0 > 44.0)
     Predict: 1.0
   Else (feature 0 > 68.0)
    Predict: 0.0

うーん。学習用データが少なくてちょっと残念な感じになってしまいました。

もうちょっとデータを増やしてやる必要があるみたいですね。

しかし、今回の目的の DataProc で MLlib を利用して決定木を作成することはできました。

おわりに

DataProc をつかってデータを処理するのは楽しいですね。

DataProc と Bigquery あたりを連携して定常的にデータ分析ができる仕組みを作りたいなと思います。

また何か進捗があったらこのブログで報告させていただければと思います。

ほな!