Goalist Developers Blog

Angularでenvironmentsの中身をいじって環境毎に変数の中身を設定したい

おはようございます。バンナイです。 Angularでenvironmentsの中身をいじって環境毎に変数の中身を設定したいです。

動機

今度新しく作るステージング環境で呼び出すAPIを既にある本番環境で呼び出すAPIとは別にしたい。

デプロイしたい環境に応じてAPIのURLを手作業で書き換えることもできる。

しかし、environmentsの中身をいじればビルドをする際のコマンドを変えるだけでそれが実現できると先輩から教えてもらったのでそれを試してみます。

作業

environments配下にファイルを作成、編集

src/environments 配下に environment.staging.tsを作成。中身は

export const environment = {
  production: false,
  apiUrl:"staging環境でのurl"
};

environment.dev.tsの中身は

export const environment = {
  production: false,
  apiUrl:"dev環境でのurl"
};

environment.prod.tsの中身は

export const environment = {
  production: false,
  apiUrl:"prod環境でのurl"
};

それぞれ以上のようにします。

.angular-cli.jsonの内容を編集

environmentsのところの内容を以下のようにします。

"apps": [
    {
     ・・・略
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts",
        "staging": "environments/environment.staging.ts"
      }
    }
  ]

environment を import

変数を利用したい箇所でenvironment を importしたり等。 今回はテストのためにapp.component.tsで環境毎に中身が変わる変数を用意して、変数の中身をapp.component.htmlで画面に表示して、環境毎に変数の中身がちゃんと書き換わっていることを確認します。

app.component.ts

