OmniAICreator/Qiita-1.07M huggingface dataset:
Rustの構造体などに追加できる振る舞いを確認する


Rustでは`derive`を使用して構造体や列挙型などに振る舞いを追加することができます。
ここでは標準ライブラリで使える主要なものの動きを確認してみます。



# Debug
デバック出力を行えるように振る舞いを追加します。
`println!`マクロを使用する際に`{:?}`というデバック出力用のフォーマットを指定することでデバック出力を表示させることができます。

```Rust
#[derive(Debug)]
struct Data{
  value: i32,
}
fn main() {
  let d = Data {value: 2};
  println!("{:?}", d);
}
```

出力

```
Data { value: 2 }
```

# PartialEq
オブジェクト同士が等価であることを比較するための振る舞いを追加します。

```Rust
#[derive(Debug,PartialEq)]
struct Data{
  value: i32,
}
fn main() {
  let d1 = Data {value: 2};
  let d2 = Data {value: 2};
  assert_eq!(d1, d2);
}
```

# Eq
同じ値での比較が**すべての値**で真に成る場合に付与できる振る舞いです。
たとえば、浮動小数点数型はNaN値同士の比較が真にならないので`Eq`が実装されていません。
構造体の場合は`Eq`が実装されていないメンバを持つ時に`Eq`の振る舞いを付与しようとするとコンパイルエラーになります。

ちなみに`Eq`を実装するには予め`PartialEq`も実装してある必要があります。

```Rust
#[derive(Debug,PartialEq,Eq)]
struct Data{
  value: i32,
}
fn main() {
  let d1 = Data {value: 2};
  let d2 = Data {value: 2};
  assert_eq!(d1, d2);
}
```

# PartialOrd
大小を比較するための振る舞いを追加します。
構造体は複数のフィールド持つ場合がありますが、その場合は先に定義されたものから順番に比較が行われます。

ちなみにPartialEqも一緒に実装されている必要があります。

```rust
#[derive(PartialEq,PartialOrd)]
struct Data{
  value: i32,
}
fn main() {
  let d1 = Data {value: 2};
  let d2 = Data {value: 1};
  assert!(d1 > d2);
}
```

# Ord
順序付け不能な値が存在しない物だけに追加できる振る舞いです。
例えば浮動小数点型ではNaNという順序付け不能な値が存在するのでOrdを実装することができません。

下記の例ではOrdの振る舞いを使用してソートする`sort`メソッドを使用しています

```rust
#[derive(Debug,PartialEq,Eq,PartialOrd,Ord)]
struct Data{
  value: i32,
}
fn main() {
  let d1 = Data {value: 2};
  let d2 = Data {value: 1};
  let d3 = Data {value: 3};
  
  let mut s = [d1, d2 ,d3];
  s.sort();
  print!("{:?}", s);
}
```

出力

```
[Data { value: 1 }, Data { value: 2 }, Data { value: 3 }] 
```

# Clone
クローン可能になる振る舞いを追加します。

下記の例ではクローンされたオブジェクトのポインタが異なっていることを出力しています。

```Rust
#[derive(Debug,Clone)]
struct Data{
  value: i32,
}

fn main() {
  let d1 = Data {value: 2};
  let d2 = d1.clone();

  //　アドレスの取得
  let raw_d1 = &d1 as *const Data;
  let raw_d2 = &d2 as *const Data;

  println!("d1: {:?}", d1);
  println!("d1 pointer: {:?}", raw_d1);
  println!("d2: {:?}", d1);
  println!("d2 pointer: {:?}", raw_d2);
}
```

出力例


```
d1: Data { value: 2 }
d1 pointer: 0x7ffd0ec0f4a0
d2: Data { value: 2 }
d2 pointer: 0x7ffd0ec0f4a8
```

同じ値を持つ構造体ですがアドレスが別になっています

# Copy
=演算子を使用したときに所有権の移動ではなく、クローンを行う振る舞いを追加します。

`Copy`を実装するには`Clone`も実装されている必要があります。

```Rust
#[derive(Debug,Clone,Copy)]
struct Data{
  value: i32,
}

fn main() {
  let d1 = Data {value: 2};
  let d2 = d1;

  //　アドレスの取得
  let raw_d1 = &d1 as *const Data;
  let raw_d2 = &d2 as *const Data;

  println!("d1: {:?}", d1);
  println!("d1 pointer: {:?}", raw_d1);
  println!("d2: {:?}", d1);
  println!("d2 pointer: {:?}", raw_d2);
}
```

出力例

```
d1: Data { value: 2 }
d1 pointer: 0x7ffc21f69990
d2: Data { value: 2 }
d2 pointer: 0x7ffc21f69998
```

同じ値を持つ構造体ですがアドレスが別になっています

# 参考資料
https://doc.rust-jp.rs/book/second-edition/appendix-03-derivable-traits.html
リンクバルの共通認証基盤について

## はじめに

この記事はLinkbal Advent Calendar 2018の25日目の記事です。技術部の船寄が担当させて頂きます。

🎄🔔聖夜の更新となりました。皆さまいかがお過ごしでしょうか。🤶🎄
今回は、今年の年始にリリースを行った共通認証基盤（リンクバル ID）についてご紹介したいと思います。

