OmniAICreator/Qiita-1.07M huggingface dataset:
GitHub CLI 完全に理解した

やぬてつです。

この記事は [完全に理解したTalk Advent Calendar 2020](https://qiita.com/advent-calendar/2020/easyeasy) の12日目の記事です。GitHub CLI を何に使ったら良いか迷っている方の一助になればと思い書きました。

みなさん GitHub CLI ご存知ですか？CLI好きにはたまらない機能ですよね。
でもですよ。正直言ってWebでもいいですよね。操作慣れてますし。
そんなことを考えていたら結局WebでIssueを大量作成するPM作業が日々続いていました。

私のプロジェクトでは一つの課題につき3つのIssueを作成しています。メインである課題、テストレビュー、テストの3つです。これをスプリントごと7課題〜10課題、つまり多い時で30Issue作成します。

1つの課題につき3つ作るだけでも面倒くさいし、定型文章も多いので、マウスによるコピペが大量に発生します。テンプレートを利用していてもめんどくさいです。これは自動化したいですね。

そこでCLIですよ。


# 早速使ってみる

今回はIssueを作成するコマンドを作成します。
Issueを作成するコマンドは以下です。

```zsh
(///ᴗㆁ✿) < gh issue create
```

タイトルを追加します。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した"
```

本文を追加します。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した" --body "本文完全に理解した"
```

ラベルを追加します。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した" --body "本文完全に理解した" --label complete
```

プロジェクトを追加します。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した" --body "本文完全に理解した" --label complete --projects understand
```

マイルストーンを追加します。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した" --body "本文完全に理解した" --label complete --projects understand --milestone asap
```

### ３種類を並べてみる

私のプロジェクトでは3つを同時に作成するので、タイトルと本文、タグを変更して作成します。
作成してまとめたものが以下になります。

```zsh
(///ᴗㆁ✿) < gh issue create --title "タイトル完全に理解した" --body "本文完全に理解した" --label complete --projects understand --milestone asap

(///ᴗㆁ✿) < gh issue create --title "テストレビュー_タイトル完全に理解した" --body "テストレビュー完全に理解した" --label test_review --projects understand --milestone asap

(///ᴗㆁ✿) < gh issue create --title "テスト_タイトル完全に理解した" --body "テスト完全に理解した" --label test --projects understand --milestone asap
```

### シェルスクリプトでいい感じにする
これを実行しただけではコピペがまだ必要な状態でミスが発生してしまいます。
シェルスクリプトでゴニョゴニョしていい感じにしましょう。
出来上がったのがこちら。

```zsh
project=understand
milestone=asap

title=$1;
main_body=$2;

main_title="${main_body##*/}_${title}"
log=`gh issue create --title $main_title --body $main_body --label enhancement --project $project --milestone $milestone`

review_title="テストレビュー_${main_title}"
review_body="テストレビュー完全に理解した　#${log##*/}" # メインIssue番号を取得
gh issue create --title $review_title --body $review_body --label test --project $project --milestone $milestone

test_title="テスト_${main_title}"
test_body="テスト完全に理解した　#${log##*/}" # メインIssue番号を取得
gh issue create --title $test_title --body $test_body --label test --project $project --milestone $milestone
```

タイトルと簡単な本文だけで、定形文付きで3つのIssueを作成することができます。

```zsh
(///ᴗㆁ✿) < zsh complete_unserstand_github_cli.sh "GitHub CLI 完全に理解したタイトル" "GitHub CLI 完全に理解した本文"
```

CLIの無限の可能性を感じますね。

大量のIssue作成にぜひ GitHub CLI を利用してみてください。

# まとめ
**GitHub CLI (issue create) の使い道完全に理解した！**
AWSを全社導入しようとすると上司からよく訊かれること

こちらは[Japan APN  Ambassador Advent Calendar 2020](https://qiita.com/advent-calendar/2020/japan-apn-ambassador)の12日目のエントリです。
 
こんにちは、キクタです。 
クラウドアドバイザリーコンサルタントをしています。
ビジネス＆テクノロジー両方の観点から、AWSを導入したい・導入したお客様の支援をしています。

なお、本ブログ内容は私個人の意見です。所属企業の見解を示すものではございませんので、予めご了承下さい。

#「パブリッククラウドの比較資料はないですか？」

さて、最近は少し減少傾向ですが
「パブリッククラウドの比較資料はないですか？」
というご相談をしばしばいただきます。

AWS導入時あるあるでして、AWSを全社導入したい！というと、
「他とは比較したのか」
と、上司から訊かれたり、そもそも市場調査結果を稟議書に添付しなければならなかったりするからなんですね。


#ベンダーは自社に都合良いことしか言わない?<BR>よろしい、ならば、アナリストレポートだ！

AWSが公開している[アナリストレポートページ](https://aws.amazon.com/jp/resources/analyst-reports)があります。

 AI, IoTなどバズワードから、インフラストラクチャ、DBなどの定番ものまで、グローバルなトップアナリスト会社のレポートが数多く公開されています。冒頭のお話であれば、インフラストラクチャでフィルターをかければOKです。

「AWSが公開しているということは、自社に有利だからじゃないの？」

という疑問も湧いて当然ですが、同様のアナリストレポートは他のクラウドベンダ（例えばG社など）でも提供しています。

アナリストレポートには市場環境から、各サービス提供ベンダーの得手不得手、今後のフォーキャストまで、一通りのネタは揃っているので、きっと上役の方に「おー！」と感心していただけること、間違いなしです。

クラウド導入の企画書、稟議書作成で悩んでいる方、是非参考にしてみて下さい。


#整理が進んできたクラウド市場

調査レポートをねちっこく時系列に沿って読んでいくのが割と好きでして、
ことIaaSに関しては調査対象の会社が年々減っているのがよくわかります。
調査会社によって異なるのですが主要プレイヤーの数は、概ね下記の通りです。

2015年頃：15〜20社
2020年: 5〜7社

トップは変わらずAWSで、続いてMicrosoft, Google、とここまでは変わらないのですが、3分の２のプレイヤーが脱落し、
ハイパースケールプロバイダーのみ生き残っている、つまり市場の成熟期に入ったということがよく分かります。


#「どのクラウドを選ぶべきか」の次の問い

お客様からのリクエストも、「どのクラウドを選ぶべきか」という相談は冒頭で述べた通り少し減って、
代わりに最近は「AWSを事業の中でどう活用していくにはどうするべきか」という相談を多くいただくようになりました。

#ビジネスとテクノロジーの２軸をつなぐ人材育成にむけて

その相談に対しては、ビジネスとテクノロジー、どちらの言葉もわかる人材育成が鍵になると考えます。
ビジネスだけ、テクノロジーだけ、それぞれ専業化した状態でイノベーションは生まれません。

それを実現する一つの解は [Well-Architectedフレームワーク](https://aws.amazon.com/jp/architecture/well-architected)にあります。
AWSのベストプラクティス集という位置づけで、Techな要素も多く書かれているのですが、意外にもクラウドネイティブな組織、カルチャーを醸成するにはどうすれば良いかという示唆も多く含んでいるのが特徴です。よって、TechメンバーがBizサイドの架け橋になるために有効な教材と考えます。

AWSというと新たなサービス、新たな技術に目が行きがちですが、 CIOも納得な視座の高い情報を多く発信しているところもまたエンタープライズで好まれる理由なのかな、と思ったりします。

では、また。
Next.js v9+でPWAに対応させる

## はじめに
Next.jsで簡単にPWAに対応させることができると言う文献をいくつか見たので勉強がてら実際にやってみました。
たくさんハマりましたが最終的にたどり着いたので共有したいと思います。

※ NuxtじゃなくてNextです。

## 環境
- Next.js v10.0.3

## 結論
Next.js（v9）以上の場合、[next-offline](https://www.npmjs.com/package/next-offline)より[next-pwa](https://www.npmjs.com/package/next-pwa)のほうが簡単

## 方法

### 1. アイコン & public/manifest.jsonを作成
https://qiita.com/shisama/items/d4d0b24980beaea57231
これを参考に作成しました。

注）アイコンのどれか一つにmaskable iconを指定してあげなければいけません。
もしもmaskable iconが指定されていない場合、android端末ではアイコンの周りが白色になってしまいますが、maskable iconが指定されていると、枠いっぱいにアイコンを表示させることができます。
→https://web.dev/maskable-icon-audit/?utm_source=lighthouse&utm_medium=devtools

### 2. Headタグに追加
以下を追加します

- 「1.」で作成したmanifest.json
- theme-color
- apple-touch-icon

```React
// pages/_document.tsx
class CustomDocument extends Document {
  render(): JSX.Element {
    return (
      <Html lang="ja-JP">
        <Head>
          <link rel="manifest" href="/manifest.json" />
          <meta name="theme-color" content="#68d391" /> {/*  好きな色を入力　*/}
          <link{/*  アイコンを選択　*/}
            rel="apple-touch-icon"
            sizes="180x180"
            href="/icon-192x192.png"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}
export default CustomDocument;

```

### 3. next.config.jsに以下を追加

https://github.com/shadowwalker/next-pwa/tree/master/examples/next-9
まさにここを見て実装すると一瞬なのですが、next.config.jsに

```js
const withPWA = require('next-pwa')

module.exports = withPWA({
  pwa: {
    dest: 'public'
  }
})
```
と入力するだけ。

![スクリーンショット 2020-12-12 0.42.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/600371/34bf39d3-d986-b41e-e55b-1b6649359858.png)

chromeの右上の三点リーダーをクリックして、
「**」をインストール...
と表示されると成功です。

## 備考
Next.js v9以上から/publicから静的ファイルを取得することができるのでそれらの静的ファイルを提供するためだけのカスタムサーバーを記述する必要がなくなったためにnext-pwaでは簡単に実装できるそうです。
（https://github.com/shadowwalker/next-pwa/tree/master/examples/next-9）

##　（番外編）next-offlineを使ってPWAを適応させようとしてみましたが、、

next-pwaではなく、next-offlineを使ってPWAを実現する方法を調べたところ、自前でカスタムサーバーを記述する必要があり、server.jsを追加しなければいけないとのことでした。
しかし、Next.jsのリファレンスによると、
>A custom server will remove important performance optimizations, like serverless functions and Automatic Static Optimization.

server.jsを追加しカスタムサーバーを記述するとSSGができなくなることがあるそうです。

さらに個人的にローカル開発環境でssl接続ができず、next-offlineではPWAが確認できなかったため、next-offlineでのPWAを断念しました。





### 参考文献
- Next-pwa （Next.jsv9+）
    - https://github.com/shadowwalker/next-pwa/tree/master/examples/next-9
- リファレンス
    - https://github.com/shadowwalker/next-pwa
【Linux】if文の評価式でリモートサーバの特定のファイル有無を評価する方法

#はじめに
お疲れさまです。
if文の評価式でリモートサーバの特定のファイル有無を評価する方法に地味に苦戦したため、誰かの助けになるかはさておき、残すことにしました。
OSはRHEL7を想定しています。


#本題

sshコマンドは以下のように実行すると、リモートサーバ上でコマンドを実行できます。

```
[user@host001 /tmp]$ ssh [ユーザ名]@[ホスト名] "ls -l"
```
そしてこの場合に出力され得る戻り値は以下の通りです。

|戻り値|状況|
|:---:|:--|
|0|sshコマンドとlsコマンドが正常終了した|
|1 or 2|ls -lコマンドがエラーとなった|
|255|sshコマンドがエラーとなった|

上記の表だけでは説明不足かもしれませんが、sshコマンドが異常終了した場合は、戻り値として「255」が返ってきます。
一方でリモートで実行したコマンドが異常終了した場合は、そのコマンドの戻り値が返ってきます。

今回はこの仕様を使用して、リモートサーバの特定のファイル有無を評価します。
結論は以下の通りです。

```
if [ `ssh [ユーザ名]@[ホスト名] "test -e [リモートファイル名]"` -eq 0 ]; then
 処理
fi
```

詳細は控えますが、「test -e [ファイル名]」でファイルの存在有無を評価可能であり、ファイルが存在する場合は戻り値は「1」が返ってきます。sshでそれをリモートサーバ上で実行するだけです。
ただしこの方法だとsshコマンド自体が異常終了となった場合もファイルが存在しないのと同じ扱いになります。

sshのエラーも検知しつつこれくらいシンプルな方法があれば知りたいものです。
ショートカットキーでクリップボードのWindowsパスをLinuxパスに一発変換

# はじめに
Windowsユーザーのみなさん、git bashやWSLを使うことが多くなった昨今、すっかりコマンドプロンプトでDOSコマンドを打つよりLinuxコマンドを打つ方が多くなってませんか？
そこで困るのがWindowとLinuxのパスの違いですよね！

そんなあなたにオススメの便利な技を紹介します！

# 準備

* 下記のbatファイルをどこかに作ってやります。

```bat:win2LinuxPath.bat
Powershell get-clipboard | "C:\Program Files\Git\usr\bin\perl" -p -e "s!\\\\!/!g;s!^(\w):!/$1!;$_=lc $_" | clip
```
**※ Perlを使うため、Git for Windowsインストール済み前提です**

* デスクトップにショートカットを作ってショートカットキーを割り当てます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68254/fcf91241-6a2a-8834-4c3e-e7defc605b63.png)

# 使い方
* エクスプローラのアドレス部分などから変換したいパスをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68254/ee8727d6-ba87-11a6-792d-0db71143b5d8.png)
* ショートカットキー（今回はCtrl＋Alt＋P）を押します。
    * 一瞬黒い画面が現れます
* どこか貼りつけると、ほらこの通りWindowsパスがLinuxパスに変換されてます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68254/21a6ee23-9678-ecdd-6411-6a1303dec41a.png)

# おわりに
.batファイルをちょっと修正すれば、共有サーバのパスをMacユーザでもアクセスできるように変換してチャットに貼り付ける時とかにも使えるよ！

# 追記
perlで頑張ってしまいましたが、git bashには便利なコマンドが用意されていました(^^;

```bat:win2LinuxPathCyg.bat
Powershell get-clipboard | "C:\Program Files\Git\usr\bin\cygpath" -u -f - | clip
```
wsl も `wsl wslpath`を使えばできそうですが、なんかうまくいかなかったです。。
Web Video Player, VideoJSの紹介

##はじめに
VideoJSはウェブ上のビデオプレイヤーです。
公式サイト: https://videojs.com/
OSS Repository: https://github.com/videojs/video.js

公式サイトによりますと450,000サイト以上が利用されています。
この記事を記録際にはVideoJSのGithubでは30.1KStarを持って、他プレイヤーに比べれば一番最多と見られます。


##HTML5ビデオプレイヤーはもうすでにあるのに、どうしてVideoJS使いますか？
確かに、HTML5には`<video>`タグが追加されて、あれを使えばいいでしょうという課題ができますね。
VideoJSを利用する理由はこちらです：

* **スタイリング**

ブラウザーによって、ビデオコントロールパネル、ボタン、 Seekbarなどのスタイリングは違っています。
以下の例をご覧ください

Google Chrome
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914725/d1c96adc-7985-16bc-7b28-239ad3562472.png)

Mozilla Firefox
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914725/dd63cf00-004b-f2d9-028d-a1ae98f09d24.png)

Safari
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914725/8ff87ed5-3607-aab6-871b-f11e4ac74038.png)

三つのメージャーブラウザー上にNative HTML5のビデオスタイリングでした。見るとだいぶ違っていますね。　
UX的にはこれは良くない、ユーザー用のドキュメントを作成するも面倒になるでしょう。

VideoJSではどんなブラウザーであっても、Stylingは一緒です。
そのスタイリングでも好みに応じて編集することが簡単にできる。

* **Adaptive Bitrate Streaming対応**
VideoJSはAdaptive Bitrate Streaming ProtocolというHLSとMPEG-DASHを提供しています。

その二つの中にHLS streaming Protocolは一番人気とみられていますがPCブラウザーでのNative Supportは現在、Safariしか持っていません。
そのため、追加開発が必要になってしまいます。

VideoJS Version 7以上にはPluginなしでHLS利用することができます。このライブラリのおかげで
https://github.com/videojs/http-streaming

VideoJS Version 6には手動で導入することができます。


##問題
どんな良くても、製品としては問題は確かにあります。今回はバグの話じゃなくて、他の問題を記事したいと思います

* **ドキュメンテーション**

ドキュメンテーションはこちらで見ることができますhttps://docs.videojs.com/
問題はいかにリストします

1. 英語版のみ
videojs自体はinternationalizationがサポートしますが、開発者用のドキュメントは英語だけしかない。
全く英語わからない方は翻訳サービスに頼ることになります

2. 不完全
たまに、GithubでIssueを上げてからものがわかることがあります。
わからない時にも、内部コードを見るしかない時もあります
例: https://github.com/videojs/video.js/issues/2730

3. 古いバージョンのドキュメンテーションがない


##代わり
OSS Video PlayerはVideo.jsだけではありません。他のOSSプロジェクトもあります。
他のプロジェクトはGithub Star順で記載します

* **Plyr**

公式サイト: https://plyr.io/
OSS Repository: https://github.com/sampotts/plyr
Star: 18.7K

* **MediaElement.js**

公式サイト: http://www.mediaelementjs.com/
OSS Repository: https://github.com/mediaelement/mediaelement
Star: 7.5K

* **jPlayer**

公式サイト: http://jplayer.org/
OSS Repository: https://github.com/jPlayer/jPlayer
Star: 4.6K


##参考
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video
* https://developer.mozilla.org/zh-TW/docs/Web/HTML/Supported_media_formats
* https://blog.bitsrc.io/5-open-source-html5-video-players-for-2018-38fa85932afb
助けて、もなふわすい～とる～む！って叫んだらツイートするようにしてみた

この記事は、[もなふわすい～とる～む Advent Calendar 2020](https://adventar.org/calendars/5183)の12日目です。

11日目の記事は[千代原もずめさんの後輩巻乃もなか概念SS](https://www.pixiv.net/novel/show.php?id=14183643)でした。
正直に申し上げますと、今までVの者のSSって避けていた節がありました。空想なので。しかし、どうしても後輩巻乃さん概念という魅力には抗えず、読んでしまいました。良かった、こんな後輩が欲しかった...！(工業高校卒です)

#「たすけて、もなふわすい～とる～む！」って叫んだらツイートするようにしてみよう！
当初考えてたネタがボツになりまして、そのときの精神状態が「たすけて、もなふわすい～とる～む！」だったわけですね。
開発やっているとそれはもう意図しないところで変なバグ踏むわ、忙しいときに限ってタスクが舞い込んでくるやら...
そんなとき叫んだらそれを検知してツイートしたらいいんじゃね？って思いつきました。謎ですが許して...

##技術選定
あまり時間がない(ボツになった時点で1週間前くらい）ので手っ取り早く実現できる方法を取っていきます。

まずは音声認識を行う方法を検討しました。自分が今まで使ったことがあり、今回も時間かからずかつ精度高くできそうな方法は2つ。
google音声認識と、大語彙連続音声認識エンジンJuliusです。

まずはgoogle音声認識で「たすけて、もなふわすい～とる～む！」って認識させてみましょう。chromeでgoogleドキュメントを開くと音声認識が使えるのでそこから試します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/a0b1dc58-0b5c-e4c3-61fe-82ec719c79aa.png)
...うーん、予想より高い確率ですが「助けてもなふわスイートルーム」と認識されます。googleに認知されてる巻乃さん強い。
しかし何回か別な単語として拾ってしまいます。最後の「助けてお腹空いてる女」は笑ってしまいました。

次はjuliusのデモを動作させてみました。その結果がコレ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/83116267-fd48-b1de-2def-df1a238d8f34.png)
助けてまでは良いのですが、もなふわすい～とる～むが壊滅的です。しかしjuliusなら辞書を作ることができるので、それを試してみます。
その結果がこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/db742d77-0f92-06ea-b506-3e0049b43e20.png)

デモで動作させていたときと表示が変わり、パラメータがいくつか表示されるようになりました。ここで注目するのはscore1の値です。この値が大きければ大きいほどそのキーワードとして認識されたということです。全く別な単語を喋るとそもそも認識されないですし、特定のキーワードを検知するという点では数値化できるのはあとでしきい値で調整が効くのでjuliusを使うことにしました。

次はツイートする方法。
twitterのAPI使うのがだいぶ面倒になっているのでIFTTTを使うことにします。curlから叩くだけでツイートできるのでお手軽ですね。
その他に制限が緩くできる方法としてseleniumを使ってchromeを自動操作化させる方法も考えましたが、今回はIFTTTで十分です。
制限かかるまで知らなかったのですが、IFTTTのツイート数の制限が100回/Dayと思ってたら25回/Dayになってたのですね。投稿動画撮ろうと何回か失敗したらツイートできなくなってて焦りました...。

構築する方法は以下３つの記事に分割して上げましたので興味があればどうぞ。

[ラズパイ4でjulius 4.6をセットアップ](https://qiita.com/en129/items/534781cd3616d87ede78)
[julius 4.6でキーワードの組み合わせを認識させる](https://qiita.com/en129/items/31b0f67c1d886df2aef6)
[pythonとjuliusとIFTTTを組み合わせて特定フレーズでツイートさせる](https://qiita.com/en129/items/f8801723e3632b6c2085)

##構築した結果...
ついでに音声が認識されたら巻乃さんボイスを再生するようにしました。ランダムです。進捗どうですか？も再生されます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">叫んでツイートされる様子です<br>（注意　一般成人男性の声がします） <a href="https://t.co/53ed3Pj7fM">pic.twitter.com/53ed3Pj7fM</a></p>&mdash; エン.129 (@en129) <a href="https://twitter.com/en129/status/1337417329546678273?ref_src=twsrc%5Etfw">December 11, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

これで叫べば巻乃さんにしゅたっ！ってしてもらえるかもしれなくなりました！

##おまけ
試した結果を画像だけならバレへんやろって思ってツイートしたら捕捉されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/0ce3ecfb-3c65-376e-1c66-925cfbab92b0.png)
AndroidTestをFastlaneで実行する

Androidアプリのインストゥルメント化単体テスト（ハードウェア デバイスまたはエミュレータで実行されるUIを使用したテスト）をCI環境に組み込むためにFastlaneから実行する方法のメモ。

## 背景
AndroidStudioからの実行はandroidTestディレクトリのコンテキストメニューから実行するという非常に直感的でわかりやすいが、完全に自動化しようとすると下記のステップを自動化する必要があり、自前では意外と簡単には行かなかった。

* エミュレータを起動
* 起動完了を待つ
* テストを実行
* テスト終了を待つ
* エミュレータを終了

## 前提
* Androidアプリ開発に必要なツールは一通りインストール済み
* Fastlaneを既にプロジェクトで使用している


## TL;TR
[AzimoLabs/fastlane-plugin-automated-test-emulator-run Fastlaneプラグイン](https://github.com/AzimoLabs/fastlane-plugin-automated-test-emulator-run)を使用することで、設定のみで実現できてしまう。

## 手順
プラグインのインストール

```
fastlane add_plugin automated_test_emulator_run
```

プロジェクトのfastlaneディレクトリにAVD_setup.jsonを作成する

```json
{
  "avd_list":
  [
    {
      "avd_name": "Nexus5_API_21_Play",

      "create_avd_package" : "system-images;android-21;google_apis;x86_64",
      "create_avd_device" : "Nexus 5",
      "create_avd_tag" : "google_apis",
      "create_avd_abi": "x86_64",
      "create_avd_hardware_config_filepath": "",
      "create_avd_additional_options": "",

      "launch_avd_snapshot_filepath": "",
      "launch_avd_launch_binary_name": "emulator",
      "launch_avd_port": "",
      "launch_avd_additional_options": "-gpu on"
    }
  ]
}
```

`create_avd_device` ではハードウェアプロファイルを指定する。AVD Manager GUI版では下記スクショの画面で選択するものにあたる。
![Screen Shot 2020-12-12 at 0.00.52 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138322/924d8fae-b2ab-681d-2c5c-7bb7c3c21535.png)

`avdmanager list device` コマンドで表示されるリストのIDを指定する。

```shell
% ~/Library/Android/sdk/tools/bin/avdmanager list device
...
---------
id: 14 or "Nexus 9"
    Name: Nexus 9
    OEM : Google
---------
id: 15 or "Nexus One"
    Name: Nexus One
    OEM : Google
---------
id: 16 or "Nexus S"
    Name: Nexus S
    OEM : Google
---------
id: 17 or "pixel"
    Name: Pixel
    OEM : Google
---------
id: 18 or "pixel_c"
    Name: Pixel C
    OEM : Google
---------
id: 19 or "pixel_xl"
    Name: Pixel XL
    OEM : Google
```

で表示されるリストのIDを指定する。
リスト中の
`id: 8 or "Nexus 5"`
のところ。

その他の詳細はこちら
https://github.com/AzimoLabs/fastlane-plugin-automated-test-emulator-run#json-config

fastlane/Fastfileに下記の様にlaneを定義。

```ruby
lane :run_android_test do
  # Delete existing emulator data to avoid failure of starting emulator
  sh("rm -rf ~/.android/avd/Nexus5_API_21_Play.avd")
  automated_test_emulator_run(
    AVD_setup_path: "fastlane/AVD_setup.json",
    gradle_task: "connectedDevelopmentDebugAndroidTest",
    gradle_flags: "-Pandroid.testInstrumentationRunnerArguments.class=com.smbc_card.vpoint.ui.email.EmailActivityTest",
  )
end
```

`AVD_setup_path`に上記で作成したjsonファイルを指定する。

`gradle_task`で指定するタスクは`connectedVariantNameAndroidTest`となる。つまり、フレーバーが`Development`でビルドモードが`Debug`なら `connectedDevelopmentDebugAndroidTest`となる。
[公式ドキュメントはこちら](https://developer.android.com/studio/test/command-line)

`gradle_flags`は必須ではないが、例では実行するテストクラスを指定している。
[ここからの情報](https://github.com/googlesamples/android-testing-templates/tree/master/AndroidTestingBlueprint#custom-gradle-command-line-arguments)


`sh("rm -rf ...")` は前回作成されたavdのディレクトリが残っているとエラーになることがあったのだが、原因までは調べる時間なかったので、毎回削除することで回避している。

下記コマンドで実行

```
bundle exec fastlane run_android_test
```

Happy testing~!
[JavaScript]作業効率を上げるためにデバックを身に付けよう

#はじめに
JavaScriptでデバックする方法を記載しました。`console.log`の一つでデバックを乗り切っていたのですが、ほかの方法も知りたいと思い記事を書きました。今回の記事では、ブラウザでブレークポイントを設定する方法は省略しています。詳しく知りたい方は、下記の記事を参考にするとよいと思います。


[ブレークポイントを使ったJavaScriptデバッグを整理してみた【再入門】](https://qiita.com/nekoneko-wanwan/items/02aed17773833c3ad3b2)

#console API一覧
デバックで役に立つconsole APIメソッド一覧になります。

|メソッド| 意味 |
|:------|:------|
|console.assert()|条件付きでエラーメッセージを出力します   |
|console.clear()|コンソール画面をクリアする |
|console.count()|引数の値が呼び出された回数を出力します|
|console.countReset()|引数のカウンターの値をリセットします|
|console.dir()|指定したオブジェクトが持つプロパティの一覧をリストで出力します   |
|console.dirxml()|指定したオブジェクトをXML/HTML要素で表示します|
|console.error()|エラーメッセージを出力します|
|console.group()|インデントを付けて出力することで階層構造を持たせます(groupEnd()で終了する)|
|console.info()|メッセージタイプのログ情報を出力します|
|console.log()|一般タイプのログ情報を出力します|
|console.table()|指定した配列やオブジェクトなどの構造をテーブルにして出力します|
|console.time()|time()～timeEnd()までの間にある処理時間を計測する   |
|console.trace()|呼び出し元などを記録した実行過程を出力します|
|console.warn()|警告メッセージを出力します|

#throw new Error()
**ソースコードの文法的エラーではなく、特定の条件時をエラーとして扱いたときがあります。**
そのような場合に、throw new Error()でエラーを投げることが出来ます。
throw文でもエラーを投げることは可能ですが、Errorインスタンスを投げるのが定石らしいです。Errorのインスタンスには、`message`プロパティと`name`プロパティがあります。

* message　⇒　エラー内容について
* name　⇒　Errorクラスのオブジェクトクラス
<br>参考：[JavaScript初級者から中級者になろう](https://uhyohyo.net/javascript/9_8.html)

利用シーンは関数に不正な値が入力された場合や、APIの戻り値が意図せぬ値になった場合などです。サンプルコードとして、引数が数値以外ならエラーを発生させる処理を載せました。

```js
function allowNumber(val) {
  if(typeof val !== 'number') {
   throw new Error(`${val}は数値ではありません。`);
  }   
}

allowNumber('カレー');
```
#try...catch
try...catchは異常エラーが発生したときに別の処理を実行させることが出来ます。
**エラーが発生しても処理を止めたくないときや別処理を実行させたいときに有効です。**

```js
const num = 10;
try {
    //　例外が発生する可能性のある処理を記述
    num = 20;
} catch (error) {
    // 例外をキャッチしたときに実行される処理を記述
    console.log("エラーが発生しました。");
} finally {
    // 例外の発生有無に関係なく実行される処理
    console.log('ラーメン');
}

console.log("すし");
```

#おわりに
これを機会に、console.log以外も使用してみたいと思います。
OCaml Upcoming Changes 2020: let punning

この記事は[ML Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ml) 12日目の記事です。

OCamlにそろそろ入りそうな機能を紹介します。

今回は、次の次のOCaml 4.13で入りそうな [Syntax proposal: let punning by stedolan · Pull Request #10013 · ocaml/ocaml](https://github.com/ocaml/ocaml/pull/10013) の紹介をします。

---

以前はOCamlでモナディックなコードを書こうとすると `fun` と `>>=` で頑張らないといけなかったが、OCaml 4.08.0 で [binding operators](https://ocaml.org/releases/4.11/htmlman/bindingops.html) が入ったことでふつうのコードのような見た目で書けるようになった。[^ppx-let]

[^ppx-let]: [ppx_let](https://github.com/janestreet/ppx_let)とかの話はひとまず措く。

binding operatorsがない時のコード（例は[All About Monads](https://wiki.haskell.org/All_About_Monads#Example_2)による）:

```ocaml
(* 下準備。よくあるbind演算子 *)
module Option_ops = struct
  let ( >>= ) x f = Option.bind x f
end

let mother's_paternal_grandfather s =
  let open Option_ops in
  mother s >>= fun m ->
  father m >>= fun gf ->
  father gf
```

binding operatorsがある時のコード:

```ocaml
(* 下準備。 + をApplicative, * をMonadにする慣習 *)
module Option_syntax = struct
  let ( let+ ) x f = Option.map f x
  let ( and+ ) x y =
    match x, y with
    | Some x, Some y -> Some (x, y)
    | _, _ -> None
  let ( let* ) x f = Option.bind x f
  let ( and* ) = ( and+ )
end

let mother's_paternal_grandfather s =
  let open Option_syntax in
  let* m = mother s in
  let* gf = father m in
  father gf
```

今回紹介するlet punningは、さらに、 binding operatorの左辺と右辺が同じ場合、右辺を省略できるようにする。

```ocaml
let liftA2_opt f x y =
  let open Option_syntax in
  let+ x and+ y in
  f x y
(*
let listA2_opt f x y =
  let open Option_syntax in
  let+ x = x
  and+ y = y in
  f x y
と同じ意味
*)
```

これは、レコード式などで、フィールド名と変数名が同じ場合に `{ x = x }` を省略して `{x}` と書けるrecord punningという機能に見た目が似ているため、 let punning と呼ばれている。

この構文を導入する動機は以下のような要因があると思う。

1. `f <$> x <*> y` のような演算子を使う方法は、OCamlでは優先順位がいい感じにならないことが多いので、多引数関数の持ち上げが面倒臭い（ユーザーが演算子の優先順位を決められない）
2. OCamlには型クラスがないので、語彙を増やそうとするとファンクタ頼りになって構文的に重い（上記の`liftA2_opt`もbinding operatorを定義したモジュールを受け取るファンクタを定義して他の型にも対応したいところ）[^modular-explicits]
3. 1., 2. から binding operator だけ用意しておいてベタ書きする機会が多い
4. 慣習的にshadowingが好まれるので、文脈に包まれた値と文脈内の値の変数を同名にしがち（誤解がなければ）

[^modular-explicits]: [Modular explicits](https://github.com/ocaml/ocaml/pull/9187)が入ればましになる？

提案段階ではbinding operatorでない素の `let` についても右辺を省略できるようにして、次のようなコードを書けるようにしようという話もあったが、さすがに気持ち悪いということでこちらは見送られた。

```ocaml
module N = struct
  open M
  let compare and equal and hash
(*
  上記2行で↓と同じ意味
  let compare = M.compare
  and equal = M.equal
  and hash = M.hash
*)
end
```

また、 binding operator 導入以前は拡張ノードを使って `let%lwt` のように ppx でモナディックな束縛を実現していた場合もあったので、こちらも同様に右辺が省略できるようになっている。
【PHP】PHPのバージョンを7.4にアップデートする（zshシェル）


#### 環境
macOS Catalina（10.15.5）
シェル：zsh
Homebrewは最新版


## 現在のPHPのバージョンを確認
下記のコマンドでバージョンを確認

```:ターミナル
$ php -v
```

## PHP7系を検索する
`Homebrew`コマンドを使って現在インストールができるPHPのバージョンを検索

```:ターミナル
$ brew search php@7
==> Formulae
php@7.2                    php@7.3                    php@7.4
```

## 最新のPHP 7.4 をインストールする
記事投稿時点で最新バージョンでかつ長期サポートなのは`7.4`です。
`Homebrew`経由でインストール

```:ターミナル
$ brew install php@7.4
```

## PATHを通す
`PATH`を通す必要があるので下記コマンドを実行

```:ターミナル
$ echo 'export PATH="/usr/local/opt/php@7.4/bin:$PATH"' >> ~/.zshrc
$ echo 'export PATH="/usr/local/opt/php@7.4/sbin:$PATH"' >> ~/.zshrc
```

## PHPを再スタート
下記コマンドでPHPを再スタートさせる

```:ターミナル
$ brew services start php@7.4
```

# 変更を反映させる

`source`コマンドで変更を反映させる

```:ターミナル
source ~/.zshrc
```

その後再度バージョンの確認をしアップデートできているかを確認

```:ターミナル
＄ php -v
PHP 7.4.13 (cli)
```

バージョンが`7.4`以上になっていれば成功。
Unity ShaderGraph レシピ #3 市松模様

#全集中！市松模様
![スクリーンショット 2020-12-11 0.45.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/f97c8f2b-1846-dca2-4541-f322ff496767.png)

#作り方
[#1シンプルなグリッド](https://qiita.com/Teppy/items/34d8ea7de87f94458ab2)より遥かにシンプルなシェーダーなのでこれを#1にすればよかったと思っています。ええ。

###1. Checkerboard

市松模様を作るためのすべての要素はCheckerboardノードに含まれています。
![スクリーンショット 2020-12-11 0.46.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/804d0917-9a9e-2f4a-d546-307212e55d19.png)

CheckerboardノードのoutをそのままColorに流すだけ。簡単！
Color A, B がそれぞれの模様の色になります。
Frequencyの値が模様の繰り返し回数になります。


Frequency = 1の場合
![スクリーンショット 2020-12-11 0.55.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/05f69b80-9672-88ce-1ac4-d7829b2b5bed.png)

Frequency = 3の場合
![スクリーンショット 2020-12-11 0.55.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198311/468aad1e-1446-9365-1a94-641581e4ae0f.png)

###完成！

---
その他のレシピは[ShaderGraphレシピ一覧](https://qiita.com/Teppy/items/73fc6141751a62f5265d)にまとまっています
【PHP(laravel)】Undefined property: stdClassのメッセージが出た時

PHPのstdClassは簡単に言うと、プロパティやメソッドが無いオブジェクトです。

その特徴を活かして使われることもありますが、
意図せず「Undefined property: stdClass」のメッセージが表示された場合は
定義していないオブジェクトを使っている可能性があります。



```.php
//クラス
class Aaa {
 public $word;
}
```

```.php
//オブジェクト
$a = new Aaa;//定義
$a->word = 'こんにちは';
echo $a->word; //出力:こんにちは

//定義していない変数$bを使うと
$b->word = 'こんにちは';
echo $a->word; //出力:Undefined property: stdClass
```

私の場合はlaravelでの開発中に意図せず出てきたので、
使っている変数を確かめてみると、メッセージが解消できました。
AI Platform Trainingで機械学習モデルの学習バッチを作った話

この記事は、MicroAd Advent Calendar 2020の12日目の記事です。

## はじめに
GCP上でバッチ処理を行うにはCloud Functions、Cloud Run、AppEngineなど色々と選択肢がありますが、これらにはタスク完了まで10~15分以内という時間制限があり、数時間・数日のオーダーである機械学習モデルのトレーニングには適していません。

一方でAI Platform Trainingは計算リソースが柔軟に変更でき、時間制限がなかったりと機械学習をするには最高の環境ですが、スケジューリング機能がないためバッチ処理には不向きです。

そこでこの記事ではCloud SchedulerとAI Platform Trainingを連携させ、機械学習モデルの学習処理を定期実行する方法をご紹介しようと思います。

## Cloud Schedulerの設定

とりあえず AI Platform Training のジョブ作成リクエストを投げるスケジューラーを組んでみます。

設定が必要なのは基本４項目。

- 頻度：cronで指定するバッチ処理の実行タイミング
- JobId： AI Platform Trainingに作成するジョブの名前
- masterType： 学習に使用するマシンタイプ
- masterConfig： 学習に使用するDockerImage（[詳しくはこちら](https://cloud.google.com/ai-platform/notebooks/docs/custom-container?hl=j!a)）

<img width="500" alt="scheduler_sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/870220/dfb3411a-544d-acfe-5290-790814856907.png">


しかしこれだけではうまく動きません。
この設定でも一度はAI Platformのジョブを作成できるのですが、２回目の実行では次のようなエラーが出てしまいます。

```json
{
  "error": {
    "code": 409,
    "message": "Field: job.job_id Error: A job with this id already exists.",
    "status": "ALREADY_EXISTS",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "job.job_id",
            "description": "A job with this id already exists."
          }
        ]
      }
    ]
  }
```

どうやらAI Platform TrainingのジョブIDは一意である必要があり、タスク実行毎に変更しなければいけないようです。

ですがCloud Scheduler単体ではリクエストを動的に生成する機能はないので、Cloud SchedulerだけでAI Platformをスケジューリングするのは無理なようです。

しょうがないので間にCloud Functionを経由してジョブIDにタイムスタンプを追加することで無理やりこの問題を解決しました。

実際に作ったCloud Functionはこんな感じです。Cloud Schedulerから送られてきたリクエストにタイムスタンプを追加するシンプルな構成になっています。

```python
import time
import json
import base64
from googleapiclient import discovery, errors


def create_job(event, context):
    """
    Triggered from a message on a Cloud Pub/Sub topic.

    Args:
         event (dict): Event payload.
         context (google.cloud.functions.Context): Metadata for the event.
    """
    project_id = 'projects/test-project'
    ml = discovery.build('ml', 'v1')

    payload = json.loads(base64.b64decode(event['data']).decode('utf-8'))
    print(payload)

    timestamp = str(int(time.time()))
    payload['jobId'] += '_' + timestamp
    request = ml.projects().jobs().create(parent=project_id, body=payload)

    try:
        response = request.execute()
        print(response)
    except errors.HttpError as error:
        print(error._get_reason())
```

スケジューラーの方もHTTPリクエストを投げるのではなくPub/SubでCloud Functionを呼び出すよう合わせて修正。

<img width="500" alt="scheduler_sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/870220/21977ddd-60f9-f8b9-5ce5-1e3af7a107f1.png">
新しいスケジューラを動かすと、、、

![スクリーンショット 2020-12-11 18.43.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/870220/b672d6fd-ff47-ca63-00b7-0eae91af1d5a.png)

Cloud Function経由でジョブが作成されているのを確認できました。
期待どうりタイムスタンプが付けられているので、ジョブIDが重複せず何度でもジョブを作成できます。


## ちなみに
今回利用した AI Platform Training のブラッシュアップ版である AI Platform（統合型）がリリースされました。まだプレビュー段階なのでリージョンで日本を選択できなかったりと制限が多く業務利用は厳しかったりもしますが、ディスク容量やOSなど計算リソースをより細かく指定できるようになっているようです。今後のアップデートに期待大ですね。
主成分分析

#主成分分析

主成分分析は、多変量データの持つ構造をより小数個の指標に圧縮する手法である。少数変数を利用した分析や２・３次元の場合の可視化が実現可能である。主成分分析は、変量の個数を減らすことに伴う、情報量の損失をなるべく小さくしたいという動機のもと行われる。
例えば、１００次元のデータを１次元や２次元に圧縮したいという時に用いる。１〜３次元まで落とすと可視化できる。
ポイントをまとめると
- 変量の個数を減らす＝次元を減らす
- 次元を減らす際は、情報量は落とさないようにする
- １次元・２次元・３次元まで落とすと可視化できる

####学習データ

```math 
x_i = (x_{i1},x_{i2},...,x_{im})∈R^m
```

####平均（ベクトル）

```math 
x̄ = \frac{1}{n}∑x_i
```
####データ行列

```math 
X̄ = (x_1 - x̄,...,x_n - x̄)^T ∈R^{n × m}
```
####分散共分散行列
```math 
∑ = Var(X̄) = \frac{1}{n}X̄^TX̄
```
####線形変換後のベクトル

```math 
s_j = (s_{1j},...,s_{nj})T = X̄a_j    (a_j ∈ R^m)
```

係数ベクトルが変われば線形変換後の値が変化する。そのため、線形変換後の変数の分散が最大となる射影軸を探索する必要がある。ポイントは、情報の量を分散の大きさと捉えることだ。
####線形変換後の分散

```math 
Var(s_j) = \frac{1}{n}s^T_js_j = \frac{1}{n}(X̄a_j)^T(X̄a_j) = \frac{1}{n}a^T_jX̄^
TX̄a_j = a^T_jVar(X̄)a_j
```
###ラグランジュ関数
目的関数に制約条件を追加する。つまり、制約付き最適化問題を解く。
まず、ラグランジュ関数をだ最大化する係数ベクトルを探索する（微分して０になる点）。

```math 
ラグランジュ関数 
E(a_j) = a^T_jVar(X̄)a_j - λ(a^T_ja_j - 1)
```
次に、ラグランジュ関数を微分して最適解を求める。
元のデータの分散共分散行列の固有値と固有ベクトルが、上記の制約付き最適化問題の解となる。
その解である射影先の分散は固有値と一致する。

```math 
Var(s_1) = a^T_1Var(X̄)a_1 = λ_1a^T_1a_1 = λ_1
```
分散共分散行列は制定地対照行列から固有値は必ず０以上・固有ベクトルは直行となる。

##寄与率
寄与率とは、第k主成分の分散の前文さんに対する割合のことである。

```math 
V_{total} = ∑λ_i
```
累積寄与率は第1ーk主成分まで圧縮した際の情報損失量の割合である。

```math 
c_k = \frac{λ_k}{∑^m_{i=1}λ_i}←第k主成分の分散 \\
r_k = \frac{∑^k_{j = 1}λ_j}{∑^m_{i = 1}λ_i}←第１ーk主成分の分散
```
基本情報技術者試験についてメモ

#基本情報技術者試験についてメモ

午前試験（150分）
全80問
テクノロジ系　　問1～50　50問<b>（多い）</b> 62.5点
マネジメント系　問51～60　10問　12.5点
ストラテジ系　　問61～80　20問　25点


午後試験（150分）
全11問のうち5問に解答
問1　　　　情報セキュリティ<b>（重要）</b> 20点
問2～問5　　ソフトウェアハードウェア、データベース、ネットワーク、ソフトウェア設計から3問
　　　　　　プロジェクトマネジメント、サービスマネジメント、システム戦略、経営戦略・企業と法から1問
　　　　　　2問選んで回答　15点、15点
問6　　　　データ構造とアルゴリズム<b>（重要）</b>　25点
問7～問11　プログラム言語問題（C/Java/Python/アセンブラ/表計算）から1問選んで解答　25点

合格基準
午前試験と午後試験で各60点以上

#今日の名言

（学問とは何かについて）貧乏人の暇つぶし

-立川談志
Amplify AuthでupdateUserAttributesでユーザ情報が更新されない

#問題
Amplify AuthでupdateUserAttributes（）を用いてユーザ情報を更新した。
awsコンソールで変更は確認できたが、currentAuthenticatedUser（）でデータを取得するとユーザー情報が更新されていない。

#解決策

currentAuthenticatedUser({ bypassCache: true })

でキャッシュクリアすると最新の情報が反映されました。
nginxのgzipについて

nginxのconfigでgzipを設定するとコンテンツがgzip圧縮される。
gzipされた場合はレスポンスヘッダーにContent-Encoding: gzipが表示される。
なお、gzip圧縮されるにはリクエストヘッダーにAccept-Encoding: gzipをつける必要がある。

```
$ curl -I -H "Accept-Encoding: gzip" localhost
HTTP/1.1 200 OK
Server: nginx/1.14.1
Date: Fri, 11 Dec 2020 14:45:39 GMT
Content-Type: text/html
Last-Modified: Mon, 07 Oct 2019 21:16:24 GMT
Connection: keep-alive
ETag: W/"5d9bab28-fd9"
Content-Encoding: gzip * これ
```

gzip圧縮により4.0kのレスポンスサイズが1.4kまで減った。
ハッカソンに参加したら1つしかコミットできなかった件

この記事はエイチーム引越し侍 / エイチームコネクトの社員による、[Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020](https://qiita.com/advent-calendar/2020/hikkoshizamurai-connect) 12日目の記事です。

どうも。エイチームコネクトのエンジニア、@fujimkです。好きなVSCodeのテーマはMonokai Proです。

先日、グループ横断でハッカソンが開催され、意気込んで参加したのはよかったもののエンジニアらしい貢献があまりできないまま終えてしまいました。同じ過ちを犯さないよう記事として残しておきたいと思います。

誤解を招く前に述べておきたいと思いますが、みんなでアイデアを出し合って形にしていくというのはとても楽しいのものでした。次の機会があれば是非参加したいと思っています。ハッカソンはいいぞ。

# グループ横断ハッカソンの開催
与えられた期間は約4週間。テーマは「在宅勤務によって生じているマネジメント課題の解決ツール」でした。
エンジニアになって約1年半、業務に慣れてきて自信がついてきた私は、普段の業務では使用しない技術を使って開発したい、普段の業務では関わらない人たちと開発したいという思いで参加することにしました。
私のチームはデザイナー2名、エンジニア3名の合計5名のチームでした。

# フルリモート・業務後に集まるということの難しさ
メンバー全員が物理的に離れているということもあり、Zoomに集まって進捗の共有等を定期的に行うようにしていましたが、普段の業務などでなかなか時間が合わず、基本的にSlackでやり取りをするようになりました。（全員がZoomに揃ったのは何を作るか決める際の1度だけだったかなと思います。）

# 圧倒的技術不足
私は他のエンジニアと比べて今回使用する技術に対する経験も浅いということもありサポートにまわることになりました。実装されたコードの確認と実際に画面を操作し問題がないか確認を行っていました。どのファイルが画面の対応しているのか。どういうデータが返ってきているのか。またそれをどうしたら確認できるのか。わからないことが多すぎてあかんなあという気持ちでいっぱいでした。

# 結果：1つしかコミットできなかった
動作確認の際にバグを見つけたのですが、発生箇所の特定からバグを解消するまでに時間がかかり、結果1つしかコミットできませんでした。

# どうしていればよかったか
## テキストベースでのコミュニケーション力の強化
普段からあまり文章を書かない私は、文章の推敲に時間がかかり、チャットでのやり取りを億劫に思っている節があるなと思いました。普段から分報を活用し、自分の思考や状況を文字に起こすことに慣れていく必要があるなと思いました。
また、最終日は全員チャットにいたため、Zoomに集まることを提案して質問しやすい環境を作れていればよかったな思いました。

## 技術面の強化
普段の業務では既存のシステムの改修がメインなため、新しい言語や技術に触れる機会は自分で作っていくしかありません。プライベートで色々な技術に触れて、ほぼゼロの状態から言語や技術を扱うことに慣れておく必要があるなと感じました。

# おわり
[Ateam Hikkoshi samurai Inc.× Ateam Connect Inc. Advent Calendar 2020](https://qiita.com/advent-calendar/2020/hikkoshizamurai-connect) 12日目の記事は、いかがでしたでしょうか。今回の件で自分のリモート下での働き方、コミュニケーションのとり方を考える良いきっかけになったとも思います。また、もっとこうしたら良いよ、自分もこういう失敗したよなど、コメントいただけると幸いです。

明日は@sho-hataさんが記事を書きます。お楽しみに！
【疑似乱数】Apple M1とMT19937再考

# MTよりSFMTの方が速いという幻想？

実際、MTの配布元がMTよりSFMTの方が2倍速いと標榜しているのだが、MTの方は、今時の乱数ライブラリインライン呼び出しに対応していないとか、配列への一括生成モードに対応してないとか、生成ルーチンそのものでないオーバーヘッドが大きいこともあるので、「だったら対応させれば速いんじゃないの？」ということである。

なお、この記事はApple M1の性能検証を兼ねているので [この記事](https://qiita.com/STDNUL/items/e1521852a8c7f9044827)なんかも目を通していただけると幸いである。

# 64ビット版MTはSIMDでも有効

SIMD化は少ないビット数の演算を複数並列で行うのでビット数が少ないほど恩恵が大きいと言われるが、結論からいうとMT19937-64はSIMDに向いている。
64ビットだから１回で使わないといけないこともないし、16ビットとして4つに分割して使ってもいい。そもそもそういうデータ均質性があるからこそのC++標準ライブラリ入りなのである。

もちろんSIMDで64ビット×2のパックド演算が頻出するが、AltiVecでは64ビット×２のSIMDをサポートしてないとかおそらくそういうことであれば、x86のSSE2以降とかARMのNEONではその範囲内でできるので、その点は問題にならない。

むしろAVX2の256ビットまで素直にスケールするポテンシャルを秘めている。
AVX-512もアラインメント不整合をどうにか我慢すればいける。
というかむしろ速い。

## 64ビット版なら配列アクセスにかかる定数が偶数（＝アラインメントが揃う）
というのも、なんのことはない、定数定義が偶数であるため、mt[i+MM] とかを並列にアクセスする際にアラインメント補正が不要なのである。mt[i+1]の部分はどう足掻いても奇数になるのでここは仕方がないが。

```
#define NN 312
#define MM 156
```

もしここが奇数だとミスアラインとなるため、NEON(ASIMD)ではVEXT*操作、SSEだとpalignrなどが必要になってきて、その分性能のロスになるのである（Intel AVX以降はロードユニットのサイクルが大きくなるがずれていてもロードはできる）。その点、156は4でも割り切れるので、256ビットSIMDまではアラインメント揃ったデータとしてロードできる。

## データの依存性が低い
SFMTと比べて直近のデータとの依存がない点も考慮したい。
SFMTは1つ前と2つ前の各128ビット値を参照してXORを取る。したがって、256ビットとか512とかに容易に並列化できない。
（SFMTの256ビット並列版は、特定のパラメータの時だけ限定して演算をショートカットして2並列動作させることができるインチキをしているが2つ前までは無理だった）

## とりあえずはコードだ

そんなわけでApple M1向けに書いてみたのがこのコードである。
https://github.com/magurosan/PRNG-Apple/blob/main/mt19937-64-neon.c

## なぜそこでインラインアセンブラ？

我ながら不思議なことをやっているので、まずこれの説明をしよう。

```
#if defined(__apple_build_version__) 
#define INSERT_IF_TRUE(a,b,mask)  __asm__ ("bit.16b %1,%2,%3" :"=w"(a) : "w"(a), "w"(b), "w"(mask))
#else 
#define INSERT_IF_TRUE(a,b,mask)  a = vbslq_u64(mask, b, a)
#endif
```

新命令はともかく既存命令であるが、明示的にインラインアセンブラで命令指定しているのは、vbslq_u64と書いたのをコンパイラがわざわざ **A and UPPER_MASK | B and LOWER_MASK** の3命令(AND+AND+OR)に勝手に展開するというあまりに "FUBAR" なコードを生成してくれたせい。clang君、今回ばかりは無能な働き者にも程がある。
グローバルな最適化を阻害するのであんまり使いたくないのだけどせっかくのハンドオプティマイズを台無しにされては怒り心頭なので今回は致し方ないとする。

Apple版clangだけがこうであるとは限らないがとりあえずこうしておく。

## MT19937-64でもSHA3命令はもちろん有効

[SFMT](https://qiita.com/STDNUL/items/e1521852a8c7f9044827)と同じく新命令のBCAXとEOR3がいい仕事をしてくれている。
やはり新たに書き起こしたSIMDを用いた配列への一括生成でのスコアが良く、SFMTと同じ50,000,000回分の生成に換算すると34.6msとなり、[先の性能改善版SFMT](https://github.com/MersenneTwister-Lab/SFMT/pull/7)とほぼ同等の生成速度となる。

```
% clang -O3 mt19937-64-neon.c -o neon_test 
% ./neon_test bench      
100000000 random numbers generation time                                                                                 
Original:       178.26 ms
NEON:           145.24 ms
NEON x2:         95.45 ms
NEON ARRAY:      84.76 ms

% clang -O3 -march=armv8.4-a+simd+sha3 -DHAVE_SHA3 mt19937-64-neon.c -o m1test 
% ./m1test bench 
100000000 random numbers generation time                                               
Original:            173.37 ms
NEON+SHA3:           111.67 ms
NEON+SHA3 x2:         86.29 ms
NEON+SHA3 ARRAY:      69.13 ms
```

Cortex-A77機で追試してみたらやっぱりMTは遅くでSFMTの方が安定して性能上がっていたので、性能向上はCPUの実装依存なのかもしれない。
Goのプロファイラ fgprof

[Go2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/go2) 12日目のエントリです。

fgprofというGoのプロファイラがあります。fgprofはサンプリング型のプロファイラで、On-CPUとOff-CPUのプロファイルを統合的に収集して分析できるという特徴を持ちます。
これにより、fgprofではCPUリソースを消費するワークロードとI/O（Didk, Networkなど）のワークロードが混在するアプリケーションで、両方の観点からボトルネックを分析するのに役立てることができます。

fgprofは次のようにプロファイリング対象のアプリケーションにHTTPハンドラを追加する形で組み込み、`go tool pprof`でプロファイリングの実行と解析ができます。

[GitHub - felixge/fgprof - Quick Start](https://github.com/felixge/fgprof/tree/fe01e87ceec08ea5024e8168f88468af8f818b62#quick-start)

```go
package main
        
import(
    _ "net/http/pprof"
    "github.com/felixge/fgprof"
)

func main() {
    http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
    go func() {
       log.Println(http.ListenAndServe(":6060", nil))
    }()

    // <code to profile>
}
```

```
go tool pprof --http=:6061 http://localhost:6060/debug/fgprof?seconds=10
```

Goの`net/http/pprof`パッケージでも同様の方法でプロファイリングを行えますが、`/debug/pprof/profiler`エンドポイントを利用するCPU ProfilerではOn-CPUの分析しか行なえません。Off-CPUの分析をする場合には別途`/debug/pprof/trace`を利用することができますが、fgprofのように統合的に扱うことはできません。
READMEの[The Problem](https://github.com/felixge/fgprof/tree/fe01e87ceec08ea5024e8168f88468af8f818b62#the-problem) に詳しく書かれていますが、fgprofはこの点についての課題感から生まれたツールのようです。


## fgprofの実装

fgprofが具体的にどのようにプロファイリングを行っているのか、また`net/http/pprof`とどのような点が異なるのか興味があったので実装を調べてみました。

fgprofではプロファイリング実行中に、バックグラウンドのGoroutineで[`runtime.GoroutineProfile()`](https://golang.org/pkg/runtime/#GoroutineProfile) を呼び出して全Goroutineのスタックトレースを取得する、ということを99/secの頻度で行っています。
`runtime.GoroutineProfile()`で取得できる情報には、CPU時間を利用して処理を行っているGoroutineのものも、I/O待ちで待機中のGoroutineのものも含まれるので、これを利用してOn-CPUとOff-CPUを統合的に収集し、分析可能にしています。

なお、`net/http/pprof`のCPU Profile(`/debug/pprof/profile`)では、内部的に`runtime/pprof`の[`StartCPUProfile`](https://golang.org/pkg/runtime/pprof/#StartCPUProfile) でプロファイルを収集していますが、ここでは実行中のGoroutineのスタックトレースしか収集されません。
このあたりはほぼREADMEの[Go's builtin CPU Profiler](https://github.com/felixge/fgprof/tree/fe01e87ceec08ea5024e8168f88468af8f818b62#gos-builtin-cpu-profiler) の受け売りですが、GoではIOが内部的にnon-blockingで処理されるため、IOを待機しているGoroutineはOSスレッドに割り当てられず実行中とならないため、`StartCPUProfile()`で収集できるプロファイルにOff-CPUの情報が載ってこないということのようです。


## 参考

- [GitHub - felixge/fgprof](https://github.com/felixge/fgprof)
- [fgprof The Full Go Profiler](https://github.com/felixge/talks/tree/master/fgprof)
- [net/http/pprof](https://pkg.go.dev/net/http/pprof)
- [runtime/pprof](https://pkg.go.dev/runtime/pprof)
- [Profiling Go Program](https://blog.golang.org/pprof)
- [Goのnet/http/pprofでCPUやMemoryをprofileする流れと内部実装](https://www.sambaiz.net/article/238/)
【Node.js Express】Express のミドルウェアについて

※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。

##Expressにおけるミドルウェアとは

Express.JSの根幹的な機構で、サードパーティのミドルウェアをNPMで簡単に組み込むこともでき、プログラマが独自のミドルウェアを追加して拡張することもできる。
Expressジェネレータが作成するスケルトンにもミドルウェアが各所で使われている。

##ミドルウェアの役割とは

Expressの処理の流れをざっくり言うとブラウザからHTTPリクエストを受けとり、何らかの処理をしてレスポンスを返す。
ミドルウェアは名前の通り、この流れの中間に位置する処理のまとまり。

複数の異なる処理において共通する処理が、中間にあるとする。
この一連の処理を、現実世界の飲食店での注文の流れに例えると

```
１　客が商品を注文する。

２　注文を聞いて、注文表に記載し、厨房に伝える。

３　注文内容から、個別の料理法で料理を作る。

４　客に料理を出す。

５　料金を受け取る。
```
このような流れがあったとする。

Expressの処理の流れに合わせると、

１の　「客が料理を注文する。」というのが「ルーティング」に合致する。ユーザーがアクセスしてきたURLにより処理が変わる（注文内容により、作る料理が変わる）

３の　「注文を受けて、個別の料理法で料理をつくる。」と言う処理が、「ルーティングにより異なるコントローラーで処理をすること」に合致する。

２、４、５の処理についてはどのメニューが注文されたとしても、共通して処理する内容である。このような処理を行うのがミドルウェアとなる。

また、　４　→　５　のように、異なる処理のミドルウェアが連続して処理されることをミドルウェアチェーンと呼ぶ。

今、Expressのインスタンスをappという変数に格納した場合、下記のような記載でExpressにミドルウェアは組み込まれる。

```js
const app = express();

app.use(function(req, res, next){
  //ミドルウェアが行う処理。
  next();//次のミドルウェアを実行する。
});
//この場合、全ての処理にミドルウェアの処理が組み込まれる。
```

```js
app.use("/admin", function(req, res, next){
  //この場合は URLが　/admin　配下のルーティングに対し、ミドルウェアの処理が組み込まれる。
  next();
});`
```

```js
app.get("/admin", function(req, res, next){
  next();
//メソッドを限定することも可能
});
```

##ミドルウェアの種類

```
1 アプリケーション・レベルのミドルウェア
2 ルーター・レベルのミドルウェア
3 Express本体に標準装備されたミドルウェア
4 サード・パーティー・ミドルウェア
```
###1 アプリケーション・レベルのミドルウェア

appにマウントするミドルウェア。引数として次の3つが決められている。

```
引数1 : リクエストオブジェクト (req) （ブラウザからのリクエスト情報を知る。）
引数2 : レスポンスオブジェクト (res) (ブラウザへのレスポンスを操作する。)
引数3 : 次のミドルウェアに処理を渡すコールバック（next） 

※引数名については慣習的なものであるが特別な理由がない限り上記の3つを使うのが、混乱もなく無難。
```

```js
var app = express();

app.use(function(req, res, next){
  next();
});
```

###2 ルーター・レベルのミドルウェア
router = express.Router()のrouterにマウントするミドルウェア。ルーターが複数あるときに個別のルーターに対してミドルウェアを直接マウントすることができる。

```js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  //処理
});

module.exports = router;
```

###3Express本体に標準装備されたミドルウェア

expressジェネレーターでアプリを生成すると、標準で組み込まれているミドルウェアがある。

例えば

```js
app.use(express.static(path.join(__dirname, 'public')));
```
これは、静的なファイルを配信するためのミドルウェア。引数に
expressオブジェクトのstatic(path.join(__dirname, 'public'))関数がミドルウェアとして組み込まれている。

なお、この関数は

```js
return function (req, res, next) {
  ...
  next();
}
```
というふうに、ミドルウェアの記述にのっとった関数を返すようになっている。

###4 サード・パーティー・ミドルウェア

```js
const app = express();

const cookieParser = require('cookie-parser');
app.use(cookieParser());
//cookieParserはクッキーを処理するためのミドルウェア
//モジュールインストール後、requireでモジュールロードし、
//app.useでミドルウェアとして処理を組み込んでいる。
```
【Nuxt.js】Google Fontsの導入方法

# `nuxt-webfontloader`をダウンロードする

```:ターミナル
$ npm i nuxt-webfontloader
```

#`nuxt.config.js`で設定


```javascript:nuxt.config.js

//------省略
  modules: [
    'nuxt-webfontloader'
  ],
  webfontloader: {
    google: {
      families: ['Langar', 'Lobster']
    }
  },
//------省略
```

#Fontを使用する

```vuejs:index.vue
<template>
  <div>
    <h1 style="font-family: 'Langar';">ABCDEF</h1>
    <h1 style="font-family: 'Lobster';">ABCDEF</h1>
    <h1>ABCDEF</h1>
  </div>
</template>
```

![スクリーンショット 2020-12-11 23.39.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/0563c114-ec22-969b-c812-008bce7a2c6d.png)
ARDUINO UNOとOLED、AL12832AWWB-H-U02でHello Worldを表示2(U8glib)

1.arduino ide 1.8.11を起動、U8glibをインストール
2.arduino unoを選択
3.ソースの例からHelloWorldを選択
4.U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// I2C / TWI 
を選択

必要ないけど3.3V->5Vコンバーターがついている。


```c++


#include "U8glib.h"

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);	// I2C / TWI 

void draw(void) {
  // graphic commands to redraw the complete screen should be placed here  
  u8g.setFont(u8g_font_unifont);
  //u8g.setFont(u8g_font_osb21);
  u8g.drawStr( 0, 22, "Hello World!");
}

void setup(void) {
  // flip screen, if required
  // u8g.setRot180();
  
  // set SPI backup if required
  //u8g.setHardwareBackup(u8g_backup_avr_spi);

  // assign default color value
  if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
    u8g.setColorIndex(255);     // white
  }
  else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
    u8g.setColorIndex(3);         // max intensity
  }
  else if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
  else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
    u8g.setHiColorByRGB(255,255,255);
  }
  
  pinMode(8, OUTPUT);
}

void loop(void) {
  // picture loop
  u8g.firstPage();  
  do {
    draw();
  } while( u8g.nextPage() );
  
  // rebuild the picture after some delay
  //delay(50);
}


```
![image_original (12).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/babfb475-ae08-d7bf-4ec9-63b7a7d6c716.jpeg)
pythonとjuliusとIFTTTを組み合わせて特定フレーズでツイートさせる

参考
[Raspberry Pi×JuliusとPythonでスマートスピーカー風にカメラを操作 | パソコン工房 NEXMAG](https://www.pc-koubou.jp/magazine/20637)
[IFTTTとRaspberry PiでIoTエッジデバイスを作る | パソコン工房 NEXMAG](https://www.pc-koubou.jp/magazine/31960)

##IFTTTのセットアップ

IFTTTでツイートするアプレットを立てましょう。今回は固定キーワードと重複ツイートを避けるために日時をツイートさせます。
アプレット名はtweetとして、こんな感じ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/a6b6081a-0e63-3247-cf83-61cad1209847.png" width="40%">

設定できたらWebhooksページのDocumentationを開いて
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/98667b15-b13b-e118-cc27-cd96ffa1288f.png" width="50%">

アプレット名を入力してcurlコマンドを取得します
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/0c04604a-1d64-94cb-bb5e-0a4908e9e9cd.png" width="70%">

##pythonコードの作成
juliusにはTCPソケット経由で認識結果を取得できるモジュールモードが備わっています。認識結果のスコア値が高かったらそのフレーズが発声されたと判断してツイートと音声を再生するようにします


```python:monafuwaTweet.oy
#!/usr/bin/python3
# -*- coding: utf-8 -*-

import socket
import time
import subprocess

host = 'localhost'
port = 10500

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))

res =   ''
while True:
        # 音声認識の区切りである「改行+.」がくるまで待つ
        while (res.find('\n.') == -1):
                # Juliusから取得した値を格納していく
                res += sock.recv(1024).decode()
        print(res)
        word2 = ''
        for line in res.split('\n'):
                #スコアを探す
                index2 = line.find('SCORE="')

                if index2 != -1:
                        print("found")
                        #スコア値を取得
                        line = line[index2+7 :line.find('"', index2 + 7)]
                        if len(line) != 0:
                                print(float(line))
                                if(float(line)<-6000.0):
                                        print("OK")
                                        #音声再生とツイート
                                        subprocess.call("/home/pi/voice/random.sh", shell=True)
                                        subprocess.call("curl -X POST https://maker.ifttt.com/trigger/tweet/with/key/##########YOURKEY#########", shell=True)
                res = ''
```

##試してみる

juliusをモジュールモードで起動

```shell
julius -C ~/julius/julius-kit/dictation-kit-4.5/am-gmm.jconf -nostrip -gram ~/julius/dict/monafuwa -input mic -module
```

pythonクライアントを起動

```shell
./monafuwaTweet.py
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/497ae9ea-1162-4f9f-a099-748c6ffbe7bd.png)

できた！！！！
julius 4.6でキーワードの組み合わせを認識させる

参考
[Raspberry PiとJuliusで特定の単語を認識させる | パソコン工房 NEXMAG](https://www.pc-koubou.jp/magazine/19743)

辞書ファイル用ディレクトリを作成します

```shell
cd ~/julius
mkdir dict
cd dict
```

##単語とその読みがなを定義するファイルを作成

```shell
nano monafuwa.yomi
```

```text:monafuwa.yomi
もなふわすいーとるーむ もなふわすいーとるーむ
助けて たすけて
```

##音素ファイル(.phone)を作成
(ハマリポイント1　入力元のファイルはUTF-8のままでOK)

```shell
~/julius/julius-4.6/gramtools/yomi2voca/yomi2voca.pl ./monafuwa.yomi > ./monafuwa.phone
```
できたファイルがこちら

```text:monafuwa.phone
もなふわすいーとるーむ  m o n a f u w a s u i: t o r u: m u
助けて  t a s u k e t e
```

##構文ファイル(.grammar)を作成

```shell
nano monafuwa.grammar
```

```text:monafuwa.grammar
S : NS_B W1 NOISE W2 NS_E

W1 : TASUKETE
W2 : MONAFUWASUITORUMU
```

grammarファイルの文法は[第7章 言語モデル](https://julius.osdn.jp/juliusbook/ja/desc_lm.html#id2537489)より引用
>"NS_B" と "NS_E" はそれぞれ文頭および文末の「無音区間」に対応する単語カテゴリである．文の最初と最後に必ず挿入すること． 
>Julius で発声の休止位置を記述するには，まず grammarファイルで，休止の出現しうる単語間にカテゴリ名「NOISE」を挿入する．

この場合では、"文頭の無音”、”たすけて”、”発声の休止”、”もなふわすいーとるーむ”、”文末の無音”のワンセットとなる。
W1とW2に列挙したシンボル列”MONAFUWASUITORUMU”と”TASUKETE”で次に作成する語彙ファイルから音素を引っ張ってくる。
ハマリポイント２　このシンボル列に.phoneファイルの文字列をそのまま使ってはいけない。今回のように伸ばす音を示すコロンなど記号は使ってはいけないため、コンパイル時にエラーが出ることになる。

##認識用文法の語彙ファイル(.voca)を作成

```shell
nano monafuwa.voca
```
.grammarファイルで使用する単語を定義する。定義内容は.phoneファイルの中身。
NS_B，NS_Eには，それぞれ文頭・文末の無音に対応する無音音響モデル"silB", "silE"を割り当てる。
NOISEは無音の音響モデル"sp"を割り当てる

```test:monafuwa.voca
% MONAFUWASUITORUMU
もなふわすいーとるーむ m o n a f u w a s u i: t o r u: m u
% TASUKETE
助けて t a s u k e t e
% NOISE
[sp] sp
% NS_B
[s] silB
% NS_E
[/s] silE
```

##コンパイル

```shell
cd ~/julius/julius-4.6/gramtools/mkdfa
mkdfa.py ~/julius/dict/monafuwa
```

monafuwa.dfa、monafuwa.term、monafuwa.dict、monafuwa.dfa.forwardの4ファイルが生成される。

##作成した辞書を使って認識を試す

```shell
cd ~/julius/dict
julius -C ../julius-kit/dictation-kit-4.5/am-gmm.jconf -nostrip -gram ./monafuwa -input mic
```

`たすけて、もなふわすい～とる～む`と喋ってみると...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/219748/fcc950fc-dd25-40af-002c-80a67f66d01f.png)