・・・略
import {environment} from "../environments/environment";
・・・略
export class AppComponent {
  public apiUrl = environment.apiUrl;
・・・略

app.component.html

<p>{{apiUrl}}</p>

確認

ローカル環境で確認してみます。

ng s --env=dev
ng s --env=prod
ng s --env=staging

で動かしてみて、それぞれ画面に

dev環境でのurl

prod環境でのurl

staging環境でのurl

と表示されたらオッケーです。

f:id:bbbbbbbbb9:20180208134351p:plain

実際にデプロイする時は

ng build --env=staging

等とすればデプロイしたい環境に応じて、コードを書き換えることなく、変数の中身を変更できます。

統計・マーケ・機械学習 Meetup! #4 @ ゴーリスト

そういえば,数か月ぶりの執筆担当だ....

お久しぶりです.新卒エンジニアのナカノです.

今回は,ゴーリストオフィスで行われた勉強会のレポートを書こうと思います.

勉強会のテーマは「統計・機械学習」です.

目 次
  • 勉強会の概要
  • 何はともあれ、まずはカンパーイ!
  • LTの内容
    • 1. 使えるデータの作り方
    • 2. DBスペシャリストに合格して自由を手に入れた話
    • 3. AICについて
    • 4. 統計検定を受けてみた
    • 5. pythonとmatlabを使った最適化とこれから読みたい本
    • 6. GoogleのAI分析の結果を、実地で確かめてみた
    • 7. 機械学習初心者が短期間でTensorFlowで作ったMNISTモデルをAndroidアプリに組み込んだ話
    • 8. IT勉強会開催の進めと「続ける」技術
    • 9. ピクトさん量産計画
    • 10. 分析手法紹介
  • 締め括り



勉強会の概要

以下のURL先のページに,勉強会の概要が書かれております.一度ご覧頂ければと思います. data-refinement.connpass.com



何はともあれ、まずはカンパーイッ!

Meetupということで,飲み食いしながら様々なLTが堪能出来る形式となっております.

さてさて,まずは各自飲み物を持って,せーの......カンパーイッ!

f:id:r-nakano:20180125015027j:plain

思いの外参加者が多く,その分だけこれから始まるLTの発表に胸が高鳴るナカノでした.

お~,ドキがムネムネッ!(某クレヨンし○ちゃんのネタ)



LTの内容

各LTの発表のまとめを,self-containedな形で以下に与えておこうと思います.

1. 使えるデータの作り方

f:id:r-nakano:20180125155328j:plain

主に,データの必要不可欠さとデータの加工に関することを発表されておりました.

何事においてもデータは必須であり,それを有効活用するためには「必要な時に必要な形」でデータが使える様にする必要があります.

そのために,CrawlingやScrapingという技術を用いてデータの加工整形が行われます.


2. DBスペシャリストに合格して自由を手に入れた話

f:id:r-nakano:20180125022557j:plain

発表によると,即戦力になるためにはやはり少なくとも資格を取得しておいた方が良いとのことです.

ですがその一方で,mqtsuo02さん自身が様々な業務を経験してきた結果から,資格の取得だけでなく「自分次第な部分を変えるための実践」も非常に大切だと説いていらっしゃいました.


3. AICについて

f:id:r-nakano:20180125135827j:plain

相関係数の話から発表は始まり,次第にAICに関する様々なツールや概念を概説されていらっしゃいました.

その時,聴講者たちはどんな表情をしていたのでしょうか...?

f:id:r-nakano:20180125140752j:plainf:id:r-nakano:20180125140801j:plain

おっと,何とも言えない表情をされていらっしゃるゾ....


4. 統計検定を受けてみた

f:id:r-nakano:20180125135915j:plain

cougarさんの「フリーランスになった経緯」から「統計検定を受けた時の状況」などについて,発表されておりました.

統計や機械学習がビジネスで活かせそうだとcougarさんは考えており,特に「顧客サービスの休止ユーザーに関する分析」への応用に期待していらっしゃるそうです.


5. pythonとmatlabを使った最適化とこれから読みたい本

飛び込みLTの発表その1です.発表者のmoritaさんは「人工衛星により捉えた生物の住む森林のエリアの画像を分析し,そのエリアの面積の変化を調査する」ということを行ってらっしゃるそうです.

その際にpythonやmatlabを使った最適化処理を実施されているのですが,現状ではデータ分析の方法や手順や方法が確立されているが分析精度の大小などは評価しづらいとのことです.

それ故にデータを観察し想定や着想などを持つことが大事であり,それは機械学習の利用においても言えることであるそうです.

また,発表の最後にオススメの関連書籍を紹介されておりました.


6. GoogleのAI分析の結果を、実地で確かめてみた

飛び込みLTの発表その2です.発表内容は,Googleの「スマートクッキー」プロジェクトに関するものでした.AI分析の結果として出力されたレシピは非常にシンプルでした.

AIの判定を実験的に確かめるため,そのレシピをもとにいざ作ってみたそうです.結果ですが,レシピに改善すべき点が幾つかみられました.

判明したことですが,AIのアウトプットは業務に落とし込む部分の手前のものなので,やはりその様な部分で人の役割は必要不可欠であるということです.


7. 機械学習初心者が短期間でTensorFlowで作ったMNISTモデルをAndroidアプリに組み込んだ話

発表内容は「数字が写っている画像を認識して正解の数字を特定するアプリの開発」に関してです.

開発の際に,Kerasという「Tensor Flowなどをバックエンドとしたニューラルネットワーク用ラッパーライブラリ」を使用しているとのことです.

まえすとろさんによれば,Kerasは初学者でもアプリ開発で何とかなる有能なツールだそうです.


8. IT勉強会開催の進めと「続ける」技術

発表によると,IT勉強会の開催のメリットとしては「勉強会はやりとりや知識・認識の共有がしやすい環境であるため,参加する意義がある」とのことです.

また,継続は力なりということも説いていらっしゃいました.

例えば「30日間をどのように続けるか?」とか「反抗期、不安定機、倦怠期などの作業継続が困難になりがちなフェーズに陥った場合,如何にして乗り越えるか?」などの話がありました.

聞いているうちに,継続は力なりという言葉は非常にシンプルだが大変重要だなと再認識しました.


9. ピクトさん量産計画

写真からのピクトの生成に関して発表されておりました.

ピクトの生成の際にDeep Learningによるポーズ推定が行われており,その結果として「画像にある人のポーズから様々なピクトの生成が可能」となっております.

ポーズ推定ですが,対象が一人或いは複数人の場合のそれぞれで推定を行います.

複数人の場合は,まずはそれぞれの部分を切り取り,そこから先は「一人の場合の推定方法」を使って推定が行われます.

推定の実験の中で,何故か大阪のグリコの画像からはピクト変換出来なかったそうです.


10. 分析手法紹介

最後の発表は,様々な分析手法の紹介に関してでした.発表の中で