## 共通認証基盤（リンクバル ID）とは
OpenID Connectを利用した、共通認証基盤です。よく、`ほげほげ ID`というフレーズを目にすることがあると思いますが、それですね。
１つのIDで複数サービスを利用できるようにするものです。
現在リンクバルでは、[machiconJAPAN](https://machicon.jp/)、[CoupLink](https://couplink.jp)、[KOIGAKU](https://koigaku.machicon.jp/)の3サービスで利用されております。

## 主な機能について
主に下記の機能を実装しています。

* OpenID Provider(OP)としての機能
    * Authorization Endpoint
    * Token Endpoint
* UserInfo Endpoint
* ユーザ情報の変更（一部）
* パスワードのリセット機能
* メッセージ送信（メール、SMS）
* システム管理者用管理画面

## OpenID ProviderのOSS利用
`OpenID Provider`に関してはこちらのOSSを使わせていただきました。[ory/hydra](https://github.com/ory/hydra)
OpenID Provider(OP)としての機能を提供してくれるものです。ただし、Authorization Endpointの認証についての機能や、ユーザ情報の保持については自分で実装する必要があります。
もう少し具体的には、下記につて自前で実装する必要がありました。（実装しました。）

* ログイン機能
* 新規登録機能
* プロフィール登録機能

※もちろん`UserInfo Endpoint`もhydraには含まれておらず、自前で実装しました。

## アプリケーションの分割
実装する機能から、マイクロサービス風に、下記のようにアプリケーションを分割して構築することにしました。

* OP
    * hydraそのもの
* IDP
    * 新規登録、ログイン、パスワード再発行、ユーザ情報更新などの機能を提供するアプリケーションです。
* UserInfo
    * Userinfoエンドポイント
* Message
    * メール、SMS送信
* Admin
    * 管理画面（CS対応用）

## 開発言語/ライブラリなど
* golang(go1.9.1)
    * gin-gonic/gin
    * go-xorm/xorm
    * gorilla/securecookie
    * gorilla/sessions
    * validator.v9
    * gorilla/csrf
    * gorilla/schema
    * spf13/viper
    * ory/hydra
    * GoogleCloudPlatform/cloudsql-proxy

golangを当社で初めて導入しました。hydra以外に、特に珍しいパッケージなどは利用しておりません。

## インフラについて
Google Cloud Platformを利用しております。今回はGoogle Kubernetes Engineを採用し、コンテナでの運用に挑戦しました。
Ingressを利用し、ルーティングルールを設定することにより、単一のロードバランサとパブリックIPアドレスで、複数のアプリケーションを運用しております。

![イメージ.png](https://qiita-image-store.s3.amazonaws.com/0/53317/7e35d42f-268e-4a94-a415-5c314a1d8c5d.png)

データベースはCloudSQL（MySQL5.7）を利用しています。全てのアプリケーションから単一のデータベースを利用する設計としております。
また、Stackdriver Loggingに全てのログを出力しています。




## dockerイメージのビルドとデプロイ

### ビルド
Cloud Buildを利用し、dockerイメージをビルドしています。Cloud Buildのトリガーにgithubリポジトリへの `タグへの push` をトリガーとして登録しております。
プッシュされたタグをリリースする運用です。
ビルドする際に複数のアプリケーションを１つのイメージの中にまとめています。

### デプロイ
`kubectl`をでデプロイしています。具体的には下記のようなイメージです。

```deploy.sh
RELEASE_TAG=$1
sed -e "s/<TAG>/$RELEASE_TAG/g" ./application.yml | kubectl apply -f -
```

```application.yml
# application.ymlの一部
spec:
  containers:
  - name: userinfo
    image: hoge.gcr.io/linkbal/id:<TAG>
```

## モニタリング
Datadogを利用してモニタリングを行っております。
また、アプリケーションエラーの検知に関してはStackDriver Loggingに出力されたerrorログを検知し、Slackへ通知する仕組みとなっています。

## 開発環境
docker-composeを利用して環境を構築するようにしています。
複数サービスで利用される基盤であることから、開発者がなるべく簡単に環境を再現できるようにしております。
リポジトリをcloneし、`docker-compose up` を叩くだけで初期マスタの投入を含めて環境構築をしてくれます。

## 運用後のトラブル
hydraの中で適切にindexが貼られておらず、データベースに深刻な負荷を与える事案が発生しました。
slowqeryを確認すると、token周りのテーブルで適切にindexが貼られていないことが判明しました。
その他は、特に大きな問題なく運用を続けることができています。

## おわりに
各社で共通IDを持つ事が当たり前になりつつありますが、皆さまどのように構築されているのか気になりますよね。
まだまだお話したいことはあるのですが、この辺りで終わりにしたいと思います。
それでは良いクリスマスを！🎄
2018年を振り返って

お疲れ様です、づやです。

この記事は[LIG Advent Calendar 2018](https://qiita.com/advent-calendar/2018/lig)の最終日の記事です。

早いもので、生まれ落ちてから35回目の年の瀬を迎えています。
LIGは今12期目を迎えています。
人生の３分の1をこの会社と共に過ごしてきたんだと思うと、なかなか感慨深いものがありますな。

## 2018年の振り返り
2018年はなかなか個人的にも組織的にもけっこう変化があった年となりました。

### お仕事的な話
弊社は受託開発を行っている会社です。
2018年も非常にありがたいことに、たくさんの開発のお仕事を頂くことができました。
改めて頂いた案件を振り返っていたのですがシステム開発、サービス開発のお話をいただける事が非常に増えてきました。
今年はPHPのお仕事ではLaravelを使うものが増えました。
pythonの楽しげな開発案件もあったりといい変化がでてきたとのではないかと思います。
フロントエンドに関しては気づいたらNuxt.jsを使うこことが多くなってました。
2019年も前半はNuxt.js多いんだろうなと思いつつ、年末は全然違う模様になってるかもななんて思ってます。
インフラ周りはAWSで作る事が多いですね、こりゃもうしばらく変わりませんな。
GCPももちっと触っていきたいのですが。

### 会社的な話
１番大きいのは、会社が引っ越しました。
2013年に引っ越していたので、5年ぶりの引っ越しとなりました。
台東区内での移動となりましたが、いろいろあって固定IPアドレスが変わってしまいました。
固定IPが変わる、つまりIP制限かけてるようなサーバは弊社都合で繋がらなくなるリスクが発生するというけっこう大変なイベントです。
2013年の時にもIPが変わってドタバタしたのですが、今回は引っ越して２ヶ月たった今もIPによる問題は聞こえてこないのでうまくできたのではないでしょうか。
うちのバックエンドチームがしっかり頑張ってくれたおかげです、ほんとうにありがとう。
※2013年の時は１件だけ失敗してしまってたりします、解決はできたのですがあわあわしました。
あと引っ越しに乗じて椅子がよくなりました、実は。

### 個人的な話
CTOに戻りました。
あんまり役職にこだわりはないのですが、割と大きな出来事でした。
軽く背景をお話すると、私は今の会社でいうとファウンダーであり、取締役であり、チームリーダーでした。
偉そうな肩書きを持ちつつも、チームリーダーとして部下の下で動くことも多いというよくわからない存在と化していたのですが、口に出さないまでも皆がやりづらさを当たり前ですが感じていたらしく、組織の事を考えた結果の配置となりました。
肩書きに意味はないので、2020年もCTOとして結果を残して振り返れるよう頑張りたいと思います。

## まとめっぽい話
どういう切り口で書くかちゃんと決まらないまま、書いてますこの記事。
こんな感じで書いてくといくらでも書けてしまうので無理やりまとめに入ります。

１番大きな変化は、案件の最前線を卒業することです。
良くも悪くもずっと案件に入ってコード書いてました。
ちょっと寂しいですが、もうその動きは卒業します。
すでに俺より全然できる人がいるし、育っています。
そういう人たちがずっとLIGにいてくれて、楽しく過ごしてくれる組織にできるよう、尽力したいと思います。
当たり前のことなんですけどね。個人的にはこれほんとに大きい変化です。
まあでも決めたので頑張る。


最後に、今年も Advent Calendarを完走できたことに対して、記事を書いてくれたみんなと、みなを先導してくれた[masakuni-ito](https://qiita.com/masakuni-ito)に改めて感謝を。
ほんとうにありがとうございました。

みなさま良いお年を。
AWS: Partition Placement Groupsを試してみた

Partition Placement Groupsを試してみました。

# どんなもの？
EC2の耐障害性を高める機能です。別のpartitionに起動したEC2はハードウェアを共有しないので、ハード障害が発生した場合、影響は1個のpartitionに限定されます。

# 1. partition placement groupを作成する

現在(2018-12-25)はAPIとAWS CLIのみ対応しています。

```bash
aws ec2 create-placement-group --group-name test-partition-group --strategy partition --partition-count 3
```


# 2. EC2を作成する

以下を指定してEC2を作成します。

- インスタンス数＝３
- プレイスメントグループ名＝test-partition-group

<img width="731" alt="ec2.png" src="https://qiita-image-store.s3.amazonaws.com/0/192584/d9208c65-5064-e000-d942-d3d363af4abb.png">


# 3. partitionを確認する

作成した3台のEC2のpatitionを確認します。
それぞれ別のpartitionに作成されていることが分かります。

```bash
$ aws ec2 describe-instances --filter Name=placement-group-name,Values=test-partition-group | grep PartitionNumber
                        "PartitionNumber": 1,
                        "PartitionNumber": 3,
                        "PartitionNumber": 2,
```
vue-thin-modalでモーダルを開いた状態での画面遷移に対応する

# モーダルを開いた状態で画面遷移をすると予期せぬ動作をすることがある

ktsnさんが開発した[vue-thin-modal](https://github.com/ktsn/vue-thin-modal)はとても便利なUIライブラリですが
モーダルを表示した状態で画面遷移すると予期せぬ動作をすることがあります。
vue-thin-modalを使う場合、レイアウトファイルに`modal-portal`コンポーネントを配置し、モーダルを表示するページのコンポーネントに`modal`コンポーネントを置くことになると思います。Nuxt.jsの場合だと以下のような感じになると思います。

* レイアウト

```layout.vue
<template>
  <main>
    <nuxt />
    <modal-portal />
  </main>
</template>
<script>
// ...
</script>
<style>
/* ... */
</style>
```

* ページコンポーネント

```page.vue
<template>
  <div>
    <button @click="$modal.push('someModal')">Open modal</button>
    <modal name="someModal">
      open!
    </modal>
  </div>
</template>
<script>
// ...
</script>
<style>
/* ... */
</style>

```

ページコンポーネントのボタンをクリックすると、`$modal.push('someModal')`が呼ばれてvue-thin-modalの内部にスタックされ
レイアウト側のmodal-portalコンポーネントにmodalコンポーネントのコンテンツが表示されます。
ここでモーダルを開いたままブラウザバックなどで画面遷移をすると、ページコンポーネントと共にモーダルのコンテンツは破棄されます。しかし、vue-thin-modalは画面遷移を知らないので、内部にはスタックが残ったままになってしまいます。この結果、モーダルのコンテンツが無いのにも関わらず、モーダル表示中のCSSクラスがHTMLに残ってしまい、最悪の場合アプリケーションが操作不能になります。

# とりあえず画面遷移が発生した場合には全てのモーダルを閉じる

画面遷移時の予期せぬ動作を防ぐには、モーダルのコンテンツが破棄される時に`$modal.pop()`を明示的に呼んであげればよいです。
ページ間でモーダルを兼用することは滅多にないと思うので、画面遷移時に表示中の全モーダルを破棄するようにします。
Nuxt.jsのレイアウトファイルにモーダル破棄の処理を追加した例を紹介します。

```layout.vue
<script>
import { Component, Vue } from 'nuxt-property-decorator'

@Component
export default class DefaultLayout extends Vue {
  unregisterBeforeEach = () => {}

  created() {
    // MEMO: beforeEachは登録解除の関数を返す実装になっている
    // https://github.com/vuejs/vue-router/blob/ea8cb474f869a5a12a095fcb5989c45c68971d14/src/index.js#L121-L123
    // https://github.com/vuejs/vue-router/blob/ea8cb474f869a5a12a095fcb5989c45c68971d14/src/index.js#L217-L223
    this.unregisterBeforeEach = this.$router.beforeEach((_, __, next) => {
      popAllModals(this.$modal)
      next()
    })
  }

  beforeDestroy() {
    this.unregisterBeforeEach()
  }
}

function popAllModals(modalMediator) {
  (function factorial() {
    if (modalMediator.currentName) {
      modalMediator.pop()
      factorial()
    }
  })()
}

</script>
```

各画面の遷移時にモーダル削除の処理を実行したいので`$router.beforeEach`フックから`popAllModals`を実行します。`$router.beforeEach`の戻り値はフック解除用の関数なので、これを状態として保持しておきます。レイアウトファイルが破棄されるときには先に保存したフック解除用の関数を実行し、beforeEachのフックも解除します。
`popAllModals`では、`$modal.currentName`が`undefined`になるまでモーダルを削除します。
これらの処理により、画面遷移時に全てのモーダルを閉じることが可能になりました。

# まとめ

vue-thin-modalでモーダルを開いた状態で画面遷移をしても、予期した動作をするようになりました。
この記事ではNuxt.jsを例に用いていますが、おそらく通常のVueアプリケーションでも使えると思います。
SpriteAtlasをScriptableObjectにして使うアイデア

この記事は思いつきを書いたものです。

普段TexturePackerなどでPackしたSpriteAtlasを読み込む時どうしてますか？

Resources.LoadAllだったりAssetBundle.LoadAllAssetsだったりで読んできて
Sprite[]から探して読んだりしていませんか？
そこで、今回思いついたのがSpriteをすべてScriptableObjectにして、
使う時はScriptableObjectを読み込むという方法です。


# 使用したアセット

今回Serialize可能なDictionaryであるSerializeDictionaryを利用しています。
https://github.com/neuecc/SerializableDictionary

こちらの絵素材を使用しています。
https://pipoya.net/sozai/terms-of-use-of-free-materials/

# ScriptableObjectの作成

```csharp

using System;
using System.Linq;
using SerializableCollections;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;

#endif

[Serializable]
public class StringSpriteDictionary : SerializableDictionary<string, Sprite>
{
}

[CustomPropertyDrawer(typeof(StringSpriteDictionary))]
public class ExtendedSerializableDictionaryPropertyDrawer : SerializableDictionaryPropertyDrawer
{
}

public class SpriteAtlasAsset : ScriptableObject
{
#if UNITY_EDITOR
    [MenuItem("Tools/CreateSpriteAtlasAsset")]
    static void CreateSpriteAtlasAssetInstance()
    {
        var spriteAtlas = CreateInstance<SpriteAtlasAsset>();
        spriteAtlas.atlasCache = new StringSpriteDictionary();
        foreach (var obj in Selection.objects)
        {
            var assetPath = AssetDatabase.GetAssetPath(obj);![SpriteAtlasInspector.PNG](https://qiita-image-store.s3.amazonaws.com/0/16088/daacc87f-3ef1-fb1c-7495-804062243688.png)

            var sprites = AssetDatabase.LoadAllAssetsAtPath(assetPath).OfType<Sprite>().ToArray();
            foreach (var sprite in sprites)
            {
                spriteAtlas.atlasCache.Add(sprite.name, sprite);
            }
        }

        AssetDatabase.CreateAsset(spriteAtlas, "Assets/SpriteAtlas.asset");
        AssetDatabase.Refresh();
    }
#endif

    [SerializeField] StringSpriteDictionary atlasCache = new StringSpriteDictionary();


    public Sprite GetSprite(string spriteName)
    {
        return atlasCache[spriteName];
    }
}
```

このコードを作成しmultipleなTextureを選択してToolsのCreateSpriteAtlasAssetを押すと
Assets直下にScriptableObjectが作成されます。
作られたSpriteAtlasのScriptableObjectのInspectorを見ると選択したTextureに含まれるSpriteが読まれます。
![SpriteAtlasInspector.PNG](https://qiita-image-store.s3.amazonaws.com/0/16088/068f3fb7-a109-2aef-f0df-268ea498ce60.png)




## 使用する時

```
using UnityEngine;
using UnityEngine.UI;

public class ScriptableTest : MonoBehaviour
{

    [SerializeField] SpriteAtlasAsset spriteAtlas;
    [SerializeField] Image image;

    void Start()
    {
        image.sprite = spriteAtlas.GetSprite("majo_1");
    }
}
```

上記のようなMonobehaviorを継承したクラスを作り、Inspectorで先程作成したScriptableObjectを貼ります。
実行後して確認してみると以下のようになりました。

![game.PNG](https://qiita-image-store.s3.amazonaws.com/0/16088/594ac757-123b-82ba-8222-ce7cbfe3e440.png)


## 結果

ScriptableObjectを作る手間は増えるが、ScriptableObjectをInspectorで直接設定可能で
はじめからDictionaryで保持することが可能だったりと便利になったのではないでしょうか？
プロジェクトマネージャーとして大切にしていきたいこと

はじめまして、ディップで開発マネジメントを担当している[e-takazawa](https://qiita.com/e-takazawa)です。

普段は弊社アプリのiOS/Androidアプリのプロジェクトマネージャーをしながら、たまに[Bitriseおじさん](https://qiita.com/e-takazawa/items/5e64e3ed19f8fd3a19b9)をやっています。

今回の記事では、今までのマネジメント経験を振り返ってこれからもこれは大事にしていきたい！と思うことをアウトプットしていこうかなと思います。

# 簡単に今までやってきたこと
書く前に今まで自分が何をやってきたかを簡単に振り返ってみます。
文系大学を卒業した後、2006〜2013年の間に新卒で入社した会社で、業務系Javaアプリや自社のAndroidアプリ開発、CakePHPでECサイト開発をしていました。

ディップには2014年からJoinし、最初の一年はAndroidエンジニアとして自社アプリの開発をしていましたが、2015年くらいからチームリーダーを任されることになりました。
そこからはいくつかの大小様々な開発プロジェクトを乗り切り、今はiOS/Androidアプリのプロジェクトマネージャーをしています。
大切なことはそれまでの経験の中で見えてきたことを書いていきたいと思います。

# 大切にしていきたいこと
ここからは本題ですが、マネージャーの仕事は多岐にわたるので、大切なことは山ほどたくさんあり、なかなか書くことも多く文書化しづらいと思っています。（それこそ一冊本が書けるくらいあると思います。）
今回は私が特に大事だと思っている以下の事柄について書いていこうと思います。

【体制を作る】
【チャレンジできる環境】
【日々のコミュニケーション】
【先を見る】
【諦めない】

## 体制を作る
社内プロジェクトを円滑に進めるためには、全員の理解レベルを一定以上にして動く必要があるのと思っているので、自社サービスに対して、いかにしてメンバ全員に理解してもらうかに重要です。

ですので、Joinしてもらった時のオリエンテーションで説明、説明した後も理解度を上げるためのフォロー、実際に作業をお願いした後もフォローをしていきます。
Join直後のメンバにはいいスタートダッシュをしてもらいたいので、特に慎重に、細かくやっていくこと必要だと思っています。

ただ、このようにマネージャーが全てのことを考えて動く環境にしてしまうと、必然的にメンバが待ちの姿勢になってしまうので、理解が進んだら「ある程度はお任せしながら、重要なところだけを押さえておく」ようにしてメンバ一人一人が自立して動いてもらうようにすることが重要だと思っています。

## チャレンジできる環境を作る
せっかくJoinしてくれたメンバも、お願いする作業を淡々とこなすだけだとすぐに飽きてしまうかなと思っています。
また、iOS/Androidアプリの開発は、OSの進化に合わせてどんどん新しいことに挑戦していかないと自社以外のサービスの成長にどんどんおいていかれる環境にあります。

と言った事情もあるので、新しいことにチャレンジできる空気感とそれを生み出すため動きが重要だと思っています。

具体的に言えば、プロジェクトマネージャー自らが新しいことへのチャレンジをする題材を作る（Kotlinの導入など）、メンバが「新しいライブラリを入れてみたい」と提案してくれたことを承認する、といった内容になります。

自社のサービスに対して新しい取り組みをする以上、導入には慎重になるべきですが、個人的には、「しっかりテストすれば問題ない」と思っています。

## 日々のコミュニケーション
メンバの数が増えれば増えるほどコミュニケーションは重要です。
これは誰もがそう思うことだと思います。

日々の起きがちなコミュニケーション
特定のメンバの間だけで話して決めたことが別の誰かが知らなかったり、
人によって同じ言葉を言っているのにその言葉の意味やレベル感が人によってそれぞれ違ったり、

コミュニケーションあるあるだと思いますが、このような問題は日々のコミュニケーションの中で起きがちです。

こういった問題は放置していると積み上げて負債として溜まっていき、あとあと取り返しがつかなくなることがあるので、日頃からコミュニケーションに対して気を配っておくのもマネージャーの役割かなと思っています。

基本的には、会話だけで決めた内容はそのままにしないで、Slackに流しておく、Issueとして起票しておく、など、コミュニケーションツールを積極的に活用していったり、メンバ間で問題が起きそうになったら会話して妥協点を探っていくなど、ツールだけではく生の会話をしていくように促すことが重要だと思います。

## 先を見る。
上司や先輩の受け売りですが、これが一番プロジェクトマネージャーにとって重要な事だと思っています。
メンバには今やるべきことをやっていますが、マネージャーは今やっているところから1、２ステップくらい先を見ながら行動するくらいがちょうどいいと思います。
1、２ステップくらい先を見るとそれに向けて準備しなきゃいけないタスクがたくさん出てくるので、それをメンバに適切に振り分けることが重要だと思います。

## 諦めない
プロジェクトを進めていると「これは無理！」っていう問題が起きると思います。
起きた時、メンバが諦めモードになることもあると思います。
そこでマネージャーが一緒になって「うん、これは無理だね。」って一緒に折れてはいけないです。

「いや、確かにこれは無理だけど、こうしたらできるんじゃない？」とか「これは確かに無理だね。別の方法考えようか。」とか、必ず前を進むための言葉を選んで発言することが必要だと思います。

# 最後に
と、ここまで大切にしていきたいことを書いてきましたが、全てのことが今の自分にできているかと言われるとできていないことが多いなぁと思っています。（自分に言い聞かせるつもりで書いていました。）
まだまだ至らない点もありますが、これからもやっていき！という気持ちで今年の残り少ない日と来年も過ごしていこうかと思います。

ありがとうございました。
Caffeモデルの転移学習をやってみる

# 1.転移学習
# 2.再学習用のデータの収集
![image.png](https://qiita-image-store.s3.amazonaws.com/0/327825/f6d721a3-16fe-2480-6eba-8d61c72cfbf5.png)


ラベルには人工知能がラベル付けしたものを使います。人間によってラベリングされたデータは、許容度が大きく、「サングラスをかけた猫」「アニメの猫」も猫としてラベリングされているので、fine tuningは適さないのでやめておきましょう。


```
wget https://storage.googleapis.com/openimages/2017_07/images_2017_07.tar.gz
tar -zxvf images_2017_07.tar.gz
```

```
wget https://storage.googleapis.com/openimages/2017_07/annotations_machine_2017_07.tar.gz
tar -zxvf annotations_machine_2017_07.tar.gz
```



![image.png](https://qiita-image-store.s3.amazonaws.com/0/169795/5ae3ca92-f435-b201-6e69-81602c2f76e9.png)


```
import csv
import os
import time
from PIL import Image
import urllib
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

all_tgt = ["cat", "dog", "person", "yard"]

for cur_tgt in all_tgt:
    print '\nClass : ' + str(cur_tgt) + '\n\n'
    f1 = open('/home/ubuntu/labels_'+str(cur_tgt)+'.csv', 'r')
    reader1 = csv.reader(f1)
    f2 = open('/home/ubuntu/2017_07/train/images.csv', 'r')
    reader2 = csv.reader(f2)
    confidence = 1.0
    if cur_tgt == 'yard':
        confidence = 0.9

    cnt = 0
    for row1 in reader1:

        if float(row1[3]) == confidence:

            for row2 in reader2:
                if row1[0] == row2[0]:
                    break
            print str(cnt)+' : '+row2[2]

            #load image
            try:
                urlimg = urllib.urlopen(row2[2])            
                img = Image.open(urlimg)
            except:
                continue # skip if error happened

            fname = '/home/ubuntu/downloaded_images/'+str(cur_tgt)+'.'+str(cnt)+'.jpg'
            if urlimg.url.find('unavailable') == -1:
                if cur_tgt != 'yard':
                    #resize image
                    ratio = img.width / 227.0
                    if img.height < img.width:
                        ratio = img.height / 227.0
                    img_resize = img.resize((int(round(img.width/ratio)), int(round(img.height/ratio))))
                    img_resize.save(fname)
                else:
                    img.save(fname)

                cnt = cnt + 1
                if 10000 <= cnt:
                    break
    f1.close()
    f2.close()
```
images.csvの中身は以下のようになっています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/327825/a38c48b4-bf59-b123-198f-a7d4f63810d7.png)


annotations-machine.csvには、各行に画像IDとそれに対応する画像のラベルがあります。の中身はこんな形になっています。Open Images Datasetの中で猫に相当するID(/m/01yrx)の画像IDとラベル名がありますね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/327825/56c13237-eb57-c2e3-e1e3-482c1d85a55a.png)

そして900万枚という画像の中から、以下のコマンドで、猫に関する画像だけを
抽出しましょう。
/home/ubuntu/labels_cat.csv


```
0000de486dc6c49f,machine,/m/01yrx,0.9
00056e547b7c00d6,machine,/m/01yrx,1.0
00067fe83e3e21c8,machine,/m/01yrx,1.0
0008ab3d8674f6ca,machine,/m/01yrx,1.0
000aa552a9d80891,machine,/m/01yrx,1.0
000c01d8269e1554,machine,/m/01yrx,0.7
000c24660cd3fd95,machine,/m/01yrx,0.7
00129ccb99612727,machine,/m/01yrx,1.0

```

結果、/home/ubuntu/に
labals_cat.csv
labals_dog.csv
labals_person.csv
labals_yard.csv
という今回fine tuningしたい自分のカテゴリ群にあった学習データ収集用の
メタデータファイルが生成されます。
Caffeモデルの転移学習をやってみる

ラベルには人工知能がラベル付けしたものを使います。人間によってラベリングされたデータは、許容度が大きく、「サングラスをかけた猫」「アニメの猫」も猫としてラベリングされているので、fine tuningは適さないのでやめておきましょう。


```
wget https://storage.googleapis.com/openimages/2017_07/images_2017_07.tar.gz
tar -zxvf images_2017_07.tar.gz
```

```
wget https://storage.googleapis.com/openimages/2017_07/annotations_machine_2017_07.tar.gz
tar -zxvf annotations_machine_2017_07.tar.gz
```



![image.png](https://qiita-image-store.s3.amazonaws.com/0/169795/5ae3ca92-f435-b201-6e69-81602c2f76e9.png)


```
import csv
import os
import time
from PIL import Image
import urllib
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

all_tgt = ["cat", "dog", "person", "yard"]

for cur_tgt in all_tgt:
    print '\nClass : ' + str(cur_tgt) + '\n\n'
    f1 = open('/home/ubuntu/labels_'+str(cur_tgt)+'.csv', 'r')
    reader1 = csv.reader(f1)
    f2 = open('/home/ubuntu/2017_07/train/images.csv', 'r')
    reader2 = csv.reader(f2)
    confidence = 1.0
    if cur_tgt == 'yard':
        confidence = 0.9

    cnt = 0
    for row1 in reader1:

        if float(row1[3]) == confidence:

            for row2 in reader2:
                if row1[0] == row2[0]:
                    break
            print str(cnt)+' : '+row2[2]

            #load image
            try:
                urlimg = urllib.urlopen(row2[2])            
                img = Image.open(urlimg)
            except:
                continue # skip if error happened

            fname = '/home/ubuntu/downloaded_images/'+str(cur_tgt)+'.'+str(cnt)+'.jpg'
            if urlimg.url.find('unavailable') == -1:
                if cur_tgt != 'yard':
                    #resize image
                    ratio = img.width / 227.0
                    if img.height < img.width:
                        ratio = img.height / 227.0
                    img_resize = img.resize((int(round(img.width/ratio)), int(round(img.height/ratio))))
                    img_resize.save(fname)
                else:
                    img.save(fname)

                cnt = cnt + 1
                if 10000 <= cnt:
                    break
    f1.close()
    f2.close()
```
一人暮らしの社会人1年目を平和に生き抜くためのAlexa×IFTTT連携（スマホアプリから簡単に）

# はじめに
メリークリスマス！
この記事は、[スマートスピーカーIFTTT 大喜利 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ssifttt) の最終日です。
今回は初めてのAlexa×IFTTT連携ということで、まずは社会人1年目のわたし自身が使ってみたいサービス（アプレット）を作成してみました。

# 一人暮らしの社会人1年目に直面したリスクとその解決アプレット
入社後の研修にも慣れてきた頃。。無意識にアラームを止めてしまったのか、起きたのが家を出る5分前という絶望の起床を経験しました。その後も何度か二度寝をしてしまい青ざめながら起床することがあり、その解決策として朝アラームを止めると母親に通知が行くアプレットを作成しました。これで時間になっても通知が来ない場合、大量のLINEや電話で起こしてもらうことができます。。！（社会人としてあるまじき甘えではありますが、。）

## IFTTTのConfigure
![MicrosoftTeams-image (8).png](https://qiita-image-store.s3.amazonaws.com/0/133223/d4351e97-06f2-35d0-6191-15022a1f4658.png)
![MicrosoftTeams-image (9).png](https://qiita-image-store.s3.amazonaws.com/0/133223/a15e22df-81f8-3ee6-a308-95c7217b7038.png)


## 準備するもの
- Amazon echo（使える状態に設定済み）
- スマートフォン
    - 今回使用したのはiphonex
- LINEのアプリ（会員登録済み）
- IFTTTのアプリ（会員登録済み）
- LINEとIFTTTの連携
    - [使いこなせばとっても便利！LINE Notifyの使い方＋IFTTTの基本操作](https://ferret-plus.com/5355) を参照

## アプレット作成手順
1. LINEアプリを開き、起床通知を送る先のLINEグループを作成する
※この時、グループにLINE Notifyを必ず招待します。LINE Notifyがいないとトリガーが発火しても通知されません
![MicrosoftTeams-image (1).png](https://qiita-image-store.s3.amazonaws.com/0/133223/012bbb27-5682-ac12-61bd-aa103f53102b.png)

2. IFTTTアプリを開き、アプレットを作成する
    1. アプリを開いたら「My Applets」のタブを選択し、右上の「＋」ボタンから新規アプレットを作成する
    ![MicrosoftTeams-image (10).png](https://qiita-image-store.s3.amazonaws.com/0/133223/8af2ceee-e264-47c9-4b96-c26e8512af21.png)

    2. 「＋this」をタップしてアプレットで使用するサービス・アプレットの発動トリガーを決める。「Amazon Alexa」を選択する
    ![MicrosoftTeams-image (11).png](https://qiita-image-store.s3.amazonaws.com/0/133223/0e8f7989-f5de-ec10-a7e4-752393477073.png)

    3. アラームを止めた時をトリガーとしたいため、「Your Alarm goes off」を選択する
    ![MicrosoftTeams-image (12).png](https://qiita-image-store.s3.amazonaws.com/0/133223/2b884bd2-ff3e-8586-328d-117a78a2b128.png)

    4. 「＋that」をタップしてアプレット出力先のサービス「LINE」を選択する
    ![MicrosoftTeams-image (13).png](https://qiita-image-store.s3.amazonaws.com/0/133223/ef97ae71-ea39-5338-6c4a-fa31c19b9528.png)

    5. 「send message」をタップし、通知の送信先グループ・通知の内容・一緒に送る画像のURLを入力する
    ![MicrosoftTeams-image (4).png](https://qiita-image-store.s3.amazonaws.com/0/133223/dcb61d69-1174-6672-cc11-9e23c0f829ac.png)

    6. 「Create action」をタップして作成完了！

3. Amazon echoでAlexaにアラームの設定をお願いする
「Alexa、毎朝7時にアラームをかけて」
->毎朝午前7時に、アラームを設定しました

4. 設定通りアラームが鳴り「Alexa、ストップ」でアラームを止めるとLINE通知が届く
![図1.png](https://qiita-image-store.s3.amazonaws.com/0/133223/5cbb6de2-2961-3747-4720-3c0f7f9a5fd6.png)


これで通知までが完了です！
ただこの通知だけでは「起床していない」ことに母親自身が気づく必要があるため、寝坊のリスクを回避するには不十分です。
起床時間になってもアラームが止められていないときにのみ通知を送る、起床時間になっても通知が来ない場合は起床するまで10分起きに通知を送る等が必要ですね。

# まとめ
今回は「寝坊」というリスクに対するアプレットを作成してみました。
実は最初、「朝の起床を通知する」アプレットを作るにあたり「Alexa、トリガー、おはよう」×メール通知を選択していました。ただ、朝テンションが低く頭が回っていない時に「Alexa、トリガー、おはよう」を思い出すのが難しそうでわざわざ声に出すのも面倒くさいことから「アラームを止めたとき」のトリガーに変更しました。アプレットの目的を達成することだけでなく、様々な組み合わせからどれがユーザー体験的にベストなのかについても検証し甲斐がありそうだと感じました。

他にも体調不良の時に「Alexa、トリガー、しんどい」でLINE通知にて母親に体調不良を伝えて心配してもらいつつ、ポカリやお粥を送ってもらう、「Alexa、トリガー、今日は休む」で事前にセットしたお休み連絡のメールが職場の人に届くなど様々なシーンに使えそうです。
サーバで起動したJupyter Labをローカルで使用する

# SSHのポートフォワーディング
簡単に言うと、サーバ側で開いている（使用している）ポートをローカルで開いているものとして扱うことができる技

## 例


### ローカル側

- sshで接続
    - -Lオプションを使用

```bash
$ ssh -i 鍵 ユーザ名@IPアドレス -L 8889:localhost:8890
```

- このコマンドでローカルの8889番ポートがサーバ側の8890番ポートに扮する。

### サーバ側
- 下記コマンドでJupyter Labを8890コマンドで起動 

```bash
$ jupyter lab --port 8890
```


### ローカル側
- ブラウザで localhost:8889 で起動
    - サーバ側のJupyter Labが表示

# 以上
- -Lオプションのどっちがローカル側でどっちがサーバ側か考えるのがめんどくさかったら下記コマンドでオッケー

```bash
ローカル側　↓
$ ssh -i 鍵 ユーザ名@IPアドレス -L 8889:localhost:8889
サーバ側　↓
$ jupyter lab --port 8889
```
Homebrewのインストールから操作まで

お疲れ様です。Homebrew環境構築の項です。

#ゴール
Homebrewを使えるようになる。

#前提
- Mac OS Mojave 10.14.2
- 入門者として, DockerでなくHomebrewを使って行く
- 簡単なコマンドライン操作が出来る
    - Progateの [Command Line基礎編](https://prog-8.com/lessons/commandline/study/1) が出来れば十分

#やること
1. Homebrewとは
2. Command Line Developer Toolsインストール
3. Homebrewインストール
4. Homebrewセットアップ
5. Homebrew操作

##1. Homebrewとは
![homebrew-social-card.jpg](https://qiita-image-store.s3.amazonaws.com/0/288484/91121550-1ea9-75ee-cbb7-7c39f199e4d1.jpeg)
>Homebrew（ホームブルー）は、macOSオペレーティングシステム上でソフトウェアの導入を単純化するパッケージ管理システムのひとつである。
(典拠：Wikipedia)

詳細資料 : [homebrewとは何者か。仕組みについて調べてみた](https://qiita.com/omega999/items/6f65217b81ad3fffe7e6)

##2. Command Line Developer Toolsインストール
- Homebrewのインストールに必要
- Xcodeを最初からインストールする必要はない
    - XcodeはAppleの統合開発環境(IDE)
    - 容量が大きいため, Apple製品のアプリ開発をしなければ不要

```terminal:terminal
$ xcode-select --install
```
実行後ダイアログ表示 -> [インストール]

##3. Homebrewインストール
- MacにはRubyが標準搭載されている
- HomebrewはRubyで書かれているので実行できる

```terminal:terminal
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

##4. Homebrewセットアップ
動作環境に異常がないか確認する。

```terminal:terminal
$ brew doctor
Your system is ready to brew.
```

警告が表示されたら指示に従って解消する
<details><summary>参考：私の場合は2つの警告が表示された</summary><div>

```terminal:Warning1
Warning: Unbrewed dylibs were found in /usr/local/lib.
If you didn't put them there on purpose they could cause problems when building Homebrew formulae, and may need to be deleted.

Unexpected dylibs:
  /usr/local/lib/libFB360-PluginShell-ambiX.dylib

$ rm /usr/local/lib/libFB360-PluginShell-ambiX.dylib

# ダイナミックライブラリを削除して解決
```

```terminal:Warning2
Warning: Broken symlinks were found. Remove them with `brew prune`:
  /usr/local/share/man/man3/*

$ brew prune /usr/local/share/man/man3/*
Pruned 1 symbolic links and 3 directories from /usr/local

# デッドシンボリックリンクを削除して解決
```
</div></details>

##5. Homebrew操作
バージョンを確認する。

```terminal:terminal
$ brew --version
Homebrew 1.8.6-36-gf050e42
Homebrew/homebrew-core (git revision 167c; last commit 2018-12-23)
```

Homebrewを更新する。

```terminal:terminal
$ brew update
Updated 1 tap (homebrew/core).
==> Updated Formulae ...
```

Homebrewが管理するパッケージを更新する。

```terminal:terminal
$ brew upgrade            # パッケージ全部
$ brew upgrade パッケージ名 # パッケージ限定
```

古いバージョンを削除する。

```terminal:terminal
$ brew cleanup
Removing: /Users/ユーザ名/Library/Caches/Homebrew/... (18.7KB)
==> This operation has freed approximately 18.7KB of disk space.
```

パッケージの一覧を表示する。

```terminal:terminal
$ brew list
autoconf	pkg-config	  openssl ...
```

情報を表示する。

```terminal:terminal
$ brew info            # Homebrewの情報
$ brew info パッケージ名 # パッケージの情報
```

お疲れ様でした。
この項・完
DockerからDocker-Composeへ：快適なプログラミング環境を目指して

#dockerは便利なのですが、コマンドをいちいちコピペするのが徐々に面倒くなり...
当初からdocker-composeを使おうとしていたのですが、dockerコマンドとの対応がわからず後回しにしていました...    
しかし、コンテナを落としてしまった後に同じ環境に戻して起動するのが面倒くさくなってきたので、使用することにしました( ﾟДﾟ)（Dockerfileを使えばいいだけですが...）    
docker-composeを使用することで複数のコンテナを管理することが容易になります。（kubernetesも使ってみたいですね）  
とりあえず試しに環境構築しました。ディレクトリの構成は以下の通りです。（pythonのanacondaしか使っていないので1つしかありませんが...）

```
docker
│  docker-compose.yml
│
└─build
    └─app
        └─anaconda3
                Dockerfile
```

自分用の『Dockerfileとdocker-compose.yml』を一度作ってしまえば、基本コマンドをうつだけでコンテナが起動したので、ちょっと感動しました(*‘ω‘ *)   
anacondaでjupyter notebookを使う環境作るためのDockerfileと、起動用のdocker-compose.ymlを作成してみました。内容は以下の通りです。

```yaml

docker-compose.yml
---------------------------------------------------
version: '2'
services:
  anaconda3:
    build: ./build/app/anaconda3
    tty: true
    ports:
     - "8888:8888"
    volumes:
     - 自分作業のディレクトリ:/opt/notebooks
```
```dockerfile

Dockerfile
---------------------------------------------------

FROM continuumio/anaconda3

RUN apt update -y && \
    apt install -y --no-install-recommends \
    git && \
    rm -rf /var/lib/apt/list/* /var/cache/apt/archives/*

RUN mkdir /opt/notebooks
RUN conda install -c conda-forge selenium
RUN conda install -c conda-forge phantomjs
RUN conda install -c clinicalgraphics chromedriver
CMD bash -c "conda install jupyter -y --quiet && jupyter notebook --notebook-dir=/opt/notebooks --ip='*' --no-browser --allow-root"``
```

ファイルを作り終わったら以下のコマンドを打てばコンテナが立ち上がります。
まず、イメージファイルを作ります。

```bash

docker-compose build
```

次に、イメージからコンテナを起動します。

```bash

docker-compose up
```

コンテナを止めたければ、以下のコマンドですべてのコンテナが停止します。

```bash

docker-compose down
```

以上、自分用のメモとしてまとめました。今度はkubernetesも使ってみたいですね(*'ω'*)
Azure Custom Vision を使って「ハムスターが起きているか？」を判定してみた


* [NGK2018B 昼の部（LT大会） - connpass](https://ngk2018b.connpass.com/event/104965/)

で発表した内容のフォローアップです。
５分厳守なガチLTは久しぶりだったので、タイムキープには特に注意しました。1分余らせるつもりがちょうど良い感じでしたが、ちょっとテンパってしまいましたね。

LT大会自体も、多種多様なネタを聞くことができてとっても参考になりましたね。
最近は特定のテクノロジー向けの勉強会・イベントしか参加してなかったので思わぬ発見ができてよかったです。

当日の録画が

* [NGK2018B オープニング & LT #1 - YouTube](https://www.youtube.com/watch?v=eAlJ-FK-2tw&list=PLscGe0lpp6WY0E0VA_aFgPyeCn2GuEXor)

にあるので、年末年始のテレビがつまらないと思う人はどうぞ。たぶんそれよりもずっと面白いです。

さて自分の発表は、

* [ハムスター検出器を1日で作ってみた #NGK2018B - Speaker Deck](https://speakerdeck.com/amay077/hamusutajian-chu-qi-wo1ri-dezuo-tutemita-number-ngk2018b)

だったわけですが、かいつまんで言うと、

* ハムスター飼っている
* ケージの様子をライブ配信している
* ハムスターが起きているか寝ているかを判定したい
* Azure Custom Vision でやってみた

というものです。普段ウケを狙う事はないのですが、笑っていただけたので良かったです。

## 投入したデータについて

約2000枚の画像にタグ付けをしたわけですが、「ハムスターはほとんど寝ている」ので、必然的に「寝ている画像」の方が多くなります。

それをそのまま Custom Vision にぶっこんだら、Train の結果で Warning が出ました。
内容は「タグ毎のデータ量に偏りがあるから是正したほうが良いよ(意訳)」というものでした。

その後機械学習のデータ準備について少し学んだら同じ情報が得られたので、Custom Vision は、その辺りのアドバイスもしてくれるのだなあ、親切！と感じました。

## Performance について

スライドにもあるように、タグ付けしたデータの一部を「検証用データ」としてとっておいて、学習後の Custom Vision に与えて Validation しようと思っていたのですが、 Performance というタブになんだか評価結果が出ているようでした。

各タグについて Precision と Recall のパーセンテージが出ており、それぞれ

* Precision - 「起きてる」とタグ付けされた画像が、「起きてる」と判定された確率
* Recall - 「起きてる」と判定された画像の内、「起きてる」とタグ付けされた画像の確率

であると、こちらも学習して理解しました。
そして、Custom Vision がこの数値を出しているということは、Custom Vision の中で検証のプロセスも行っていると判断しました。

## Free(Trial)版について

無償の Free 版では、以下の [制約](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/custom-vision-service/) があります。

* 2 Project まで
* アップロード画像 5000 画像まで

複数のデータセットを用意して、同時に評価したい場合は 2 project だと足りません。
その場合は Azure の有償サブスクリプションに紐づけて利用することになります。

今回は Standard(S0) という有償プランを使いましたが、数日利用していて現在までの利用料は「247円」でした。
アップロード、学習、判定の各トランザクションで課金され、額は $1 per 1,000 transactions とのことなので、判定APIが大量に利用されるようなサービスでは要注意になるのでしょう。

## 判定結果の所感

今回は、配信PCのスクリーンショットを教師データとして使用しました。これはアプリウィンドウのフレームやカメラ映像以外の部分も含まれたものですが、加工が面倒だったのでこのまま使用しました。
その状態でも Precision:94%, Recall:69% というのは実用に耐えられる数値ではないかと思います。

ポイントとなる箇所のみに画像をトリミングしたところ、１００% に近い数値になったので、「簡単な加工をするだけでかなりの精度向上が見込める」ことも分かりました。

趣味やお仕事で使うことも増えそうな、よいサービスだなと改めて思いました。
LSL（Linden Script Language）のコーディング規約のまとめ

#まえがき
LSLにはPythonの[PEP8](https://www.python.org/dev/peps/pep-0008/)のような明確な規約がなく、作者によってコーディングの規則が違います。
そこで、各自の規則を考える際の手がかりになるように、慣例的に用いられているコーディング規約についてまとめました。
重要なことは可読性と一貫性を保つことです。

##インデント
インデントは**半角スペース4文字**
もしくはそれと同じ幅のTAB文字

##名前の表記法
命名には以下の三つのタイプがよく用いられます。

+ スネークケース (snake case)
+ ローワーキャメルケース (lower camel case)
+ アッパーキャメルケース (upper camel case)

###スネークケース (snake case)
単語を**`_`**(アンダースコア)で区切る。
例：`snake_case, example_name`
###ローワーキャメルケース (lower camel case)
最初の単語は小文字、それ以降の単語の先頭を大文字で区切る。
例：`lowerCamelCase, exampleName`
###アッパーキャメルケース (upper camel case)
単語の先頭を全て大文字で区切る。
例：`UpperCamelCase, ExampleName`

より詳しい説明は以下の記事などが参考になります。
[「変数/クラス名の命名規則に使われる記法の分類(Qiita)」](https://qiita.com/yimajo/items/a3fac0026c07ec538fc2)
[「キャメルケースで省略語をどう扱うか(Qiita)」](https://qiita.com/gamako/items/a5b1428c9b5c8d6f0694)

#命名規則
##基本規則
###意味を読み取ることができる名前
+ 名称から役割、意味が読み取られる名前を付ける。
+ info, data, temp, str, bufなど曖昧・抽象的な名前は避ける。
+ 意味が分かる名前を付けるようにし、単語の省略はしない。
 + （ただし、十分にスコープの狭いローカル変数やループカウンタなどの場合、可読性を損なわない範囲で省略形が使われることがある。）

###名称の対称性
対照的な名称は正しい組み合わせで使用する。

| A | B |  | A | B |
|:-:|:-:|:-:|:-:|:-:|
|add|remove||put|get|
|insert|deflete||up|down|
|get|set||show|hide|
|start|stop||source|target|
|begin|end||open|close|
|send|receive||increment|decrement|
|first|last||old|new|
|get|release||next|previous|

##変数名
その機能を表すために必要十分な短いものとする。
__スネークケース__ か__ローワーキャメルケース__を用いる。

###グローバル変数
グローバル変数の先頭には、`g_`もしくは`g`をつけてもよい。
例：`g_owner_key, g_ownerKey, gOwnerKey`

###ローカル変数
グローバル変数と区別するために、関数内のローカル変数には、先頭に**`_`**(アンダースコア)をつけてもよい。
例：`_channel, _message`

###定数
すべて大文字にして**`_`**(アンダースコア)でつなぐ。
`SECONDS_PER_DAY, MAIN_MENU_CHANNEL`

###ループカウンター
スコープが狭いループカウンターに`i, j, k`という名前をこの順に使う。

##関数名
__ローワーキャメルケース__か__アッパーキャメルケース__を用いる。
変数がローワーキャメルケースなら、関数はアッパーキャメルケースを使うというように、変数と関数のスタイルをわけるのが望ましい。
先頭の単語は動詞にする。

###属性を取得する関数

```c
getObject()
findUser()
```

###属性を設定する関数

```c
setParameter()
```

###boolean値を返す関数
形容詞、is + 形容詞、can + 動詞、has + 過去分詞、三単現動詞、三単現動詞 + 名詞。

```c
isEnabled()
isEmpty()
containsKey()
```

##リンク
1. [Second Life Wiki > LSL Style Guide/ja](http://wiki.secondlife.com/wiki/LSL_Style_Guide/ja)
1. [Second Life Wiki > LSL Style Guide](http://wiki.secondlife.com/wiki/LSL_Style_Guide)
Cordova アプリで Google Apps Script で書いたプログラムを呼出してみた

# はじめに

Google Apps Script で書いたプログラムを Cordova アプリから Google アカウント認証して使用しようと思いました。
まず、Cordova アプリで Google API 呼出できるようにしました。
前回の記事　[Cordova アプリで Google API 呼出してみた]
(https://qiita.com/tinymouse/items/dcd03c7371e88e232ca9)

続いて、Google Apps Script で書いたプログラムを呼出できるようにしたいと思います。
この記事のサンプルを動かせるようにします。
[Google Apps Script Execution APIを試してみた。 | 初心者備忘録]
(https://www.ka-net.org/blog/?p=6326)

# Google Apps Script のプログラムを準備する

1. Google アカウントを作成する。既にあればそれを使用する
(https://www.google.com/accounts)

2. Apps Script プロジェクトを作成する
(https://script.google.com)

3. Google Apps Script でプログラムを作成する
4. 作成したプログラムを実行して`承認（許可）`しておく

5. スクリプトエディタ画面で`実行可能 API として導入`
6. `Developer Console プロジェクト`と関連付け

7. 関連付けられた Developer Console プロジェクトで `Google Apps Script Execution API` を有効にしておく

## Google Apps Script でプログラムを作成する

今回は指定したスプレッドシートのシート名一覧を取得するプログラムにします。

```javascript
function getSheetNames(sheetId) {
  var ss = SpreadsheetApp.openById(sheetId);
  var sheets = ss.getSheets();
  return sheets.map(function(sheet) {
    return sheet.getName();
  });
}
 
function myFunction() {
  Logger.log(getSheetNames("●●●●●●●●"));
}
```

上記の引数は実際の`シート ID`をセットします。

## 実行可能 API として導入する

ここで以下の項目を取得しておきます。

スクリプトエディタ画面で`実行可能 API として導入`

* `API ID`

スクリプトエディタ画面で`プロパティ`

* `このスクリプトで必要な OAuth スコープ`

## Developer Console プロジェクトと関連付ける

ここで以下の項目を設定しておきます。

スクリプトエディタ画面で`Developer Console プロジェクト`

* 関連付けられた Developer Console プロジェクト

Developer Console プロジェクトが関連付けられて作成されますが、既存のプロジェクトに変更することもできます。

## Developer Console プロジェクトで Google Apps Script Execution API を有効にしておく

これは忘れずに実行してかないといけません。

# Cordova アプリを作成して Google API 呼出できるようにする

以前に作成した Cordova プロジェクトを使います。

# Cordova アプリで Google Apps Script で書いたプログラムを呼出する

Google API を呼出するのと同じ要領で、Apps Script で書いたプログラムが呼出できます。

```index.js
var scope = 'https://www.googleapis.com/auth/spreadsheets';

$('#request').click(function(){
    if (!accessToken) {
        return;
    }
    // ③リクエストを送信
    $.ajax({
        type: 'post',
        url: 'https://script.googleapis.com/v1/scripts/●●●●●●●●●●:run',
        data: JSON.stringify({
            'function': 'getSheetNames',
            'parameters': '●●●●●●●●●●',
            'devMode': true
        }),
        contentType: 'application/json',
        dataType: 'json',
        beforeSend: function(xhr, settings){
            xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
        }
    })
    .done(function(data, status){
        console.log(status, data);
    })
    .fail(function(data, status){
        console.log(status, data);
    });
});
```

### メモ

ヘッダに `Authorization: Bearer [アクセストークン]` をセットしないといけないのは分かって、

>
$.post({
    headers: {
        'Authorization', 'Bearer ' + accessToken
    },

としていたが、エラーになる。`beforeSend` プロパティを使うのが分かるのに苦労しました。

`data` プロパティに直接オブジェクトをセットして

>
data: {
    'function': 'getSheetNames',
    'parameters': '●●●●●●●●●●',
    'devMode': true
},

としていたが、エラーになる。文字列にしないといけないのに分かるのに苦労しました。
心を入れ替えた俺がPythonで格子ボルツマン法の流体シミュレーションをできなかった話

# 格子ボルツマン法による流体の数値計算

## 格子ボルツマン法とは

水や空気などの流体の運動を数値的にシミュレーションするための方法の一つです。

流体の運動をシミュレーションする際に、よく使われるのが、流体の基礎方程式であるナヴィエ・ストークス方程式を数値計算するというものです。

それに対して、格子ボルツマン法では、もうちょっと「雑な」アプローチをとります。

まず空間を格子に分割して、水や空気の粒子は全て格子点の上を動きまわるものとします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/b6105be5-ae0d-7150-0c1f-7964a7e2db86.png)

これは2次元の場合の図になりますが、このとき粒子はその場に留まり続けるか、隣の格子に移動するかの9通りの運動しかできません。

雑ですね。

このような格子モデルをD2Q9モデル（2次元かつ9通りの運動が可能）といいます。3次元の流体の運動に対しては、また別の格子モデルがあります。

さて、格子点上にある粒子は、次の瞬間には（留まるものを除いて）隣の格子点に流れていってしまいます。しかし、同時に隣の格子点からも粒子が流れ込んできます。これを粒子の併進と呼んでやります。

こうして流れ込んできた粒子たちは「衝突」して、あるものは衝突によって運動のパターンを変えたり、あるものは変えなかったりします。

この「併進」と「衝突」を繰り返すことで、流体の運動が実現するというのが格子ボルツマン法の基本的な発想です。

## 格子BGK方程式

上記のような粒子の併進や衝突を以下のような格子BGK方程式で表現します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/645c3ee8-fcb8-8199-40d9-25b13e132bd1.png)

ここで $t$ は時間、 $i = 0 \dots 9$ でD2Q9の図に対応した粒子の運動方向、$f_i$ は $i$ 方向の粒子の数密度、$f_i^{eq}$ は $i$ 方向の局所並行分布関数をあらわすものとします。

$v$ は流体の流速、$\rho$ は流体の密度ですが、粒子の数密度を使って強引に

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/2705383b-d0c3-66ca-0d7e-5df07f2f6b30.png)

と表せるものと考えます。

格子BGK方程式では、衝突過程を局所平衡分布関数 $f_i^{eq}$ に緩和する過程であるとみなしています。
その局所平衡分布関数とは、簡単に言うと粒子の平均速度が $v$ で与えられるような「正規分布」なのですが、計算の都合上多項式で近似して

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/b8461a5a-4f76-674e-9b47-e6862a655eb3.png)

としています。ここで

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/44ce4aed-7e83-77f2-a0d2-410a7116ebd3.png)

ですが、 $F_1 = F_2 = F_3 = F_4, F_5 = F_6 = F_7 = F_8$ です。これはD2Q9モデルの場合の値です。また、この値は格子BGK方程式がナヴィエ・ストークス方程式を復元できるように計算された値でもあります。

## 境界条件の処理

他の数値計算方法と同じく、格子ボルツマン法でも境界において特別な処理が必要となります。

例えば、境界が壁であるケースです。壁の外には粒子は出ていかないし、また壁の外から粒子がやってくることもありません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/309074/f1d89507-5d30-d0bb-7b75-1c79b3cb5606.png)

どちらの図も粒子は「1→2→3→4」の順で運動しています。「2→3」の部分が境界条件の適用による粒子の運動です。

左の図は、単純に壁にぶつかって跳ね返るという「Bounce-back」の境界条件を表しています。物理的には壁の上で流体の速度が0であるという条件でもあります。

一方、右の図はそれとは逆に壁にぶつかった粒子が「滑る」境界条件をあらわしています。

## まとめ

以上のように

- 格子BGK方程式に基づく粒子の衝突
- 粒子の併進、あるいは境界条件の処理

を繰り返していくことで流体の運動を計算することができます。

もちろん「流体の運動を計算することができる」というためには、格子BGK方程式から「何らかの操作」をほどこすことで流体の基礎方程式であるナヴィエ・ストークス方程式を復元できることが必要です。

今回はそこまではやりません。

また併進過程において近接格子とは粒子（データ）のやりとりが発生するのを除くと、基本的にはそれぞれの格子点上で計算を進めることができます。numpyが縦横無尽に活躍しそうですね。GPUを用いた計算も簡単にできそうです。

くたばれFORTRAN！！

さてお待ちかね、Pythonのコードです↓↓↓↓

## Pythonコード

なんとここでタイムアップ！
書ききれませんでした〜
気が向いたら追記します :dogeza:

# おわりに
Pythonを使って格子ボルツマン法による流体シミュレーションをや~~ってみました~~ることができませんでした。

いかがでしたか？

# おわびに
すいませんでした。

アルゴリズムとしてはそこまで難しくはないため、何となく動くコードを書くことも難しくないと思います。

実用的なコードを書こうと思った場合は、ハードルが多少上がります。
格子のモデルや格子BGK方程式の詳細、境界条件の処理方法は各種の論文をざっと読んでトレンドを押さえておくというのが基本で、本はあまり買わない方が良いです。

論文を読むとはいっても、基本的なアルゴリズムはここで書いたものとそうかわらないため、書かれている内容を追いかけることはそれほど難しくありません。

例えば境界条件の処理一つを取ってみても、今回紹介した方法は小学生でもわかるくらいシンプルですが、その分シビアな計算をしようとするとそこから簡単に発散します。実用的なコードを書く場合は、古い論文ですが、最低限[この方法](https://arxiv.org/abs/comp-gas/9611001)で境界条件を処理するのがよいかと思います。

なおマイクロアドでも格子ボルツマン法は特に使っていません。

ではまた〜
半年間のスクラムマスター経験から得た体験談とアンチパターン集

この投稿は、アジャイル開発 Advent Calendar 2018の25日目です。
昨日は、@matsu0228さんの「ヨーロッパ最大のアジャイル・テスト自動化カンファレンス「AgileTestingDays2018」に参加してきた」でした。

この記事は、約半年間、スクラムマスターとして経験したアンチパターンの振り返りとそれに関連するスクラムの概念のまとめです。
記事下部にリンクしてあるLT資料からの抜粋 + α の内容なので併せてご覧ください。

# アンチパターン集

## リリースバーンダウンチャート
- これは正直判断が難しいですが、SCMでは、3ー4スプリント以上の未来予測はウォーターフォールになってしまうのでリリースバーンダウンは非推奨と言われました。一方で、組織としてはリリース予定日の決定と、それに向かっての経過報告は避けきれない部分もあったので、PBI(プロダクトバックログアイテム)を積んで、6スプリント先までの未来予測を行いました。結果、合計100ptのストーリーポイントが、リリース時には170ptまでにふくれあがっていましたが、チームの成長も相まってリリース日は守りきりました。
序盤の見積もりの精度は相対的に低めであることから、やはりあまり長期のPJにはリリースバーンダウンは非推奨だと感じます。
あまりに未来日のリリースポイントをに設定しすぎないような検討と併せて使うのならば効果的だと考えます。

## 人の入れかわりへの対応
スクラム開発は、「長期的に安定したメンバーによって構成される」という原則がありますが、実際にPJを進める中で以下の困難にぶつかりました。
1. バス因子になっていたメンバーの離脱
2. 新規メンバーの加入

人の減少は私がコントロールできるレベルを超えている事象でしたが、今後は「この人が抜けたら困る」という人数をいかに減らすかという視点を手に入れることができました。
また、私個人としても「失敗したなあ」と感じていた新規参入者のオンボーディングについて、PJ全体の振り返りの場で当人からキャッチアップするまでかなりしんどかったとのフィードバックをもらいました。もっとペアプロなどを活用して、自分がスクラムチームの一員だと感じてもらうまでのプロセスには気を配るべきだと反省しております。
一方で、少ない開発リソースしか持たない組織において、既存プロダクトの理解と進行中の新規機能開発のどちらに比重をおいて教育すべきか？というのは自分の中でも答えが出ていない部分です。コメント待ってます。

## チームの学習レベルにあわせた行動ができなかった
もっと早くElastic Leadershipを読んでおけばよかったなと思います。同著によれば、チームの学習レベルは以下の3フェーズに分けられます。
1. サバイバルフェーズ
- 仕事に追われている状態。自律的な判断というより統率者が必要。
2. 学習フェーズ
- チームに学習に使う時間がある状態。ティーチングよりもコーチングが必要。
3. 自己組織化フェーズ
- チームメンバーが自律的に意思決定ができる。ファシリテーターが必要。

今自分のチームがどのフェーズにいるのか？という問いと理解は戦略上必要だと思います。この概念を理解せずに、スクラムの「自己組織化されたチーム」という文言に囚われてしまったのは失敗でした。

## 学習という変数を考慮に入れておらず、モチベーションに負の影響を与えた
序盤のベロシティ低迷期に、学習の途上なので今は下がるのはしょうがないという考えでファシリテートできなかったのは勉強不足でした。
また、当初はスプリントゴールの設定をしていなかったため、スプリントプランニングで決定したストーリーの全消化そのものが目標になってしまい、チームで開発をしている一体感が出せませんでした。
スプリントゴールを決め、チームに対してこれを学習してもらうぞという戦略を実行し始めてから、スクラムがかなりうまく回るようになった気がします。
最終的には、平均ベロシティも10ptだったものが30pt程度まで上昇しました。
一方で、仮に学習をしないメンバーがいるとスクラム開発の前提が崩れます。そういう場合は、本人に改善を求め、それでもダメならチームから外れていただくというアプローチが必要になると思います。

# 振り返り
PJ終了後、チームとして振り返りを行いました。
参考：https://blog.lucidmeetings.com/blog/how-to-lead-a-successful-project-retrospective-meeting


# 何を得たか
リリースできた成果物があることもさることながら、この数ヶ月の経験から学習し、HRTの原則を理解しているメンバーがいることが財産なのだと思います。スクラム開発における「長期的に安定したメンバー」、「職能横断的な組織」、どちらもハードルは高いですが、学習意欲のあるメンバーが一つのPJをやりきったというのはチームの成長なのだと思います。

# 発表資料
この題材でLTした際に使用した発表資料です。
https://speakerdeck.com/mpls104/experienced-anti-pattern-as-a-scrum-master
Nefryとの出会いがIoTとの出会いだった

「機械の部品か？」2017年12月にもつ鍋をつつきながらわけも分からず受け取ったそれはNefryBTという名前があると知ったが一体これを何に使うのか、USBがついているところから何かに挿し込むことくらいしか法学部出身で営業と事務くらいしかまともな経歴のない私には皆目見当もつかなかった。IoTという言葉がInternet of Thingsの略だということはGoogleが教えてくれたので「ネット系の何か」と明らかにテストで正解にはならなそうな納得をさせる。12月のIoTLTに参加した際には話をほぼ聴いてないも同然くらいには理解ができず、あのままいつもの日常に戻っていればIoTにかかわることもなかっただろうし、Nefryがこんなに価値の感じるものだと思うこともなかっただろうがどちらが良かったのかは分からない。だが知らなければクリスマス前後の三連休にこんなにQiitaと向き合うこともなかったのだろうと思うとなんだか複雑な胸中だ。間に合ってないし。別に三連休前に書き上げておけば問題ないのではと思うかもしれないがそんなツッコミは野暮だ。話を戻して、Nefryをせっかく受け取ったこともあり年末年始に適当にググったりしていた（別段分かることはない）。そんな状態で2018年1月23日のNefryBT初心者ハンズオンがあると聞く。恐る恐る当日にNefryとPCと、明らかに何かをやらかすだろうという想像を持ってdotstudioのオフィスへ向かう。ダンボール仕様の玄関で立ち尽くしていると、スリッパを出してもらいながら第一声に「ArduinoIDEってインストールしてますか」と、え？あるでぃーの？？あるどぅいーの？？あるでゅいーの？？？「インストールしましょうか」完全に先方が察した。ハンズオンが始まり、やはり何を話しているかはよく分からないがとりあえずわみさんの言うことをきく。ヘルプの方も大変に優しく滞りは数えきれないほどあったが進むのは進んだ。おそらくIFTTT連携もできたしNefryクラウドとやらのアカウントも作成できたように思う。理屈は分かっていないができたはず。これがNefryBTを初めてきちんと動かせた日のことだが、理解していないわけだからそこまでの達成感はなくむしろ「やっとできた……」という徒労感が先に身体を走った。周りの人たちはより強い疲労があったかもしれない。
一年弱を宝の持ち腐れ状態にしていて家では「いつもごめんね・・」と見つめているNefryBTだが（キモい）ほかのボードを触った感じからしても自分には一番ちょうど良いのではないかなと思う。たとえば「見た目がとっつきやすくて、軽くて使いやすそう」なボードが一見良さそうに見えるが、割かしこういうボードは線のつなげ方がよくわからない。あと初期設定が難しいらしいとも聴く。Nefryは若干プロっぽい見た目をしているが（非ITから見たらむき出しボードは全部見た目がプロいんです）個人的には操作できるとその方が格好良いのではと憧れを抱いたりもするし、あとUSB給電が安心感。すごい安心。とりあえず挿せば良さそうそれだけじゃ良くないけど。まぁ大体のボードはむき出しで専門性が強そうな見た目をしているのだが、Groveとか穴としては見覚えあるしディスプレイつけられるし、これ以上小さいとむしろ失くしそうなんだ。すべてがたまたまのような気もするし、初めて出会ったそのボードに入れ込み過ぎのように思われるかもしれないが巡り合わせというのは得てしてそういうものだし、大体知人もわみさんの思想や展望込みで紹介してくれたようだったしそうとしたらば必然ではないか。サポートは他ボードに比べてもっとも分かりやすく、具体的で、リアルタイムかつ親身に寄り添っていただけている、と思う。「その画面は正常ですよ」の一言があるかないかは大きい。約一年近く前のストーリーから書いてみたが、特に進捗はなかったこともこうして書くだけで先の道が分かって不思議なものです。今後もNefryと付き合いながらIoTにおいて必要な技術を身に着けていきたいが、その目的達成によってついででもNefryとわみさんの知名度を上げることがプラスとして出来たら尚嬉しい。
【備忘録】ページの各読み込みタイミングにスクリプトを実行する方法まとめ【javascript】

参考元を読んだ時のメモ
参考（https://www.nishishi.com/javascript-tips/onload-page.html）

参考元は、様々なやり方を紹介しており、大変参考になる。
ただ、自分はHTMLソースはシンプルにしたい派なので
出来るだけscriptファイルで整う方法のみ抜粋。


## 前提
タイミングは下記３つを想定

1. webページが読み込まれる前(前)
2. webページのHTML要素読み込み完了時(直後)
3. webページの画像などオブジェクトの読み込み完了時（完了後）

参考元順に以下方法を記載

## webページの画像などオブジェクトの読み込み完了時（完了後）

```.javascript
window.onload = function() {
   // 実行したい処理
   alert('ページの読み込みが完了したよ！');
}
```

```.javascript
window.addEventListener("load", function() {
   // 実行したい処理
   alert('ページの読み込みが完了したよ！');
});
```

## webページのHTML要素読み込み完了時(直後)
DOMContentLoadedイベントは「ウェブページのHTMLを最後まで読んだ直後のタイミング」を示す。

```.javascript
const firstscript = function() {
   // 実行したい処理
};

window.addEventListener("DOMContentLoaded", firstscript);
```

同義だが一応

```.javascript
window.addEventListener("DOMContentLoaded", function() {
   // 実行したい処理
});
```


## webページが読み込まれる前(前)

多分使うことはないだろうけど一応メモ。
下記の解釈でいいのかな。

```.HTML
<head>
   <script type="text/javascript" src="firstscript.js"></script>
   　：　：　：　
</head>
```

```.javascript
// firstscript.js

function() {
   // 実行したい処理
});
```

## まとめ

テンプレにするとしたらこんな感じかな。
たまに見かけるリッチなサイトの、オープニングアニメーションとか作るなら知ってていい知識かも。

```.javascript
// HTML要素を読み込んだ後に実行
window.addEventListener("DOMContentLoaded", function() {
   // 実行したい処理
});

// 画像などのオブジェクトを全て読み込んだ後に実行
window.addEventListener("load", function() {
   // 実行したい処理
});
```

## 参考
https://www.nishishi.com/javascript-tips/onload-page.html
「このデータ、機密度高いから暗号化しないとね」から始まる苦行について

機密情報を取り扱うシステムに携わり、「余人はおろか保守運用担当者にすら容易には触れさせたくない」ような情報の秘匿について考える機会があった為、そこで得られた知見についてつらつらと書いていきます。


## 暗号化対象データの在処について - 通信路

クライアントにあたる端末からサーバまでの経路については改めて語るまでもなく、世の多くのシステムはHTTPSによって暗号化が図られていると思います。が、殊更に内部犯に対しても慎重を期して臨む場合は、アプリケーションサーバとデータベース間、SSL終端サーバとアプリケーションサーバ間の通信でさえ、人々が容易にアクセス可能なネットワークを流れないようにルーティングの検討が必要な場合があります。前者はDBMSのセキュア通信機能を利用する、後者はそもそもSSL終端サーバとアプリケーションサーバを1ホストに同居させてUNIXドメインソケットで通信する、等といった対策も考えられます。
クラウドサービス上に構築するシステムであれば、VPCとセキュリティグループ／ファイアウォールルール等で閉じ込める、といった方法が簡単・確実だと思います。

※ 意図的でもない限りまず発生しないとは思いますが、平文の通信がパブリックネットワークや外部ネットワーク上を流れないようにも注意しましょう。

## 暗号化対象データの在処について - ストレージ

データが永続化される場所に関しては、暗号化によって守るべき対象と手段は複数あります。

1. ハードウェアによるドライブまるごとの暗号化（自己暗号化ドライブ等）
1. 低レベルなソフトウェアによる全データの暗号化（Check Point Full Disk Encryption等）
1. データストアミドルウェアによる透過的暗号化（Oracle DBのTransparent Data Encryption等）
1. アプリケーションの実装による明示的な暗号化

1、2は稼働中のシステムにおいては誰にとっても透過的な措置であるため、アクセス可能＝解読可能であって、（物理ホストマシンからドライブごと抜き取っての盗難等の物理的な犯行以外には）ほぼ無意味です。最低でも3or4、乃至は3&4の併用が高度なセキュリティを要求されるシステムには必須になります。

さて、アプリケーションの実装による暗号化における、暗号化方式や鍵の管理運用といったメジャーな観点は余所に譲って、ここでは比較的日の当たらない、けど地味に痛し痒しなポイントについてもう少し深堀りします。

## 非透過的な暗号化を施されたデータは検索条件に出来ない

平文で「田中一郎」という氏名であったデータが暗号化されて「AAAXXXZZZ」となって記録された場合、当然ながら「氏名＝"田中一郎"」という検索条件では文字列が一致しないため検索にヒットしません。では「田中一郎」を再び暗号化した文字列を指定すればよいのかというと、昨今の主流な暗号化方式では暗号化の都度毎回異なる暗号文が生成されるため、「氏名＝"QQWWEWEEE"」等となってしまいやはり検索できません。かくなる上はデータベースからは全件持ってきてしまって1行1行復号！…は流石に富豪的発想が過ぎるので最終手段に取っておくとして、それよりは幾らかマシな性能が出る方法を考えてみます。
まず保存時には

1. 暗号化したい項目の平文を一定のルールで加工する
1. 加工後の値をハッシュ化する
1. ハッシュ値を一定のルールで加工し、元の項目とは別項目に保存する

具体的には、例えばこんな要領です。

1. 氏名の1文字目～3文字目を抜き出す　「田中一郎」→「田一」
1. ハッシュ化する　「田一」→「sfhkrkuy24hrifulhuiwh4igt」
1. ハッシュ値を文字列化し5文字目から10文字目を「氏名ハッシュ」なる項目に保存する　「sfhkrkuy24hrifulhuiwh4igt」→「kuy24」

こうして記録したデータは、次の手順で検索可能になります。

1. 検索条件として入力された文字列に上記1~3と同じ手順で加工を加える　「田中一郎」→「kuy24」
1. 加工した文字列でデータベース上を検索する　→目当ての「田中一郎」のレコードの他、偶々計算結果が「kuy24」となって被る複数のレコードが余分にヒットする。
1. 検索結果から本来検索したかった項目の値を取り出して復号し、検索条件として入力された元の値と一致するレコードのみに絞り込む。

※ このような平文の代替値（これもサロゲート値と呼ぶんでしょうか…）を用いた処理は、加工方法に応じて平文推測の難度と応答速度に影響を及ぼします。加工を手薄くすればするほど旧いECB方式のように代替値から平文を推測し易くなります。
※ 部分一致検索？~~諦めましょう。~~ こんなのがあるそうです:「 [三菱電機、「部分一致対応秘匿検索基盤ソフトウェア」を開発](https://japan.zdnet.com/article/35077468/)」

## 要暗号化！とジャッジしたからにはあらゆる場所で死守しなければならない

当たり前の事なのですが意外と忘れられがちだと思います。何の事かと言うと、ログです。
Webサーバのアクセスログやアプリケーションサーバのデバッグログ、監査対応の証跡用ログなど、普段ならデータ容量に割ける予算の限りリッチに出力しておきたいログ情報が一転して、「露出し過ぎ」に注意すべきなポイントになります。
ログ出力時にも律儀に手堅い可逆暗号化を仕掛けるという手も無くは無いですが、ログであれば対象項目値を暗号化ではなく「*****」のように一律マスクしてしまうという方法も使えます。但し当然ながらその状態では証跡としての能力を失いますので、例えば「操作した顧客の会員番号」のように証跡としては必要だがセンシティブな情報なので秘匿もしたい…というケースで困ることになります。この場合は外界でも意味を持つ「会員番号」のままではなく、「会員番号(暗号化済)を持つテーブル上の1レコードを特定可能なサロゲートキー」、即ちシステム内部でしか意味を為さない値の方をログに残すようにするという手段が考えられます。後から証跡として分析が必要になった時点でその措置に相応しい決裁と手順を経て分析する方法さえ提供しておけば目的は達する事が出来、かつ、シス管や保守運用担当が定常業務でログを覗く限りではその値から何かしら機密情報を得る事は不可能な状態を保つことが出来ます。

※ ログのログたる所以はその情報が"ある時点の断面"である事、でもあります。サロゲート値のみをログに残す場合、ログ日時とそのサロゲート値を使って当時の状態のデータを再現できる必要があります。

## まとめ

持たなくていい機密情報は持たないに限りますね。本当。(´・ω・`)