OmniAICreator/Qiita-1.07M huggingface dataset:
Eurekaで非同期処理で取得したデータでフォームを更新して画面に反映させる

# 方法
非同期処理のコールバック関数内に以下を書く。

```swift
self.form.allRows.forEach({$0.updateCell(); $0.reload()})
```
# 例

```swift
import Eureka

final class ExampleFormViewController: FormViewController {

    private var user: User?

    ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setView()
        setForm()
        fetchUser()
    }

    private func setView() {
        ...
    }

    private func setForm() {
        self.form
           +++ Section("User info")
           <<< TextRow() { row in
                row.title = "Name"
                row.placeholder = "hoge fuga"
            }.cellUpdate { [unowned self] cell, row in
                // フェッチしたuserのnameを代入
                row.value = self.user?.name
            }
            ...
    }

    private func fetchUser() {
        let successHandler: (User) -> Void = { user in
           self.user = user
           // フォームを更新・画面に反映
           self.form.allRows.forEach({$0.updateCell(); $0.reload()})
        }

        UserLogic.get(successHandler: successHandler, failureHandler: {_ in})
    }
}
```
## 説明
`$0.updateCell()`で`Row`の`cellUpdate`が呼ばれるので、ここで`row.value`を更新する。
`$0.reload()`で`Row`の変更を画面に反映させる。
NPOIでエクセルファイルを読み取り専用で開く

#NPOIについて
NPOIはOfficeのファイルを操作できるライブラリApache POI(Java)が.NETに移植されたものです。
https://qiita.com/hukatama024e/items/37427f2578a8987645dd

.NETからOfficeファイルを扱う、というとCOMの呼び出しを使ったものが定番ですが、こちらを使うとOfficeのプログラムを介さずして直接ファイルを取り扱えるようです。
API体系もVBA(≒COM呼び出し)とは違いますがシンプルなので、VBAにあまり馴染みがない、という人にとってはこちらのほうがとっつきやすいかもしれません。

#本題
エクセルワークブック(xlsx)を開くためには以下。

```OpenXLSX.cs
IWorkbook workbook = WorkbookFactory.Create(xlsxFilePath);
```

でもこれだと、ファイルの排他ロックを取りにいってしまいます。
エクセルファイルを開いて編集しながら、一方でそれを読み込んでプログラムで処理、
みたいなことをしようにも排他ロックなので、一度エクセルを閉じて実行する必要があります。

読み取り専用モードで開くことができれば、上記の問題は解消します。
ファンクションにReadOnlyModeのような引数はないし…と諦めかけていたら、
なんと、CreateメソッドにFileStreamオブジェクトを受け取るオーバーロードがあることに気づきました。

このFileStreamを読み取り専用にできれば、問題は解決するのではなかろうか。
ということでやってみたら、解決しました。
作成したのは以下のコードです。

```OpenXLSXReadOnlyMode.cs
FileStream inputStream = new FileStream(xlsxFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
IWorkbook workbook = WorkbookFactory.Create(inputStream);
```

これで、エクセルファイルを開いて編集状態で、
別プログラムからNPOIでデータを取得することができるようになりました。

#NPOIについての雑感
VBAに慣れ親しんでいない人にとっては有用かも、と書きましたが、逆にVBAが身についている人であれば、
今は素直にCOM呼び出しでコントロールしたほうがいいかもしれません。
というのも、NPOIはOfficeのファイル仕様である「Open XML」を操作するためのライブラリという印象で、
Officeの機能そのものを持っているわけではありません。

単純なデータのIN/OUTくらいならできるのですが、たとえば数式の評価をしようと思ったら、
ちょっと大変な実装が必要です。
NPOIはXMLをただ読んでいるだけなので、数式はただの文字列として拾ってきてしまいます。