  • 数理最適化(近藤次郎(最適化))、探索問題(近藤次郎(最適化))
  • 応用待ち行列(本間鶴千代(待ち行列の理論))
  • 世論調査モデル(深尾毅(分散システム論))
  • 昇給報酬モデル(羽鳥裕久(有限マルコフ連鎖))

といったものが紹介されていました.



締め括り

以上が,勉強会で発表された内容のまとめでした.各々のまとめの内容に凹凸があり,非常に稚拙なまとめとなってしまいました.

ただ,勉強会の状況や様子をお伝えすることは出来たかなと思っております.

私は現在インフラ関連の業務を行なっておりますが,統計・機械学習・関数型言語の分野にも興味があるため,分野に囚われずに積極的に勉強していければと思います.

SQLAlchemyでsetattrしたのにupdateされない

こんにちは ゴーリスト開発のモリツグです

最近ちょっとずつPythonを触り始めたひよっこです。
Python3.6でSQLAlchemyを使っていて何故かupdateが行われない現象に悩まされたので、原因と対処法をメモしておこうと思います。

ぶちあたった問題

sessionの取得やcommit flush closeあたりは適切に行われているものとして省略します。 あとPythonなのにキャメル記法なのは一旦みなかったことにしてください。

・・・(sessionの取得)・・・

# 更新したいレコードを取得
dbAccount = session.query(Account) 
    .filter(Account.id == account.id) 
    .first()

# 更新したい値を持っているaccountからdbAccount へ値をコピーして反映
for key, value in account.__dict__.items():
    if callable(value) == False: # 関数は省く
        setattr(dbAccount, key, value)

・・・(commit flush closeの処理)・・・

上記のようなソースコードを書いた場合、デバッガ上ではdbAccount の値が更新されているのに、DB上の値は更新されません。
試行錯誤した結果、setattrのvalueをちゃんとキャストしてやるとDB上の値も更新されました。

# 更新したい値を持っているaccountからdbAccount へ値をコピーして反映
for key, value in account.__dict__.items():
    if callable(value) == False: # 関数は省く
        # setattr(dbAccount, key, value) # valueの型を判定してキャスト
        if isinstance(value, int):
            setattr(updateEntity, key, int(value))
        elif isinstance(value, str):
            setattr(updateEntity, key, str(value))
    ・・・(それ以外の型の処理)・・・

何故こんなことになっているのか、詳しい理由は不明ですが、きっと何か深淵なる理由があるのだと思います。

Observableでクリックイベントを制御してドロップダウンメニューを作ってみる【Angular】

こんにちは。ゴーリスト開発の飯尾です。
AngularでWebアプリを作ったりしています。
世の賢人によるイルなコンポーネントだと微妙に機能が多すぎたり足りなかったりで
結局自作のワックコンポーネント生産したりしますよね。

作りたいもの

  • Airbnb一休の検索条件指定部分みたいなドロップダウンメニュー
  • 内側クリックで閉じず、外側クリックで閉じる
  • 一画面でひとつだけ開く

イメージとしてはこんなやつです。

f:id:y-iio:20180109180913g:plain

参考にしたもの

基本的にはこれパクっ参考にしましたが
ここのところがちょっとな〜だったので書き換えました。

@HostListener('click', ['$event']) onClick(event: Event) {
    if (!this.hideOnClick) {
        event.stopPropagation();
    }
}

変えたいポイント

その1

ドロップダウンを開いている時のみクリックイベントを拾いたい(@HostListenerでイベント登録するとリムーブできない)

github.com

その2

iOSだとdocumentをルート要素としてクリックイベントを登録しても拾ってくれない

qiita.com

解決法を考える

ドロップダウンを開いている時のみクリックイベントを拾いたい 問題

Observableでクリックイベントを制御する

qiita.com

// ドロップダウン開いてる時
const stream = Observable.fromEvent(document, 'click').subsucribe(() => {
  console.log('clicked!');
});

// 閉じたら購読をやめる
stream.unsubscribe();

iOSだとdocumentをルート要素としてクリックイベントを登録しても拾ってくれない 問題

Observable.fromEvent(document, 'touchend')ならOK

そうしてできたもの

import {
  ChangeDetectorRef, Component, ElementRef, HostBinding, Injectable, OnDestroy, OnInit, ViewEncapsulation
} from '@angular/core';
import { Observable, Subscriber } from 'rxjs/Rx';

@Injectable()
export class DropdownRegistry {
  private dropdownMenuComponents: any[] = [];

  constructor() { }

  public add(dropdownMenu: DropdownMenuComponent) {
    this.dropdownMenuComponents.push(dropdownMenu);
  }

  public remove(dropdownMenu: DropdownMenuComponent) {
    this.dropdownMenuComponents.slice(this.dropdownMenuComponents.indexOf(dropdownMenu), 1);
  }

  public hideAllExcept(dropdownMenu: DropdownMenuComponent) {
    this.dropdownMenuComponents.forEach((component) => {
      if (component !== dropdownMenu) {
        component.hide();
      }
    });
  }
}

@Component({
  selector: 'app-dropdown-menu',
  template: `<ng-content></ng-content>`,
  encapsulation: ViewEncapsulation.None // ここはカプセル化しない
})
export class DropdownMenuComponent implements OnInit, OnDestroy {
  @HostBinding('class.is-visible') isVisible: boolean = false;

  private clickEventStream: any; // Observable<Event>とかObservable<any>だと警告出る
  private subscription: Subscriber<any>;

  constructor(
    private changeDetectionRef: ChangeDetectorRef,
    private dropdownRegistry: DropdownRegistry,
    private elementRef: ElementRef,
  ) {
    this.dropdownRegistry.add(this);
  }

  ngOnInit() {
    // iOSだとボタンとかリンクの要素以外はタッチしてもクリックイベントとしてみなされない
    this.clickEventStream = Observable.merge(Observable.fromEvent(document, 'click'), Observable.fromEvent(document, 'touchend'))
      .catch(e => Observable.throw(e));
  }

  ngOnDestroy() {
    this.dropdownRegistry.remove(this);
  }

  public toggle(event: Event): void {
    if (this.isVisible) {
      this.hide();
    } else {
      this.show(event);
    }
  }

  public hide(): void {
    this.isVisible = false;
    if (this.subscription && !this.subscription.closed) { // stream流れてるなら止める
      this.subscription.unsubscribe();
    }
    this.changeDetectionRef.markForCheck();
  }

  public show(event: Event): void {
    event.stopPropagation();
    this.hideAll();
    this.isVisible = true;
    this.subscription = this.clickEventStream.subscribe((clickEvent: Event) => { // 外側クリック時に閉じる
      clickEvent.stopPropagation();
      if (this.isVisible && !this.elementRef.nativeElement.contains(clickEvent.target)) {
        this.hide();
      }
    });
  }