さらに数式のエラー判定(#REFとかの判定)となるともうお手上げで、
結局そのあたりはCOMの力を借りることになりました。
COMはエクセルならエクセルそのものを操作できるAPIなので、エクセルでできること（≒VBAでできること）
は基本的にはすべてできます。
たとえばNPOIがこのあたりに対応してきたとしても、やはりエクセルそのものではないので、
エクセル側の仕様が変わることによって意図しない動作をする可能性もあります。

厳密にエクセル準拠の動きをさせたいならCOM、
サクッとファイルの入出力をしたいだけならNPOI、
といった使い分けができるかもしれません。

ただ今のところ私が知る限りNPOIでできることはすべてCOMでできるので（逆はない）、
どちらかを選択するなら今はCOMかなと思います。
Programming The Blockchain in C#をJSでやる

# Programming The Blockchain in C# とは#
ブロックチェーン（主にbitcoin）をC#で学べる、無料で読める本です。
クリエイティブ・コモンズにのっとってオープンに作成されています。
https://programmingblockchain.gitbooks.io/programmingblockchain-japanese/

個人的にC#を触ったことがない&ブロックチェーン開発はJSの方が主流なように見受けられるので、JSで最後まで進められるのか把握していませんが、今回はJSでやっていこうと思います。

# library #
bitpay社が提供しているbitcoreを用います。
https://github.com/bitpay/bitcore

```
npm install bitcore
```
ライブラリのAPI等はこちらに載っています。
https://bitcore.io/api/lib

#　ビットコインアドレス　#

```js

var bitcore = require('bitcore-lib');

var privateKey = new bitcore.PrivateKey(); // ランダムな秘密鍵を生成
var publicKey = privateKey.toPublicKey(); //秘密鍵から公開鍵を作成

console.log(publicKey); //<PublicKey: 028f1f9fdbe95b653b36652e8b4189ec05c328015cd08ef801c865426c4808418b>

console.log(publicKey.toAddress('mainnet')); //<Address: 17SzkBJ8qkn76a8eDMYH4vHtsUKgMG7xVH, type: pubkeyhash, network: livenet>
console.log(publicKey.toAddress('testnet')); //<Address: mmxx3EP7enDMsgcFvvWetqWDjTvPJitAWa, type: pubkeyhash, network: testnet>
```
ビットコインのアドレスはメインネットとテストネットでプレフィックスが異なるので、その様子が分かりますね。アドレスは、Base58Checkにエンコードされたものです。
https://en.bitcoin.it/wiki/List_of_address_prefixes


# 参考 #
https://github.com/bitpay/bitcore-lib/blob/master/docs/examples.md
【Androidアプリ開発勉強】1画面でAsyncTaskLoaderを複数呼びたい時。

いつぞやの勉強結果で[【Androidアプリ開発勉強】Observerパターンで非同期処理はAsyncTaskLoaderがメジャーらしい](https://qiita.com/FORTE/items/7fb016259bba32a992dd)と書きました。
あれから画面とDB周りの目処が付いたので、
いよいよローダーを組み込もうと思ったらハマりました。

# 【問題】ひとつのActivityで複数のローダーはどう呼べば良い?
コールバックを複数書けるわけもなく、
ひとつのActivityで複数のローダーはどう呼べばよいのだろうか?という所で詰まりました。

また、↑で書いた記事ではActivityでいきなり以下を書けとなっています。
`getSupportLoaderManager().initLoader(LOADER_ID, args, mCallback);`

が、当然getSupportLoaderManagerはActivityには無いメソッドなので、
継承するなりinterfaceなりが必要です。

で、結果的にinterfaceにLoaderManager?を指定すれば良さそうですが、
画面の表示に必要とはいえ、
ActivityのinterfaceにLoaderManagerを指定するのがなんとなく気持ち悪い。
(確かに使うけど画面のinterfaceじゃなくて良くない?みたいな)


# 対策
以下のサイトに書いてありました。多謝。

[今更ながら入門する AsyncTaskLoader](http://exception-think.hatenablog.com/entry/20160831/1472569200)


ただ、深夜にようやく見つけたのでまったく内容は理解出来ず…。
明日試してみて結果を書けたらいいなーという感じ。

見た感じまったく頭に入らない。


# 結論
1画面で1個だけローダーを投げるのであれば既出の内容でLoaderManagerをinterfaceに指定すれば良さそう。
1画面で複数のローダーを投げたい+作りが気持ち悪い場合は対策欄のサイトみて頑張る。
php-master-changes 2018-08-23

# 2018-08-23
## dstogov: Replace zend_class_entry->traits by persistent zend_class_entry->trait_names.
- https://github.com/php/php-src/commit/67397970b25d03254f000c36a73204720475b324
- trait 実装のリファクタリング
- [trait の最初の実装時](https://github.com/php/php-src/commit/cd6415f1a93ae5169e9ab92dfa384af29c1a1d87)に入ったオペコード ZEND_ADD_TRAIT と ZEND_BIND_TRAITS にサヨナラ
- trait は class と同様 zend_class_entry で管理されている
- ZEND_ADD_TRAIT は use に対応し、trait のクラスエントリをフェッチした後、各クラスのクラスエントリが持つ「使っている trait のクラスエントリリスト」へ追加するような処理
- ZEND_BIND_TRAITS はそのリストにもとづき、クラス定義内へ実際に trait の実装をまとめて導入していく処理
- 今回の修正により、「使っている trait のクラスエントリリスト」を廃止、かわりに「使っている trait の名前（zend_class_name）リスト」を導入
- ZEND_ADD_TRAIT でやっていたのに相当する処理は、use のコンパイル時 AST から trait 名を取り出すのみに（クラスエントリのフェッチを行わない）
- ZEND_BIND_TRAITS でやっていたのに相当する処理は ZEND_DECLARE_*CLASS へ移動し、trait のクラスエントリのフェッチもバインド時に trait の名前リストから行う

## cmb69: Simplify regression test
- https://github.com/php/php-src/commit/6644bd005f94993d9e479c02ab1acd650e1181a8
- 昨日の [array_reduce() のメモリリーク](https://qiita.com/sj-i/items/abd01faffae4e5011842#cmb69-fix-76778-array_reduce-leaks-memory-if-callback-throws-exception)の回帰テストを修正
- メモリリークのテストだからと[メモリリミットを引き上げた](https://qiita.com/sj-i/items/abd01faffae4e5011842#cmb69-increase-memory_limit-to-prevent-test-failures)際、コミットコメントで[ツッコミ](https://github.com/php/php-src/commit/5fb01a3a42ec850a3d0e09247414387e8964ef44#r30258588)が入り、修正されたもの

## dstogov: Keep information about unresolved interfaces in  zend_class_entry->interface_names.
- https://github.com/php/php-src/commit/d140df58e6152037e7e3a3a0b119eae4970dd77e
- ZEND_ADD_INTERFACE と ZEND_VERIFY_ABSTRACT_CLASS にサヨナラして interface の実装を ZEND_DECLARE_*CLASS へ移動
- 構造的に trait と似たような部分はある
- こちらはクラスエントリリストを名前リストに置き換えただけでなく、名前リストからのフェッチが済んだ後は、フェッチ後の interface のクラスエントリリストを同じ領域へ保存、という作りになっている
- たとえば継承時はこのクラスエントリリストがそのまま使われる
【swift4】 imageをcache化する

Swift4でimageをcache化する情報がなかなか見つからなかったので、記載していきたいと思います。
もし間違いなどがあれば、ご指摘いただけると嬉しいです！:grin:

まずiOSではNSCacheクラスを用いて、キャッシュ化を行います。
##NSCacheについて
イメージなどをURLから読み込むような処理をしていると、毎回読み込みに時間がかかります。そこでイメージをキャシュ化し、iOSのメモリに保存します。それにより、再度読み込む必要がなくなり、処理を高速化します。
ただiOSのメモリが足りなくなってくると、自動的にこのキャッシュはなくなるようにできています。

- 型<br>
dictionary型と同じで、keyとvalueのセットです。
保存したいデータがvalueになり、保存したデータを取り出すためにkeyを設定しなければなりません。

ex)
 
```
static let imageCache = NSCache<AnyObject, AnyObject>()
```


今回はimageをキャッシュ化するので、UIImageViewのextensionとして定義していきます。
##UIImageViewExtension

```swift:UIImageViewExtension.swift
import UIKit

extension UIImageView {

    //NSCacheのインスタンスを生成しておく。ここに、どんどんキャッシュ化されたものが保存されていく。
    static let imageCache = NSCache<AnyObject, AnyObject>()
    
    //読み込むURLのstringを引数にする。
    func cacheImage(imageUrlString: String) {
           
        //引数のimageUrlStringをURLに型変換する。
        let url = URL(string: imageUrlString)
        
        //引数で渡されたimageUrlStringがすでにキャッシュとして保存されている場合は、キャッシュからそのimageを取り出し、self.imageに代入し、returnで抜ける。
        if let imageFromCache = UIImageView.imageCache.object(forKey: imageUrlString as AnyObject) as? UIImage {
            self.image = imageFromCache
            return
        }
        
        //上記のifに引っかからないということは、キャッシュとして保存されていないということなので、以下でキャッシュ化をしていく。
        //URLSessionクラスのdataTaskメソッドで、urlを元にして、バックグランドでサーバーと通信を行う。
        //{ 以降はcompletionHandler(クロージャー)で、通信処理が終わってから実行される。
        //dataはサーバーからの返り値。urlResponseは。HTTPヘッダーやHTTPステータスコードなどの情報。リクエストが失敗したときに、errorに値が入力される。失敗しない限り、nilとなる。
        URLSession.shared.dataTask(with: url!) { (data, urlResponse, error) in
            
            //errorがnilじゃないということは、リクエストに失敗しているということ。returnで抜け出す。
            if error != nil {
                print(error)
                return
            }
            
            //リクエストが成功して、サーバーからのresponseがある状態。
            //しかし、UIKitのオブジェクトは必ずメインスレッドで実行しなければならないので、DispatchQueue.mainでメインキューに処理を追加する。非同期で登録するので、asyncで実装。
            DispatchQueue.main.async {

                //サーバーからのレスポンスのdataを元にして、UIImageを取得し、imageToCacheに代入。
                let imageToCache = UIImage(data:data!)

                //self.imageにimageToCacheを代入。
                self.image = imageToCache
                
                //keyをimageUrlStringとして、imageToCacheをキャッシュとして保存する。
                UIImageView.imageCache.setObject(imageToCache!, forKey: imageUrlString as AnyObject)
            }
        }.resume()
    }
}
```


実際にこのメソッドを呼んでいるコード

```swift:example.swift
import UIKit

class ExampleViewController: UIViewController {

    let imgString = "https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
      
    override func viewDidLoad() {
        super.viewDidLoad()
        
        profileImage.cacheImage(imageUrlString: imgString)

    let profileImage: UIImageView = {
        let image = UIImageView()
        let myColor = UIColor(red: 246/255, green: 177/255, blue: 67/255, alpha: 1)
        image.contentMode = .scaleAspectFill
        image.layer.borderColor = myColor.cgColor
        image.backgroundColor = UIColor.white
        return image
    }()

}


```
Pythonにおける真偽値

# 無限ループにハマった

Pythonでコードを書いていて無限ループにハマりました。


## 問題

以下、その時のコードのシミュレーションです。
    

```
import random

def _randomReturnArgOrZero(a):
    """ランダムに引数をそのまま返したり0を返したりする"""

    if bool(random.getrandbits(1)):
        return 0
    return a

def main():
    x = None

    while True:
        x = _randomReturnArgOrZero(x)
        if x:
            print('the argument is not returned; end.')
            break
        else:
            print('just the argument is returned; retry.')
    return x
```

ナンセンスな例ですが、こんな感じのことをやろうとしました。はい、無限ループです。

Pythonists な各位にはお分かりでしょうが、 `if x` は絶対に通らない節になっています。


# Pythonでは0は偽として扱われる

ということです。単純な話でしたが、実際のコードの中ではなかなか気づけませんでした。

シェルで0が真、0以外が偽として扱われるのとは反対ですね。
シェルで0が真を表すのは、成功を意味するステータスコードとして0が利用されているというのはそうなんですが、突き詰めていくと、何も返さないことで成功を表すというUnix哲学から来ている気がします。

ちなみにCommon Lisp では `nil` および空リスト `()` のみが偽で、それ以外は真(明示的に真の値を表現する場合はシンボル `t` を使う)です。つまり、0も1も、空文字列 `""` も真です。
これはLispが"List Processor"であることから来ている性格ですね。

なお、Cでは0が偽として扱われますが、これは「Cがもともとビット処理言語だったことを意味している」（『実践Common Lisp』 p.47 オーム社、Peter Seibel 著、佐野匡俊・水丸淳・園城雅之・金子祐介 共訳）ということです。

rubyだとnilとfalseのみが偽です。

この辺り、各言語の性格が現れていて非常に面白いです。


## Pythonにおける真偽値

では、Pythonではどうでしょうか。
0が偽として扱われるのは冒頭で見た通りですが、空文字列や空リストは？
ということで、いくつか見ていきます。

```
>>> bool(False)
False
>>> bool(True)
True
>>> bool(0)
False
>>> bool(1)
True
>>> bool(0.0)
False
>>> bool("") # 空文字列
False
>>> bool([]) # 空リスト
False
>>> bool({}) # 空辞書
False
>>> bool(()) # 空タプル
False
>>> bool(set()) # 空セット
False
>>> bool(None) # None型
False
```

Pythonの場合は空のオブジェクトはすべて偽、という扱いですね。
シンプルでわかりやすく、を目指す言語的性格の表れなのでしょうか。

Perlとは少し似ている気がします。言語の登場時期が近いのも関係があるかもしれません。
Cf.) [Perl の真偽値について](https://qiita.com/xtetsuji/items/e584b819d3cd1604a91f)


# 結論

xにbool型の値以外が入ってくる可能性があるところで、安易に `if x` みたいなコードを書くな、ということですね。
挙動が完全に理解できているなら良いですが、そうでなければ `if x is not None` みたいにちゃんと比較演算子を使ってあげるべきです。


### 最初の例についての補足

最初の例で `bool(random.getrandbits(1))` でランダムにif文を通す処理を書いていましたが、まさにこれは0/1をboolで変換して真偽値を作っていました。これを書ければ `if 0` が決して通過しない条件節であることは分かるはずです。
ただ、実際のコードではランダムに真偽値を作るということは行なっていなかったので、もちろんそんなことには気づかなかったわけです。
ナンセンスな例を出してしまったので、ちょっと補足でした。
OpenCVのRTreesで学習を行う小さめのコード

OpenCVのRTreeを利用して学習を行う。

下記記事内のコードをRtreeを使うように書き直したもの。
https://qiita.com/color_box/items/6f7d06fc3d65c6913ebf

```python
# -*- coding: utf-8 -*
import glob
import cv2
import numpy as np

# generateimage array
def creeate_image_array(img_paths):
    images = []
    for img_path in img_paths:
        image = cv2.imread(img_path)
        reshaped_image = image.reshape(image.shape[0] * image.shape[1] * 3)
        images.append(reshaped_image)
    return np.array(images, np.float32)

def main():
    # positive and negative image paths
    pos_img_paths = glob.glob('./data/grouped_icons/0/*')
    neg_img_paths = glob.glob('./data/grouped_icons/1/*') + glob.glob('./data/grouped_icons/2/*') + glob.glob('./data/grouped_icons/3/*')

    # load images
    positive_images = creeate_image_array(pos_img_paths)
    negative_images = creeate_image_array(neg_img_paths)
    images = np.r_[positive_images, negative_images]

    # generate labels
    positive_labels = np.ones(len(pos_img_paths), np.int32)
    negative_labels = np.zeros(len(neg_img_paths), np.int32)
    labels = np.array([np.r_[positive_labels, negative_labels]])

    # RTree
    rtree = cv2.ml.RTrees_create()
    rtree.setMaxDepth(50)
    rtree.train(images, cv2.ml.ROW_SAMPLE, labels)
    rtree.save('dead_train.xml')

if __name__ == '__main__':
    main()
```

無加工の画像を利用して学習を行っている。
出力した学習モデルを利用して分類を行うコードは下記のようになる。

```python
# -*- coding: utf-8 -*
import glob
import cv2
import numpy as np

# generate image array
def creeate_image_array(img_paths):
    images = []
    for img_path in img_paths:
        image = cv2.imread(img_path)
        reshaped_image = image.reshape(image.shape[0] * image.shape[1] * 3)
        images.append(reshaped_image)
    return np.array(images, np.float32)

def main():
    # set test target images
    test_img_paths = glob.glob('./data/test_tearget/*')

    # loading
    rtree = cv2.ml.RTrees_load("dead_train.xml")
    
    # test
    images = creeate_image_array(test_img_paths)
    predicted = rtree.predict(images)
    result = predicted[1]
    for (i, img_path) in enumerate(test_img_paths):
        if result[i][0] == 1.0:
            print("yes", img_path)
        else:
            print("no", img_path)

if __name__ == '__main__':
    main()
```

## 参考コードについて
https://github.com/opencv/opencv/blob/master/samples/python/letter_recog.py
OpenCVで利用可能な機械学習の手法がいくつかあるが、上記URL内のコード例が公式で用意されているものなのでかなり参考になる。


## 参考
https://github.com/opencv/opencv/blob/master/samples/python/letter_recog.py#L108
https://qiita.com/color_box/items/6f7d06fc3d65c6913ebf
運用現場を立て直す、生産的なチームの目指し方

大仰なタイトルを書いておりますが、できるだけあっさりすっきりと書いてみます。

具体的な施策の話は以前記事にした「[Excel方眼紙で手順書作るのやめ隊 - AWS/GitHub/CircleCI/mkdocs でドキュメント管理-](https://qiita.com/hassaku_63/items/83daf4dac111f0b5390f)」のような話があります。ツールなどの具体的な手段の話は個別にアップデートしていくつとして、ここではもっとソフトな話、改善を回していけるチームの作り方について言及しようと思います。

この記事は今後もアップデートしていくつもりです。私以外の方が持つ知見も追記していきたいので、プラクティスがあればコメント欄などでご教示を、是非に。

# 前提事項

所謂「定型運用」の現場における話です。手順書に基づいてそれを日々こなすための部隊。

フロントにあたるサービスデスクがいて、そこからディスパッチされた作業依頼がやってくるような業務イメージです。あと、割と破綻寄りな状況の現場を想定してます。

なお、これを書いてる私の立場は運用部隊でオペレーター上がりのチームマネージャーです。運用畑の職歴は2018年8月の時点で1年少々です。

あと、ここに書いてあることは全てフィクションです。現実にこのような現場はございませんのでお察しください。

# 心得

いくつかありますが、要点は次の3つです。

- 叱責をしない
- 人間である以上、ミスは起こるものという前提に立つ
- 現場を大事にする

--- 

現場をだめにしたければ、オペミスしたメンバーを詰めて、徹底的に再発防止策を検討させればいいです。オペミスは絶対にあってはならないことであり、ミスをするのは注意が足りないからだと、現場に突きつければいいです。

生産性を上げて効率のよいオペレーションを実現したければ、失敗したメンバーを責めたり否定したりすることは金輪際やめて、建設的・寛容的な態度で根本解決の策を一緒に探っていく道を模索すると良いです。

--- 

オペミスは、人間が介在する以上必ず起こりうるものだという前提に立つことです。

疲労すれば集中力は低下し、チェックの精度が落ちます。何十件何百件も同じ作業を繰り返せば、どれだけ気をつけようとたまには惰性になってしまう時があります。

「定型作業」は基本的にルーティンワークであり、刺激が比較的少ない作業と言えます。刺激が少なければ脳の働きだって低下しやすいわけで、普通に作業を進めるだけでも集中力が低下しやすいコンディションにあると言えます。

こうしたことを踏まえてみると、「オペミスはありえない」「もっと注意深くやれ」などと強要することは、人間の生理現象をも否定しかねない考え方だと、言えはしませんか？

いかにして人依存を減らすのか。いかにして認知負荷が低く、曖昧さのない、よい手順書を作っていくか。人が頑張るのではなく、人に頑張らせないためにどのような手段が取れるのか。そうした考え方こそが求められる指針であり、チームマネージャーが示すべき道筋ではないでしょうか。

---

メンバーを大事にすることです。

現場で起きていることを一番知っているのは現場のメンバーであり、チームマネージャーは無力です。よく話を聞き、そこから何が非効率であるのか、何がミスを誘発するのか。オペレーターの言葉から学び、本当に解決すべき問題と改善指針を考察していく必要があります。見出される解決は、決して「人間によるチェックを増やす」ことや「気をつける」などではないはずです。

オペミスが起きた時のこと。叱責したり、「さいはつぼうし」「しまつしょ」なる報告書を書かせたり、それらはメンバーにさらなる（心理的・物理的な）負担を強いるものです。成果を出すために、それは果たして、本当に必要なことなのでしょうか？よしんばそれが必要だとしても、それってチームマネージャー、現場を管理する立場であるあなたがやるべき仕事ではありませんか。現場のオペレーターはヒアリング対象であって報告書の責を引き受ける主体ではありませんね。

メンバーの愚痴を大事にしましょう。それはメンバーが主体的に発信した改善の種です。どういうときにそう感じるのか。どうなれば解決しそうか。具体的な施策を打つ場合、自分に支援できることはあるか。などといった対話から新しい改善のアイデアが生まれるかもしれません。メンバーの愚痴は歓迎すべきものです。ネガティブな発言を否定するでもなく、感情的に同意するでもない、きちんと受け止めて、建設的な態度で話を聞くように努めましょう。


#　あとがき

最近知ったのですが、Googleが 「心理的安全性」という言葉でチームの生産性向上のやり方を説いています。上で述べたようなことは全て、メンバーの「心理的安全性を高める」ための心がけであると言えます。

[参考書籍 - 世界最高のチーム グーグル流「最少の人数」で「最大の成果」を生み出す方法](http://amzn.asia/d/eqWNfSx)

自分がこれまで思っていたことを非常に明瞭に言葉にしてくれたので、記事起こしするときに思った以上に書籍にインスパイア（）されてしまいました。。。
HTTPリクエスト、HTTPレスポンスの具体的内容

# 1.序（HTTPとは何か）
HTTPは、Hypertext Transfer Protocolの略称です。Hypertextという語は「複数の文書（テキスト）を相互に関連付け、結び付ける仕組み」のことで、今日ではありふれた概念となっています。（ウェブサイト上で公開されるほぼ全てのページは、aタグ等により実装された他ページとのリンクを有するため、Hypertextです。）

従って、HTTPは、「HypertextをTransfer（転送 = 送受信）する際のProtocol（取り決め）」を意味します。

下記に例示する通り、送受信される内容自体はただの文字列ですが、この取り決め（HTTP）に従ったフォーマットで通信が行われることで、送信側・受信側の間の通信が齟齬なく可能になります。

※URLの先頭に記述される「http://」は、プロトコルの宣言で、「Httpに則った通信を行う」ということを送信先に通達します。

# 2.HTTPリクエストの構成
HTTPリクエストは、以下の3つの要素で構成される。

- リクエスト行
- ヘッダーフィールド
- ボディ

ヘッダーフィールド、ボディは省略可能。
ヘッダーフィールドとボディの間は、空行を１行挟む。

## 2-1.リクエスト行  
メソッド、URI、HTTPバージョンの3つの要素から構成され、それぞれスペースで区切られる。

``` text:リクエスト行の例
POST /index.html HTTP/1.0
```

## 2-2.ヘッダーフィールド
ヘッダの各要素は、
フィールド名: 内容
の形式で構成される。

``` text:ヘッダーフィールドの例
Accept: image/gif, image/jpeg, */*
Accept-Language: ja
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (Compatible; MSIE 6.0; Windows NT 5.1;)
Host: www.xxx.zzz
Connection: Keep-Alive
```
リクエストヘッダの種類については、下記参照
https://triple-underscore.github.io/RFC7231-ja.html#section-5

## 2-3.ボディ
主に、POST通信時のパラメータが記述される。

``` text:ボディ（POSTパラメータ）の例
hoge=piyo&yoshiki=xjapan
```



# 3.HTTPレスポンスの構成
HTTPレスポンスは、以下の3つの要素で構成される。

- ステータス行
- ヘッダーフィールド
- ボディ

## 3-1.ステータス行
3桁の数字で構成され、リクエストの結果をクライアントに通知する。
大まかに、下記の通り分類される。
100番台「情報」
200番台「成功」
300番台「リダイレクト」
400番台「クライアントエラー」
500番台「サーバエラー」

詳細については下記参照
https://triple-underscore.github.io/RFC7231-ja.html#section-6

## 3-2.ヘッダーフィールド 
リクエスト同様、ヘッダの各要素は、
フィールド名: 内容
の形式で構成される。

詳細については下記参照
https://triple-underscore.github.io/RFC7231-ja.html#section-7

``` text:レスポンスヘッダの例
alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"
cache-control: private, max-age=0
content-encoding: br
content-type: text/html; charset=UTF-8
date: Thu, 23 Aug 2018 15:37:28 GMT
expires: -1
server: gws
status: 200
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
```

## 3-3.ボディ
リクエストに対する、サーバー応答内容の本文。
webサーバーへのリクエストに対するレスポンスの場合、HTML形式のテキストデータが記述される。

# 4.実際にレスポンスを見てみる
curlが一番簡単なので、下記コマンドでgoogle.co.jpにリクエストしてみよう。

``` sh
curl -i google.co.jp
```

実行結果は、下記のようになる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/77380/e1f92050-abcc-f297-c88f-001cfe23d7ef.png)

上記の結果を見ると、google.co.jp が www.google.co.jp にリダイレクトされていることがわかる。
（ステータスコードが301、ヘッダにLocationの記述がある）

curlで叩いた場合、単に返ってきたレスポンスが文字列として表示されるだけ。
実体としては文字列のやり取りが行われているだけ、ということがわかる。

ウェブブラウザは、これらの記述を「解釈」して、様々な動作を行っている。
「解釈」が可能なのは、HTTPによって通信内容と意味が定義されているためである。



# 5.参考
https://ja.wikipedia.org/wiki/Hypertext_Transfer_Protocol
https://qiita.com/shuntaro_tamura/items/ae55b99deb9e2a170754
https://ja.wikipedia.org/wiki/Hypertext_Transfer_Protocol
http://www.tohoho-web.com/ex/http.htm
https://qiita.com/yasuhiroki/items/a569d3371a66e365316f
Go言語　プリミティブ型と型宣言

#Go言語のプリミティブ型
大別すると以下がある。

- 真偽値型
- 数値型
- 文字列型

#真偽値型
|型名|説明|
|:---:|:--:|
| bool |真（true）または偽（false）|

#数値型
数値型は以下に分けられる。

##符号なし整数
|型名|説明|範囲|
|:---|:--|:---|
| uint8 | 8ビット符号なし整数 | 0 ～ 255 |
| uint16 | 16ビット符号なし整数 | 0 ～ 65535 |
| uint32 | 32ビット符号なし整数 | 0 ～ 4294967295 |
| uint64 | 64ビット符号なし整数 | 0 ～ 18446744073709551615 |
| uint | 32ビットまたは64ビットの符号なし整数<br>（環境依存） | 0 ～ 18446744073709551615 <br><br>or<br><br>0 ～ 4294967295 |
| uintptr | ポインタの値 | （環境依存） |
|byte|	uint8のエイリアス| 0 ～ 255 |


##符号付き整数
| 型名 | 説明 | 範囲 |
|:--|:--|:--|
| int8 | 8ビット符号付き整数 | -128 ～ 127 |
| int16 | 16ビット符号付き整数 | -32768 ～ 32767 |
| int32 | 32ビット符号付き整数 | -2147483648 ～ 2147483647 |
| int64 | 64ビット符号付き整数 | -9223372036854775808 ～ 9223372036854775807 |
| int | 32ビットまたは64ビットの符号付き整数<br>（環境依存） | -2147483648 ～ 2147483647 <br><br>or<br><br>-9223372036854775808 ～ 9223372036854775807|
|rune|	int32のエイリアス| -2147483648 ～ 2147483647 |


##浮動小数点
| 型名 | 説明 |
|:--|:--|
| float32 | 32ビット浮動小数点 |
| float64 | 64ビット浮動小数点 |

こっちはfloatだけっていうのはないのね。

##複素数
| 型名 | 説明 |
|:--|:--|
| complex64 | 実数部・虚数部をfloat32で表現する複素数 |
| complex128 | 実数部・虚数部をfloat64で表現する複素数 |

##文字列型
| 型名 | 説明 |
|:--|:--|
| string | 文字列<br>ダブルクオートで囲む|

#型宣言
Go言語はtypeを使うことで新たな型を宣言できるようです。

```go
package main

import "fmt"

type weight int

func main() {
    var todayWeight weight = 50
    fmt.Printf("今日の体重%dkg\n", todayWeight)
}
```

typeを使うことによってプログラムの可読性が向上するとか。

```go
package main

import "fmt"

// 身長と体重
type Status struct {
    height float32
    weight float32
}

type StatusInfo func(Status) string

func main() {
    var status Status
    var statusInfo StatusInfo
    // 身長と体重を入力
    status.height = 1.7
    status.weight = 70
    // BMIを表示
    statusInfo = getBMI
    fmt.Println(statusInfo(status))
    // 理想体重を表示
    statusInfo = IdealWeight
    fmt.Println(statusInfo(status))
}

func getBMI(s Status) string {
    return fmt.Sprintf("BMIは%.1fです。", s.weight / (s.height * s.height))
}

func IdealWeight(s Status) string {
    return fmt.Sprintf("理想体重は%.1fkgです。", (s.height * s.height) * 22)
}

```

実行結果

```
BMIは24.2です。
理想体重は63.6kgです。
```
スクラム開発-タスクボード公開

## タスクボード公開
### これがウチのタスクボードだ！
半年前、タスクボード（かんばん）を作るときに、非常に悩み抜いて考案したタスクボードデザインを公開します。非常に評判良くずっと使っています。
![Scrum_taskboard_kanbann.jpg](https://qiita-image-store.s3.amazonaws.com/0/283248/25a746b0-1e44-5a2b-d193-ec8fd6046fe4.jpeg)


◆TMP RULE（スプリント一時ルール）
このスプリントで試してみるルールやKPTのTRYを張る。
【例】ペアプログラミングをなるべく行う。

◆SPRINT BACKLOG（スプリントバックログ）
よくあるTODAY TODO DONEの3列形式では、全体のスケジュール感が見えないことを解消した。
この欄の付箋を移動しながら、__何日にどれをやるべきという予想を話し合う。__一番良くメンテナンスするエリア。

◆Doneノート（タスク完了ノート）
ホワイトボードのDONEとなった付箋を朝会後にDoneノートへ移動する。
実働時間の計測方法や完了条件などにブレがあるのでチェックする。
また、__次回のスプリント計画に活用できるので、状態よく付箋を保管する。__

## 前提となっているプロジェクトルール
◆付箋の書き方
見積時間が4h以下の粒度となるようにタスクを分割すること。

◆朝会
毎朝15分。下記を一人ひとり発表していく。
 1-1.昨日やったこと
 1-2.問題点
その後、チームで話し合いながら下記を行う。
 2-1.Doneに不正がないか確認し、Doneノートへ移動する。
 2-2.全体スケジュールを見直す。
 2-3.今日やることをTODAYに移動する。
jupyterでアニメーションとgifファイルを作成する準備

#要旨
jupyter でアニメーションをつくり、それをgifで保存できると聞いたので、その環境を整えました
わりと迷ったので方法を残しておきます。

OSはwindows10です。

↓こちらのサイトを参考にさせていただきました。
https://imagingsolution.net/program/python/matplotlib/animationgif/

##方法
まず[ImageMagick](http://www.imagemagick.org/script/download.php"ImageMagick]")をインストールします。

OSXやLinuxならコマンドでインストールできるようですが、winはそうではないようです。
インストールができたらmatplotlibのにパスを設定しているファイルの位置を特定します。
次のコードでディレクトリが分かります。これはjupyter内で

```
import matplotlib
print(matplotlib.matplotlib_fname())
```
と実行すればわかります。(jupyterでないとできないわけではない)

__matplotlibrc__をみつけたらこれを編集します。
matplotlibrcの最下方に

```
#animation.convert_path: ‘convert’ .....
```
という一行があるのでここを

```
animation.convert_path: E:\ImageMagick-7.0.8-Q16\magick.exe
```
と変更します。__＃は削除__、 `'convert'` の部分をImageMagickのパスに変更します。
このとき `' '`　は__不要__なことに注意です

ここまでができたらAnimationの保存をしてみましょう

```
ani.save("fugaa.gif",writer='imagemagick')
```

同ディレクトリにfugaa.gifが保存されるはずです。

ffmpegなのかimagemagickなのかがよくわからず、しっかりできていないとエラーを大量に吐くため
けっこう焦りました。imagemagick１本でうまくいったのでこれでいいかなーと思います。
imagemagickのディレクトリ内にffmpegもあるみたいだし……。
MDWikiの気に入った点と課題に感じた点について

# Markdownで書けるWikiとしてはメチャよいと思う。

## 公式ドキュメント
公式ドキュメントがあるので使い方は一番そちらが詳しい。
[MDwiki - Markdown based wiki done 100% on the client via javascript](http://dynalon.github.io/mdwiki/#!index.md)

## MDwikiを導入するときには
[Firefox](https://www.mozilla.org/ja/firefox/new/)をインストールしておけばローカル上でMDwikiを試せる。

## 気に入った点
* MarkDownで書けること
* [Gimmicks](http://dynalon.github.io/mdwiki/#!gimmicks.md)が面白い。ドキュメントがグラフィカルで面白くなる。
* Wikiのインストールだとかよく分からない自分には便利だった。
* 静的なサービスに見えるので、第三者に悪意に編集される問題も少なさそう。
* レスポンシブ対応していること(スマホでも見やすい)

## 課題に感じたこと
* 広告バーなど入れたくて、ページレイアウトまで扱いたいときは逆に難しかった。
* 読み込むまでの遷移が視認できるスピードなので気にする自分は向かないと感じた。

## 課題の対策
* MDWikiの今後の更新に期待する。
* コードいじれる人がいるならば解決案が浮かぶかもしれない。[^1]
* レイアウト編集が優先度高ければ、Wiki形式は捨てて[BootStrap](https://getbootstrap.com/)を活用して直接コードを書いた方が早い。

## 純粋に個人で編集するWikiとして使いたい人には向いていると感じた。

上記目的ならMDWikiメチャいいと思う。

[^1]: 自分は本体のファイルを編集してまで使おうとはしなかった。(解読に時間が掛かりそうだったため)
Gomega の Eventually は MatchMayChangeInTheFuture が実装されている場合 abort されうる

## `Eventually`記法

Ginkgo とセットで使われるテストマッチャーの [Gomega](https://onsi.github.io/gomega/) には
`Eventually` という便利な機能があります。
これを使うと、`Eventually`に渡した処理が期待する結果を返すか、
あるいは規定秒経過するまで、
与えた処理をリトライさせ続けるテストが書けます。
実行完了まで時間のかかる処理をテストしたい場合に有用です。

```go
By("Checking cluster status")
Eventually(func() bool {
  controlPlanes := []string{node1, node2, node3, node4}
  workers := []string{node5, node6}
  return checkClusterStatus(status, controlPlanes, workers)
}).Should(BeTrue())
```

## タイムアウトを待たずして`Eventually`をabortするMatcherとそうでないMatcherがあります

しかし 「`Eventually` に渡した処理のアサーションは規定秒必ず待ってくれるんだな〜」と言えるのかというと、そうではないです。

デフォルトで用意されてるマッチャーの中には
`MatchMayChangeInTheFuture`というメソッドが実装されているものがあります。
これが実装されているマッチャーは、
`MatchMayChangeInTheFuture`のチェックで
これ以上値の変化は起きないだろうと判断した場合、タイムアウトを待たずしてテストをfailします。

具体的には `gexec.Command` を使ったときの `exitMatcher` がそれです。
これを使うと、

```go
Eventually(session).Should(gexec.Exit())
```

みたいな感じで `Eventually` にコマンドを渡してexecすることができるのですが、
これは `-1` 以外のステータスコードが返った時点で `Eventually` が終了します。
その後はリトライされません。

自分はこのことを知らないまま雰囲気で`Eventually`を使っていたので「`Eventually`に渡した処理が5秒で終わるんだが…？」などと悩み、
20分かかるテストを再実行したり、
実装を読んだりしました。

そしてこの話は普通にドキュメントに書いてありました。
ちゃんとドキュメントを読もう。(自戒)
https://onsi.github.io/gomega/#aborting-eventuallyconsistently
「家の鍵、閉めたっけ？」をAIでなくそう！

当記事は以下のパクリです。
> 「エアコン消したっけ？」をAIでなくそう！
> https://qiita.com/it31415/items/f3726b547795904837ef

# 概要
AutoMLをつかって、家の鍵が閉まってるかを見分けるAIをつくりました。

# はじめに
あんまり有用性はないけど、AutoMLのポテンシャルを測るにはいいかと思います。「エアコン消したっけ？」をAIでなくそう！の記事書いた人がKeras好きそうなので [AutoKeras](https://autokeras.com/) 使ってほしいですね。

# 準備
今回は3種類のドアの鍵で試してみました。

## 写真を撮る
自分の家の鍵の開閉を見分けたいので、開閉の写真をそれぞれ100枚ずつぐらい撮りました(連写)。
カメラを取り付けて定位置から認識させようと思っているため、そこまでロバストにする必要性はないと考えています。

| ドアの種類 | Open | Close |
| --- | ---- | ----- |
| 種類1 |![IMG_0676.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/e5fe7eb7-81e3-59e4-0a4f-e6ab16d05d67.jpeg)|![IMG_0493.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/4924875c-a7f9-345f-7112-135059a33e45.jpeg)|
| 種類2 |![IMG_1961.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/0f10f9ba-05de-905f-d048-5edc23d801da.jpeg)|![IMG_2169.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/d80d1c18-e48c-36a9-4da8-381ae3460f20.jpeg)|
| 種類3 |![IMG_2289.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/ef347adc-28d1-2517-42f0-6cffc3b0b7ab.jpeg)|![IMG_2391.JPG](https://qiita-image-store.s3.amazonaws.com/0/208363/12fbc2fd-6cb6-f85b-554c-f6fb875fdfa0.jpeg)|

```
door_lock/
  ├ open/
  │    └ image1.jpg
  │    └ image2.jpg
  │         ︙
  │    └ image100.jpg
  └ close/
       └ image1.jpg
       └ image2.jpg
            ︙
       └ image100.jpg
door_lock.zip
```
**door_lock** は任意の好きな名前です。 **open**, **close** は画像のラベル名です。あとは **door_lock** フォルダを圧縮してください。
## 実行環境
TPUが無料で使えるAutoML Vision上で実行しました。TPUをつかうとGPUより数十倍も学習速度が速くなりとても便利です。

## データセットの作成
データセット名は任意の好きな名前をいれてください。先程作成したzipファイルを選択してください。
![beta-dot-custom-vision.appspot.com_vision_datasets_create_project=automl-mnist.png](https://qiita-image-store.s3.amazonaws.com/0/208363/c07db33f-c1ed-4192-c08d-67a84f50bdec.png)

## 学習
無料で学習できます。
![beta-dot-custom-vision.appspot.com_vision_datasets_train_dataset=ICN9135484089041269417&model=ICN7784613418023568642&project=automl-mnist.png](https://qiita-image-store.s3.amazonaws.com/0/208363/b901f28b-770a-305f-ccf4-7ac83d198b62.png)

# 結果
200枚ぐらいの画像で学習してテストしたところ、正解率は99.9%でした。

## 種類1
![beta-dot-custom-vision.appspot.com_vision_datasets_evaluate_dataset=ICN9135484089041269417&model=ICN7784613418023568642&project=automl-mnist (1).png](https://qiita-image-store.s3.amazonaws.com/0/208363/84d40c73-c2eb-479f-0a12-c58660defacc.png)

## 種類2
![beta-dot-custom-vision.appspot.com_vision_datasets_evaluate_dataset=ICN7959225301562370646&model=ICN7690753354884568616&project=automl-mnist.png](https://qiita-image-store.s3.amazonaws.com/0/208363/6687afec-7c3f-82f8-6ea5-a3269a733bd7.png)

## 種類3
![beta-dot-custom-vision.appspot.com_vision_datasets_evaluate_dataset=ICN508234856730133198&model=ICN8867420221173771186&project=automl-mnist.png](https://qiita-image-store.s3.amazonaws.com/0/208363/4a3c83ba-3d00-0ec9-41f4-72b64dbc61d4.png)



# おわりに
データセットを [こちら](https://drive.google.com/drive/folders/1gUhLH-_f4MRD5FeCeZ3PDz_9iIupePVm?usp=sharing) に用意しましたのでご自由にお使いください。鍵が開いてるか閉じてるかだけのちがいだったので、見分けるのは難しいかなあと思っていましたが、結構いい精度になりました。ほとんど同じ位置からの写真だったので簡単だったのかもしれません。
Amazon Simple Notification Service

AWSのSNS(Amazon Simple Notification Service)について勉強したことをまとめます。

SNS(Amazon Simple Notification Service)とSQS(Amazon Simple Queue Service)はAWSでイベントドリブンアーキテクチャを実現するためのサービスです。

#1 SNS
###1.1 SNSの概要
特定のサービスへメッセージを送信するためのサービス。
トピックと呼ばれるものを作成してメッセージの送信を行う。
パブリッシャー(メッセージを送信する側)がSNSにメッセージをトピックに登録するとサブスクライバー(メッセージを受信する側)に即時にメッセージを送信する。
SNSはHTTPやEmail、AWSLambdaなどの複数のプロトコルに対応しているのでサブスクライバーにはWEBサーバ、メールアドレスなど色々なものを登録できる。
後述するSQSとも連携可能。
メッセージを瞬時に処理することができ、シンプルな構造でアプリケーションを統合することが可能。

###1.2 CLIから操作してみる
####1.2.1 トピックの作成

```
aws sns create-topic --name my_topic
```

####1.2.2 サブスクライバーの設定

メッセージをSNSから受信するサービスの設定を行う。
今回は手軽にEメールを使用するので受信に使用するプロトコルに"email"を指定し、
エンドポイントに使用可能なメールアドレスを指定した。

```
aws sns subscribe \
--topic-arn arn:aws:sns:ap-northeast-1:[AWSアカウントID]:my_topic \
--protocol email \
--notification-endpoint 〇〇〇〇〇〇〇@〇〇〇〇.〇〇〇
```

上記コマンドを叩くとこんなメールが届いた。
メールアドレスの確認のためのメールのようです。
Confirm subscriptionのリンクをクリックすればOK。

```
You have chosen to subscribe to the topic: 
arn:aws:sns:ap-northeast-1:[AWSアカウントID]:my_topic

To confirm this subscription, click or visit the link below (If this was in error no action is necessary): 
Confirm subscription

Please do not reply directly to this email. If you wish to remove yourself from receiving all future SNS subscription confirmation requests please send an email to sns-opt-out
```

####1.2.4 トピックにメッセージ登録
Hello worldというメッセージをSNSに登録してみる

```
aws sns publish \
--topic-arn arn:aws:sns:ap-northeast-1:[AWSアカウントID]:my_topic \
--subject "test" \
--message "Hello World"
```




####1.2.5 メールの確認
トピックに登録されたメッセージは即時にサブスクライバーに送信されるのでメールを確認してみる。
件名：test
内容：hello world
というメールを受信できていたので動作確認完了です。
typedef

蟻本やってたらtypedefとかいうのが出てきてわかんなってなったから調べる。
まず、その型の変数の宣言文を書く。変数の名前をPATHBUFにする。
char PATHBUF[_MAX_PATH];
次に、この文の先頭にtypedefと書きます。
typedef char PATHBUF[_MAX_PATH]
これで、char型の_MAX_PATH個の要素を持った配列という型の同義語PATHBUFが定義された。これは変数の宣言は行っておらず、型を作っただけであることに注意。
競プロでの標準入力

毎回よくわからなくなるのでメモ的記録。

```python
s = input() # <= "abc"
```

```python
N = int(input()) # <= 3
```

```python
s1, s2, s3 = input().split() # <= aaa bbb ccc
```

```python
N1, N2, N3 = map(int, input().split()) # <= 1 2 3
```

```python
s = [input() for i in range(N)]　# <= aaa
                                 #    bbb
                                 #    ccc
```

```python
n = [int(input()) for i in range(N)]　# <= 1
                                      #    2
                                      #    3
```

```python
s = [input().split() for i in range(N)]　# <= aaa ddd
                                         #    bbb eee
                                         #    ccc fff
```

```python
N = [list(map(int, input().split())) for i in range(3)]　# <= 1 1
                                                         #    2 2
                                                         #    3 3
```
Gradle で Failed to resolve: com.android.support:recyclerview-v7:27.1.1 

一発ネタ。

Androidで突然GradleのSyncに失敗するようになってしまった。結構長い時間ハマったので、メモ。

# エラー内容

冒頭のとおり、Sync時に

```
Failed to resolve: com.android.support:recyclerview-v7:27.1.1 
```

とメッセージが出る。

原因は間違いなく <code>build.gradle</code>に以下を追加したこと。

``` gradle
dependencies {
       :
    implementation 'com.android.support.recyclerview-v7:27.1.1'
       :
}
```


# 一般的な解決策

一般的には以下で治ることが多い

### サポートライブラリの更新

Android Studio右上から<code>SDK Manager</code>を起動し、<code>SDK tools</code>タブから<code>Android Support Repository</code>にチェックを入れ、OKを押下し、更新する。

### mavenの追加

<code>build.gradle</code>（プロジェクト側）に以下を追加する。

``` diff
allprojects {
    repositories {
        google()
        jcenter()
+       maven { url 'https://maven.google.com' }
    }
}
```
どこかに記載があるらしいが、一次ソースがみあたらなかった。

# 今回の解決策

上位いずれでもなおらなかった。アプリ側の<code>build.gradle</code>に以下のように変更することでなおった。

``` diff
dependencies {
     :
-   implementation 'com.android.support.recyclerview-v7:27.1.1'
+   implementation 'com.android.support:design:27.1.1'
     :
}
```

<code>design</code>を入れると<code>recylerview</code>も使えるようになるのか..
ただ、いまいち釈然としないので、詳しい人いたら教えてください。
何がともあれ、これでサポートライブラリの<code>recyclerview</code>が使えるようになった。
Auto Scaling

##1.AWS Auto Scaling##
アプリケーションの負荷を処理するのに適切な数のAmazon EC2インスタンスが生成、維持されます。



##2.スケールアウト・スケールイン##

- スケールアウト：インスタンスを追加されること
- スケールイン：インスタンスが削除されること



##3.必要な設定##　

- 起動設定:何を立ち上げるのか、起動する要素を決めます。
 - AMI
 - インスタンスタイプ
 - セキュリティグループ
 - ロール

- Auto Scaling グループ:どこで行うか、デプロイの範囲を決めます。
 - デプロイ設定
 - VPCとサブネット
 - ロードバランサー
 - 最小・最大インスタンス数
 - キャパシティ
  
- Auto Scaling ポリシー：いつトリガーとして実行するのか決めます。
 - ポリシー設定
 - スケジュール
 - オンデマンド
 - スケールアウト・インポリシー



##4.料金##
Auto Scaling 設定そのものは無料です。



##5.ヘルスチェック##
EC2ヘルスチェックにするか、ELBヘルスチェックにするか？
前者にすると、EC2インスタンスが正常に稼働しているかを調べるため、アプリケーションが正常に稼働しているかまではわからない。
なので、後者であれば、アプリケーションレベルの応答を持って正常と判断できる。

- EC2ステータス:インスタンスのステータスがrunning以外の状態を異常と判断
- ELB:ELBのヘルスチェック機能を活用する。

・猶予時間が短い場合、どのような問題が生じるのでしょうか.
ヘルスチェックの猶予期間はインスタンスの起動～アプリケーションデプロイ～サービス組み込み、までに要する時間よりも長くとっておく必要があります。
猶予期間が短い場合、インスタンスの構成が終わっていない状態でヘルスチェックが開始してしまい、結果ヘルスチェックに失敗→Terminateという動きになってしまいます。
場合によってはインスタンスが追加されないことで再度アラームが起き続け、インスタンス起動～削除の無限ループに陥ってしまう可能性もあるので注意してください。


##6.AMIかuser-dataか##

AMIはアプリケーションがき見込まれているため、起動が早い。ただし、アプリケーションを変更するたびに毎回AMIを作り直す必要がある。

ユーザーデータはアプリケーションは組み込まないため、変更時に手間がない。しかし、配備するたびにアプリケーションをインストールすす必要があるため、起動に時間がかかる。

変更の少ない部分はAMIで、変更の多いソースコードなどの部分や外部パラメータなどは、ユーザーデータで実装することは推奨されています。

##7.注意##

- データベースサーバーやファイルサーバーなどデータをローカルに保存する用途には向いていない。
- ステートフルな実装を求める。AMIから復旧するので、消えてしまうデータはRDS,S3などにおく構成にする。
- EC2,アプリケーションの起動に数分かかる。突発的なアクセスには向いていないので動的スケーリングではなく、スケジュールスケーリングを利用する。


##8.demo##
[Auto Scalling demo](https://qiita.com/leomaro7/items/2644bcf2991a371fe4a9)
[Auto Scaling を試してみた](https://qiita.com/leomaro7/items/eb8305c47662bba6b28f)


##9.プラン##

|プラン||
|---|---|
|手動スケーリング|設定外で予期せぬスケーリングが必要となった場合に手動でスケーリングを実施 |
|スケジュールベース|予測された需要増時期にあわせたスケーリングスケジュールを設定|
|動的スケーリング|予測不能なケースにスケーリングポリシーにスケール方針を設定して、動的にスケールアウトを実施|

スケジュールを設定し、スケジュール設定を超過したら動的スケーリングにするといったように組み合わせることも可能です。


##10.設計のポイント##

- インスタンスの最大値／最小値の設定は慎重に設定する
- ステートフルなアプリケーションへの設定には、AutoScaling Groupでの自動設定が必要
- ライフサイクルフック（起動時または削除時にインスタンスを一時停止してカスタムアクションを実行）を使用、つまりターミネーション時に直ぐに削除していいものかを検討する。
- Auto-Scalingスラッシング（急なスケールインによる影響）を避ける


##11.スケールインポリシー##

需要減に基づくスケールインの際にどのインスタンスから終了するかを設定

||||
|---|---|---|
|1.OldestInstance|古いインスタンスから終了する |インスタンスタイプを変更する時|
|2.NewestInstance|新しいインスタンスから終了する	|新しい起動設定をテストしたい時|
|3.OldestLaunchConfiguration|古い起動設定を使用しているインスタンスから終了する|Auto Scaling groupをアップデートして、以前のlaunch configurationを利用するインスタンスを置き換えたい時　|
|4.ClosestToNextInstanceHour|次の請求時間に最も近いインスタンスを終了する|インスタンスの利用量を最大化しつつコストを抑えたい時　|
|5.デフォルト設定|3.4の順　その後、複数インスタンスが残った場合はランダムに終了する|Auto Scaling groupに1個以上のスケーリングポリシーを関連づけている時　|


##12.クールダウン##
スケーリング動作が完了してから別の動作が開始できるまでの秒数。
短期間にスケールアウトとスケールインを繰り返してしまう場合に利用する。


##13.予測スケーリング##

- 予測スケーリングとは
 - その名の通り、予測される需要に応じてAuto Scaling グループを事前に拡大/縮小することができる。

- どんな時に利用するのか？
 - 朝の始業時のスパイクなど、スリープ需要の変更のパターンが繰り返し発生するアプリケーションに適切です。

https://aws.amazon.com/jp/about-aws/whats-new/2021/05/amazon-ec2-auto-scaling-introduces-predictive-scaling-native-scaling-policy/
Springbootでエラー画面を表示する

###Springbootでエラーが発生したときにエラー画面を表示するサンプルです。

1.Controller

エラー処理はControllerクラスに書きます。

```ExceptionController.java
package com.stone.sample;

import java.util.Locale;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ExceptionController {
    @ExceptionHandler(NullPointerException.class)
    public String NullPointerExceptionHandler() {
        System.out.println("NullPointerException occured");
        return "error";
    }
    @ExceptionHandler(IllegalArgumentException.class)
    public String IllegalArgumentExceptionHandler() {
        System.out.println("IllegalArgumentException  occured");
        return "error";
    }
    @ExceptionHandler(Throwable.class)
    public String ThrowableHandler() {
        System.out.println("Exceotion  occured");
        return "error";
    }
    /**
     * このメソッドはテスト用です。
     */
    @RequestMapping(value = "/err", method = RequestMethod.GET)
    public String errorpage() throws Exception {
        throw new Exception();
    }
}
```

2.html

コントローラクラスでerrorをリターンしていますので、error.htmlを作成します。

```error.html
<!DOCTYPE html>
<html>
<head>
    <title>error</title>
</head>
<body>
<h1>
    error!!
</h1>

</body>
</html>
```

3.確認

プロジェクトを実行してhttp://localhost:8080/err
にアクセスしてみます。

4.実行結果

![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/7333cf54-102c-1076-256a-bd955c67efcd.png)

エラー画面をハンドルできました。
Perlでバイナリいじるときの備忘録




## Windows（とか）でPerlを使えるようにする
ActivePerlを[サイト](https://www.activestate.com/activeperl/downloads "Download Perl: Platforms and Versions")から入手

## コマンドプロンプトで試す
いい方法がわからんかった。
非Unixシステムで実行[^NonUnix]とPerlデバッガー[^Debugger]のあたりを参考にした。

``` cmd:こんな感じで試す
# 簡単に試したい場合はperl -e [command]で実行
perl -e "print 'Hello, world!';"
# もうちょっと色々試したい場合はPerlデバッガを起動
perl -d -e 42
```
## バイナリをいじる
とあるファイルのバイナリを編集しようと思って、どのプログラムが簡単だろうとネットを眺めてて、決め手となったのがこのページ[^EditBin]。
>5C がバイナリの１６進表記であると仮定して、
>\#perl -p -i.back -e 's/\x5C\x5C/\x5C/g' FILENAME

上記のようにお手軽にバイナリいじることができるって分かってのでPerlにすることにした。
Packについては、変数使っていると意図通りに動かなかったので、そういった部分についてはこのサイト[^ConvNum]を参考にして個別でPackした。

```perl:バイナリ
$suuji = 1;# 数字だよ
$packedSuuji = pack(‘H2’,’0’.$suuji);#'03'(文字型)をPackしないと想定どおり動かないぞ
$contents =~ s/00\x02/00$packedSuuji/g;#Perlでは「.」で結合するけどここではいらないよ（深く考えない）
# \x30 \x30 \x01　って扱いになる

$missPackedSuuji = pack(‘H2’,$suuji);
$missContents =~ s/00\x02/00$missPackedSuuji/g;
# \x30 \x30 \x0xxx　って扱いになる
```
## ファイル入出力
[Qiitaの記事](https://qiita.com/ymko/items/2b1002bae152bc92c4e4 "Perlでファイルをバイナリで読む")とかその他を読んでそれっぽくやった。


## Optionについて
わかりやすいサイトがあったのでその記事[^Option1] [^Option2]を参考に作る。
これを使うと勝手に短めのオプション指定もできるらしい(オプションの頭文字がかぶったら特定できる部分まで書けばOK)。
とくにEmptyPage.jp様の記事[^1]はとてもわかりやすく、感謝の気持ちでいっぱいになった。
> test.pl -no=2 -type=others
> test.pl -n=2 -t=others
> test.pl -n 2 -t others
> test.pl --n 2 --t others
↑どれもおなじ意味だよ

```perl:test.pl
# モジュールの読み込みと関数のインポート
use Getopt::Long 'GetOptions';

# デフォルト値設定
my $no = 1;
my $type = 'default';

# オプション受け取り
GetOptions(
 'no=i' => $no,
 'type=s' => $type
);
```

[^Debugger]: [The Perl Debugger](http://perldoc.jp/docs/perl/5.8.8/perldebug.pod#The32Perl32Debugger "The Perl Debugger")
[^NonUnix]: [非 Unix システムでの #! とクォート](http://perldoc.jp/docs/perl/5.6.1/perlrun.pod#35-33-32and32quoting32on32non-Unix32systems "非 Unix システムでの #! とクォート")
[^EditBin]: [Perlでバイナリ楽そうって思ったきっかけ](http://q.hatena.ne.jp/1155375639 "sedなどで巨大ファイル(約1GBぐらい)のバイナリの「5C5C」を「5C」に置換するなどの処理を行いたいのですが、")
[^ConvNum]: [Perl の数値変換](http://mikeneko.creator.club.ne.jp/~lab/perl/numerical_transform/#h1 "16進文字列→バイナリ文字列への変換のあたり")
[^Option1]: [参考サイト：Perl スクリプトでのコマンドラインオプション処理](https://emptypage.jp/notes/getopt-on-perl.html "Perl スクリプトでのコマンドラインオプション処理")
[^Option2]: [参考サイト：Getopt::Long - コマンドラインオプションを処理する](http://d.hatena.ne.jp/perlcodesample/20100514/1276960096 "Getopt::Long - コマンドラインオプションを処理する")
Rails チュートリアル　初心者のためのテスト  作成のこつ ①

こんにちは。 
今日もRailチュートリアルを勉強している皆さんへ。　
テストで悩んでませんか？　
　
よろしければ、[こちら](https://qiita.com/duka/items/2d724ea2226984cb544f)の記事でテストの概略を復習してください。
　　

さて、Userモデルを新たに追加する時に 
nameや、mail、password が、正しく設定できているかを
validate で検証しますよね。(Rails チュートリアル第6章)　


Rails チュートリアルを終えて、いいね機能をつけようと　
Likeモデルを作る時に　

`テストをどう書いたらいいんだろ？`

と悩みはげそうになったので、まとめまてみました。　


チュートリアルに沿って、Userモデルで考えてみましょう！　

>
1. まず、有効なモデルオブジェクトを作る。
2. そのモデルが有効かどうか、テストを行う。
3. あえて、モデルオブジェクトの属性を、有効でないものに変える
4. 失敗するか、テストをする。
5. validates を設定！テストでGreen になるか確認する。



User モデルを作成した時のテストを、
上の順で書いていきます。



##1.  set up メソッドでモデルオブジェクトを作成

```
def setup
　@user = User.new(name: "example", email: "example@example.com")
end
```


@userはインスタンス変数であり、
setupメソッド内で宣言しておけば、
`すべてのテスト内で`、このインスタンス変数が使えるようになります。


##2. そのモデルが有効かどうか、テストを行う。


```
 assert @user.valid?
```

assert メソッドは、第1引数がtrue である場合に、 
テストが成功したものとみなすんでしたね。　
 
忘れてしまった方は、[こちら](https://qiita.com/duka/items/2d724ea2226984cb544f)を参照ください。　
 
その後、rails test を行い、Green を確認します。


##3,4.  あえて、モデルオブジェクトの属性を有効でないものに変える　
 
あえて、エラーにしましょう！　
そして、テストしましょう！ (有効じゃないよね？と確認)

```
@user.name = ""
assert_not @user.valid?
```
 



これは、Red になるはずです。　
validates 設定前なので、name が空でも登録できちゃうんです。　 


##5  validates を設定！テストでGreen を確認する。
ラストです。validates を設定しましょう。

```
validates (:name, {presence: true })
```

validates メソッドに{presence: true}というハッシュを渡してます。
ちなみにRails では、`()は省略できて、メソッドの最後の引数としてハッシュを渡すときは、{} を省略できる`んでしたね。　
なので、省略したコードはこうなります。　

```
validates :name, presence: true
``` 
 
これで、rails test は Green になります。　
email も同様のことを行い、最終的なコードはこうなります。　
 
```test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do
    assert @user.valid?
  end

  test "name should be present" do
    @user.name = ""
    assert_not @user.valid?
  end

  test "email should be present" do
    @user.email = "     "
    assert_not @user.valid?
  end
end
``` 
 
```app/models/user.rb
class User < ApplicationRecord
  validates :name,  presence: true
  validates :email, presence: true
end
```
　

以上となります。
モデルオブジェクトを作って
あえて、属性を変えてエラーにする。
テストでRed を確認して
validatesを設定して、テストをGreenに変える。
痺れるような、流れですね！　
 
　
email でも、password も　
以上の流れを意識して書いてみましょう。　　
それでは、第2回に続きます。(多分)
swift4でrxswiftを使用してUITextFieldにmaxlengthを設定する

[この記事](https://qiita.com/6rats/items/baef5b9b70866a697701)を参考に、UITextFieldのmaxlengthを設定していたのですが、maxlengthって割と色々なところで設定するんですよね……
なので、もうちょっと色々なUITextFieldから使えるようにしてみました。

# 実装！

```maxlength.swift
func setMaxLength(textField: UITextField, limit: Int) -> (String?) -> Void {
    return { txt in
        guard let txt = txt else {
            return
        }

        if txt.count >= limit {
            let start = txt.startIndex
            let end = txt.index(start, offsetBy: limit)
            textField.text = txt.substring(with: start..<end)
        }
    }
}

hogeTextField.rx.text.subscribe(
    onNext: setMaxLength(textField: hogeTextField, limit: 10)
)

fugaTextField.rx.text.subscribe(
    onNext: setMaxLength(textField: fugaTextField, limit: 10)
)
```

textFieldを引数で渡さないようにしたい・・・が、ぐぬぬ:joy:
Clipboard.jsの使い方

#使用した経緯
Flashでクリップボードに文言をコピーするボタンを「ZeroClipboard」というライブラリを導入して開発をしていたが、Chrome69へのアップデートによって、これが無効化され、許可を得ないと動作しないようになっていた。

#対応方法
そもそもFlashっていう懐かしさを感じるものは使用しないで、違うやり方を選びました。
そこで使用したのが[`Clipboard.js`](https://clipboardjs.com/)というjQueryプラグインです。

#使い方とてもシンプル
1. jsファイルをプロジェクト内に招待
    2. 使用するのは`dist/clipboard.min.jp`だけでいいので、これをcpとかでルートディレクトリのjsディレクトリに配置
3. HTMLのheadにて読み込み

```
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <script src="./js/clipboard.min.js"></script>
    <script src="./js/jquery-3.3.1.min.js"></script>
</head>
```
3. clipboardを使う

とりあえずボタンを配置する。

```:index.htmlのbody内
<button type="button" class="btn" data-clipboard-text="ピーコ内容">コピー</button>

```

`data-clipboard-text`に代入している文字列が、クリップボードにコピーされます。

```:js/clip.js
    var clipboard = new ClipboardJS('.btn');
    // クリップ成功
    clipboard.on('success', (e) => {
        alert(e.text + 'をコピーしたよ');
    });
```
インスタンスを生成して引数にはHTMLelement（クラス名とか）などを入れることができます。
具体的にはドキュメントをご覧あれ。

#しかし困った。
業務で実行したいのは、かなり長い文章をコピーするボタンで、アラートにはこちらで定めたタイトル的な短い文言を追加したいのだった。

直感で色々やって見た。

#やって見た1 : インスタンス生成時にコールバック関数にてtitleを定義する

```:js/clip.js
    var clipboard = new ClipboardJS('.btn', (e) => {
        text: (e) => {
            return e.text;
        }
        title: (e) => {
            return 'タイトル';
        }
    });
    // クリップ成功
    clipboard.on('success', (e) => {
        alert(e.title + 'をコピーしたよ');
    });
```
こんな感じでやると、textやcontainer,triggerなど、もともと用意してある（eの直下にある）やつしか使用できない。
よって、これはundefinedでダメだった。

#やって見た2 : data-clipboard-titleを作っちゃおう

```
<button type="button" class="btn" data-clipboard-text="ピーコ内容" data-clipboard-title="タイトル">コピー</button>
```
これで指定すれば`e.title`とかできんじゃね？とか思ったが、またもやundefined

#ここで気づいた。俺は`e`と正面から向き合ってない

とりあえず`console.log(e)`としてログ出力。

<img width="467" alt="スクリーンショット 2018-09-12 23.25.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/174991/4ceabf79-d578-208b-5347-e732cce61da9.png">

こん感じで、自分でダメもとで作った`data-clipboard-title`がちゃんと格納されている。
俺はこの`clipboardTitle`が欲しい。

#やって見た3 : 最終

```
    var clipboard = new ClipboardJS('.btn');
    // クリップ成功
    clipboard.on('success', (e) => {
        const title = e.trigger.dataset.clipboardTitle;
        alert(title + 'をコピーしたよ');
    });
```
これでちゃんと予期した動きをしてくれた。

<img width="451" alt="スクリーンショット 2018-09-12 23.28.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/174991/128c9e29-8e9c-636b-23dd-6b9ca8a93004.png">

貼り付けをしてもちゃんと`data-clipboard-text`にて指定した文言が貼り付けられた。

結論、俺は`e`の直下しか踏み入れないようにしていたため、undefinedが表示され続けていた。

#結論
jsもっと勉強します。
当たり前かもしれないけど、`data-clipboard-piyo='piyo'`なんかで指定しても、
`e.trigger.dataset.clipboardPiyo`として値を持つことができます。
たくさん持てますね
Safari でお気に入りのアイコンが更新されない問題

自分用の備忘録です。

## ざっくり言うと
- Safari でお気に入りのアイコンが更新されない罠にはまった
- 原因はキャッシュのせいらしい
- Mac は頑張ってキャッシュクリアできるが、iPhone は頑張りようがないので自然更新を待つしかない模様

## 詳細
### 経緯
サイトに設定していたファビコン、ウェブクリップアイコンを新しい画像に差し替えたところ、
Mac、iPhone の Safari だけお気に入りのアイコンが古い画像のまま更新されませんでした。

### 試したこと
1. 履歴やキャッシュの削除
2. 新しい画像に差し替えられているか確認
3. アイコンがルートディレクトリに配置されていることを確認
4. アイコンの命名を変更する
5. アイコンのサイズを変えてみる
6. Mac や iPhone の再起動
7. 登録済みのお気に入りを削除後、1〜6 を繰り返してから再度お気に入り登録を行い確認

色々やりましたが、暖簾に腕押しな感じでした...。

### Mac でのキャッシュクリア
調べる中で、Safari のアイコンは普通にキャッシュを削除するだけでは消えないことがわかりました。
下記の操作で、自分の Mac の Safari でお気に入りのアイコンが更新されるようになりました。

1. ライブラリフォルダの safari フォルダへアクセス
2. Touch Icons Cache フォルダ内の Images フォルダの中身を全削除

### iPhone でのキャッシュクリア
iPhone も PC と同様、どこかにアイコンのキャッシュがあると思われます。
けれど PC のようにライブラリフォルダにアクセスできないので、設定からキャッシュをクリアする以上の対応は難しそうです。

### 結論
Apple Developer でファビコン関係のページも読んだのですが、解決方法を見つけるには至らず...。
Safari でお気に入りのアイコンが更新されないのは仕様と見なし、自然に更新されるのを待とうと割り切ることにしました。

この件に関して他に試すべきだった方法、適当な情報をご存知の方がいらっしゃいましたら、ご教示くださると幸いです:bow:

## 参考
- [「アイコンが変わらない！」原因はキャッシュ。safariのブックマークアイコンが更新できないのまじなんなの。](https://www.misallychan.com/entry/2017/04/05/222945)
  - キャッシュクリアに関して、こちらの情報を参考にさせていただきました。ありがとうございます。
オレオレ証明書(SSL証明書,自己証明書)の作成方法

#概要
オレオレ証明書(自己証明書)を作成する方法を以下に記載する。

#手順
##１．Keytoolで「オレオレ証明書」を作成する。
###1. JDKをインストールする
OracleのサイトよりJDKをダウンロードしてインストールする。
ここでは、Program File配下に「jdk1.8.0_161」をインストールしている。
###2. keystoreファイルを格納するフォルダを作成する。
ここでは、「C:\tmp」としておく。
###3. インストールしたJDKフォルダ配下に「keytool.exe」が格納されていることを確認する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/204260/48d5ad92-86d0-ed71-1938-e495b44ce91f.png)
####4. コマンドプロンプトを立ち上げて、上記フォルダに遷移する。
```:遷移コマンド
cd C:\Program Files\Java\jdk1.8.0_161\bin
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/204260/693557ce-e3c3-deb8-e7fc-fbf972fcd87b.png)
####5. keystoreファイル作成コマンドを実行する。
```:コマンド
keytool -genkey -alias tomcat -keyalg RSA -keystore C:\tmp\.keystore
```
パスワードの入力などを求められるが、適当なものを入力する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/204260/f9d3b544-44e0-8524-3aa2-36cfec2788ef.png)
####6. keystoreファイルの作成結果を確認する。
「C:\tmp」フォルダ配下に「.keystore」ファイルが存在することを確認する。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/204260/2f38e35e-6ebe-7c13-a370-d0d8d23b5632.png)