  private hideAll(): void {
    this.dropdownRegistry.hideAllExcept(this);
  }

}

あとはドロップダウン表示用のトグルボタンとCSSをいいかんじにする
いいかんじにする

こんな感じで動いているよ

f:id:y-iio:20180109181027g:plain

おわりに

もっとドープな書き方を教えてくれるマイメンを募集しています!!

テンソルフローの第一歩、環境構築 (Mac)

こんにちは、ゴーリストのチナパです。 現在の一番人気な機械学習ライブラリがグーグルのテンソルフローです。ここではテンソルフローの環境を作る方法を紹介します。まずは「テンソルとは何か?」についてお話しします。 f:id:c-pattamada:20180104130155p:plain

テンソルとは何か?

テンソルは数学でよく使われるベクターに似ています。それはつまり、テンソルは数字的に情報を表す為の道具です。仮想的な空間で等級と方向の組み合わせで情報を表しています。例えば、

[3 2]

と言うテンソルは「西側3歩、北側2歩」のような二次元空間で場所を表したり出来ます。

ただし、本当の力はこの方向の数を増やしたことによって、[力、魔力、速度、HP] などの「方向」でキャラクターでも表したりすることです。現実的な使い方を想像しましょう。「プラス性」と「マイナス性」を見つける為の便利な方向 [嬉しさ、ワクワク性、モティベーション] と [不満性、怒り、悲しみ]の仮想的な方向で以下の風にツイッターのポストを理解することも出来ます。

f:id:c-pattamada:20180104113222p:plain

もちろん、これは簡単にする為の軸を選んでいるんです。実際にパソコンがどんな方向や軸が使っているは人間にはもうちょっと分かりにくいでしょう。 自分よりまともな説明ができる人がいるんですが、リンクします。
(日本語版) テンソルとは何か? - YouTube (数学的な説明)

(英語版) What's a Tensor? - YouTube

環境構築

では、少しテンソルについて話しましたが、そろそろ環境を作りましょう。自分はマック使っているので、マックのPythonインストールのやり方を紹介します。

まずは、必要なもののダウンロードです。マックのターミナルを開いて、

sudo easy_install pip
pip install --upgrade virtualenv

を入力します。sudo なのでパスコードが必要です。

それから、この仮想環境を立ち上げてテンソルフローを使う為のディレクトリを作りましょう。例えば、
mkdir ~/tensorflow

そしてこのディレクトリで仮想環境を作りましょう
virtualenv --system-site-packages -p python3 ~/tensorflow

f:id:c-pattamada:20180104115234p:plain

(python 2を使いたい場合には virtualenv --system-site-packages ~/tensorflow)
出来ましたら、source ~/tensorflow/bin/activateで発動出来ます。うまく行きました場合、
(tensorflow)[パソコン名]$_
見たいなものが見えます。

ここで、また

easy_install -U pip
pip3 install --upgrade tensorflow

(また、python 2.7の場合は pip install --upgrade tensorflowです) で必要なものをダウンロードし、準備オッケーです。

パイソンを起動し、テンソルフローをimportしましょう
python
import tensorflow as tf

うまく行きましたら、いかの画像のように見えます。

試して見ましょう、

c = tf.constant([3, 2])
c

f:id:c-pattamada:20180104120454p:plain このような結果が出てくれば、バッチリです!
様々なインストールのやり方がこちらです。 

Installing TensorFlow  |  TensorFlow (英語)

iOS11になって変わったAppStoreまとめ

こんちは。渡部です。

f:id:watabe1028:20171222193758j:plain

この季節の「気づいたら指先が切れてた」現象をなんとかしたいです。はい。

今回はiOS11になって変わったことAppStore編を思いつく限り書いていきます。
ご参考までに。
 
 

1.アイコン

 アイコンが変わってます。
 筆者は訳あってiOS10を保ってます。
 (画像サイズが違うのは勘弁)

 iOS10
f:id:watabe1028:20171222194229p:plain
 
 iOS11
f:id:watabe1028:20171222194241j:plain
 
 

2.トップセールスランキングが削除

 インストール基準の一つがなくなるのは
 ゲーム系開発会社にはかなりの痛手かと。
 なのでこれからは流入経路は広告か
 検索からのインストールに偏っていきそうです。
 
 iOS10
f:id:watabe1028:20171227103502j:plain
 
 iOS11
f:id:watabe1028:20171227103518j:plain

 
 

3.タブが「Today」「ゲーム」「App」に

 2ともかぶるんですが「見つけやすい」ようにリニューアルされてると思います。
 ランキングは勝手に入ってくる情報ですが
 検索は目的のアプリを探す行為です。
 なので見つけやすさ、ファーストオンボーディングが重要になってくるかと。
 
f:id:watabe1028:20171227103049j:plain

 
 

4.ストアのアプリ一覧で横画面が可能に、スクショの枚数は3枚に

 今までは検索後の一覧画面では2枚のスクショが表示されてましたが
 iOS11からは3枚表示されます。
 また1枚の横画像の設定も可能になりました。
 
 iOS10
f:id:watabe1028:20171227103157p:plain
 
 iOS11
f:id:watabe1028:20171227103219p:plain

 
 

5.コメントへの返信機能

 レビューに対して返信できるようになりました。
 CXという言葉が流行り始めているように
 これからはアプリもカスタマーへのサポートが大事になっていきます。
 また、レビューをリセットすることもできます。
 ASO的にはレビューの件数や星の数が非常に大事になってくるので
 レビューリセットは考えものですね。

 
 

所感

重複しますがストアが全体的に「見つけやすく」なるようにリニューアルされた気がします。
またレビューの返信機能など、アプリをサービス化し、サポートもしっかりやれよ!的な
メッセージのようなものも感じます。
改善が停止されてるアプリが溢れてますからね。。。
 
アプリのインストール基準でもあるレビュー件数と星の数がさらに重要度が増した気がします。
ランキングよりも評価やサポートがユーザーのインストール基準になっていく傾向でしょうか。
 
アプリの露出からサポートまで、UXの定義がまた広くなりそうです。
 
 

Selenium2を3に上げてみる

こんにちは ゴーリスト開発のモリツグです

そろそろSelenium2から危険な香りがするので、3に上げることしました。
ChromeのヘッドレスがEC2を利用してクローリングする時にすごい便利ですね。
今回はJavaでSelenium3 WebDriverを動かしてみたいと思います。

手順

公式サイトのダウンロードからJavaのライブラリをダウンロードする
・EclipseやInteliJなど好みのIDEでライブラリ参照するように設定する
ドライバダウンロードページから環境にあったchromeドライバを落としてくる
・ソースコードを書いて確かめる

今回落としてきたSeleniumのバージョンは3.8.1でした、知らない間に4になりそうな勢いです。
注意点としてはchromeブラウザのバージョンにドライバのバージョンが対応しているかどうかを確認することです。
「同じソースコードなのにある日突然動かなくなった!」という場合は、だいたいChromeが更新されてバージョンが上がったせいで、ドライバが対応できなくなったパターンが多いです。

Chromeは公式サイトからは最新版しか落とせないので、Chromeが最新になっちゃったけど、それに対応した最新のドライバが出ていない状況というのは起こりえます。
「今すぐに動かさないとやばい!古いバージョンのChromeが欲しい!」という場合は、以下のサイトから落とせます。古くした後は忘れずに自動更新はoffにしときましょう。

www.slimjet.com

では最後にver2と全く同じソースコードで動くのか確認します

public void exec() throws Throwable {
    System.setProperty("webdriver.chrome.driver", "path to your chrome driver");
    WebDriver driver = new ChromeDriver();
    // ヘッドレスの場合
//     ChromeOptions chromeOptions = new ChromeOptions();
//     chromeOptions.addArguments("--headless", "window-size=1280,1024"); // xvfbとか一切無しでdislayサイズも指定できちゃう
//     WebDriver driver =  ChromeDriver(chromeOptions);
    

    driver.navigate().to("https://www.google.co.jp/");

    WebElement input = driver.findElement(By.cssSelector("#lst-ib"));
    input.sendKeys("goalist");
    input.sendKeys(Keys.chord(Keys.ENTER));

    ExpectedCondition<List<WebElement>> waitFunc = new ExpectedCondition<List<WebElement>>() {
        public List<WebElement> apply(WebDriver driver) {
            List<WebElement> curTitleEle = driver.findElements(By.cssSelector("#search .rc .r a"));
            if (curTitleEle == null || curTitleEle.size() == 0) {
                return null; // NoSuchElementExceptionかreturn nullしてるあいだは続行
            }
            return curTitleEle;
        }
    };

    Wait<WebDriver> wait = new WebDriverWait(driver, 15);
    try {
        System.out.println("start wait");
        List<WebElement> result = wait.until(waitFunc);
        result.get(0).click();
        System.out.println("end wait");
    } catch (TimeoutException e) {
        e.printStackTrace();
        System.out.println("time out");
    }
}

まとめ

Chromeを使う限りではselenium2と全く同じソースで動くようです。
今すぐ3に上げましょう。