OmniAICreator/Qiita-1.07M huggingface dataset:
Linuxの整理

Linux周辺のワードがしっちゃかめっちゃかになったので整理

# Linuxとは
### 広義
**オープンソースOS**[^1]（Operationg System、動作システム）のこと
すごいフィンランド人がUnixを参考にして作った（中身はオリジナル）
→ 見た目はLinuxとUnix似てる

WindowsやMac OSもOS

### 狭義
OS内の**カーネル**[^2]の一種

### つまり
広義のLinux ＝ 狭義のLinux（カーネル） + その他諸々のソフトウェア

# Linuxディストリビューション
`狭義のLinux（カーネル） + その他諸々のソフトウェア`のこと
→ OSとしてのLinux（または配布形態）

いろいろある
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/430585/090e9dd8-bdd7-dc6e-3524-ffe7ebfc1687.png)

### RedHat（レッドハット）系
RedHat社開発のもの
「仕事」向け

・Fedora
・CentOS（セントオーエス）

### Debian（デビアン）系
ボランティア中心で開発されている
ユーザフレンドリーな考え方

・Debian
・Ubuntu（ウブントゥ）
・Linux Mint
・elementaryOS
・ZorinOS

### Slackware（スラックウェア）系
管理方法が複雑
安定性やセキュリティを重視

・openSUSE（オープン・スーゼ）


# 関連
コマンド系については[こっち](https://qiita.com/nanatsu/items/6581b18ef00af19127ea)





# 参考
1. [３分間で人に説明できるようになるUnixとLinuxの違い | エンジニアの入り口](https://eng-entrance.com/unix_linux)
2. [【初心者向け】3分でわかるLinuxでできることまとめ 10選 | エンジニアの入り口](https://eng-entrance.com/you_can_do_linux)
3. [Linuxディストリビューションとは ｜ 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word12472.html)
4. [【目的別】初心者におすすめのLinuxディストリビューション7選 | エンジニアの入り口](https://eng-entrance.com/linux_beginner_distribution#Linux-2)



[^1]: コンピュータのベース、パソコンでの基本的な処理を提供する
[^2]: OSの中核ソフトウェア
Railsでアクションごとにレイアウトを設定する方法

Railsでアクションごとにレイアウトを設定したい場面って結構あるので、解説とかありませんが、方法だけお伝えしたいと思います。

```Ruby
class ProfilesController < ApplicationController
    layout :resolve_layout
    def new
        @profile = Profile.new
    end
    
      def show
        @profile = Profile.find(params[:id])
      end
    
      def edit
        @profile = Profile.find(params[:id]) 
      end

      private
      def resolve_layout
        case action_name
        when "new", "edit"
          "devise"
        when "show"
          "main"
        else
          "application"
        end
      end
end
``` 
まぁこんな感じでです。
特に大切な部分は

``` Ruby
layout :resolve_layout #レイアウトの設定
#〜省略~
private #アクションごとにどれのレイアウトにするか設定
def resolve_layout
  case action_name
  when "new", "edit"
    #アクションがnew or editのとき
    "devise"
  when "show"
    #アクションがshowのとき
    "main"
  else
   #それ以外
   "application"
  end
end
```
でしゅ。
EV3(leJOS)でカメラの映像をPCに送って顔認識をする

# 概要
本記事は，EV3につなげられたカメラで顔認識をする手順を説明した内容となっています．
EV3内でカスケード分類機で顔認識をしようとすると，パフォーマンスが良くなかったので今回はPCに送ってから画像認識をすることにします.

# 目次
* [EV3プログラム編](https://qiita.com/RYoTA1209/private/24cdeb58dbed2fece859)
* [サーバー（PC)プログラム編](https://qiita.com/RYoTA1209/private/125c9ccb8e079ca2958f)
* [画像処理編](https://qiita.com/RYoTA1209/private/8f3a0a8848fcd7ed216c)
Media Foundation で RGB を扱うときの stride の扱い

（この記事は私の blog の http://umezawa.dyndns.info/wordpress/?p=7392 の転載です）

## 要約

Media Foundation で RGB を扱うときはメディアタイプに必ず MF_MT_DEFAULT_STRIDE を付けよう。

## 内容

Windows Vista 以降には Media Foundation という新しいマルチメディアフレームワークがあります。新しいと言っても10年以上経ってますけど。

Media Foundation 映像のメディアタイプには optional な attribute として <a href="https://docs.microsoft.com/en-us/windows/win32/medfound/mf-mt-default-stride-attribute">MF_MT_DEFAULT_STRIDE</a> というものがあります。これは非圧縮フォーマットにおいてライン間のオフセットを指定するものですが、結果としてフォーマットの「方向」がトップダウンかボトムアップかを指定することができます。 <a href="https://docs.microsoft.com/en-us/windows/win32/medfound/image-stride">Image Stride</a> に書いてある通り、正の値だとトップダウンで、負の値だとボトムアップです。最初に optional と書いた通り、この attribute は無くてもよくて、その場合フォーマットのサブタイプ（RGB24とかYUY2とかそういうの）に応じたデフォルト値を使うことになります。

YUY2 とかなら方向はトップダウンに決まっているので話は簡単です。一方、RGB の場合はトップダウンもボトムアップも両方あるため、話が面倒になります。マイクロソフト製のコードですらこの辺りは混乱しており、MFT コーデックの出力フォーマットが MF_MT_DEFAULT_STRIDE を省略した RGB に設定されている場合、エクスプローラーのサムネイル生成ではボトムアップとして表示されますが、Windows Media Player や TopoEdit ではトップダウンとして表示されます。以前、<a href="http://umezawa.dyndns.info/wordpress/?p=5625">Windows 10 だとエクスプローラーのサムネイル生成が上下反転するという問題がある</a>と書きましたが、それはこの辺の問題を踏んだせいで起きた現象というわけです。

どっちが正しいのか、ということもどうも判然としません。 <a href="https://docs.microsoft.com/en-us/windows/win32/medfound/uncompressed-video-buffers">Uncompressed Video Buffers</a> という記事には「MF_MT_DEFAULT_STRIDE が省略されている場合は <a href="https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mfgetstrideforbitmapinfoheader">MFGetStrideForBitmapInfoHeader</a> を読んで計算しろ」と書いてあります。この API は RGB フォーマットに対しては負の値を返すのですが、一般的に省略されている場合にボトムアップを仮定すべきなのかに関する直接的な記述は見つけられません。まあ、ボトムアップにしておくのが妥当な気はするのですが…

一応、コーデックがサポートしている出力フォーマットを返す際に RGB の場合は必ず MF_MT_DEFAULT_STRIDE を付けることで方向を固定することはできるようで、そうするとエクスプローラーのサムネイル生成でも Windows Media Player でも TopoEdit でも MF_MT_DEFAULT_STRIDE の付いたフォーマットが設定され、正しい方向で描画されます。というわけで、 optional といいつつ RGB の場合においてはほぼ必須 attribute という認識でいるべきです。もっとも、省略されたメディアタイプを設定された場合にどういう挙動をすべきか（設定を拒否するとかボトムアップを仮定するとか）は難しいところですし、サポートしている入力フォーマットを返す時は縦横ピクセル数が確定していないのでそもそも stride を計算できません。
EV3(leJOS)でカメラの映像をPCに送って顔認識をする①

# 概要
leJOSはEV3専用のJVM(Java Virtual Machine)です．この記事ではEV3に繋げたカメラの映像をネットワーク経由でPCに送信し，OpenCVを使って顔認識をするところまでの手順を説明します．その応用例はこの記事を読んでいるアタナ次第です！

# 環境
* LEGO mindstorms EV3
* leJOS 0.9.1-beta
* カメラ（本記事では，ロジクール ウェブカメラ C270で動作確認済 ）・・・カメラは対応しているものが限られているのでこれ以外のものを使う場合はよく調べてから用意してください．

# まずはEV3のカメラの映像をPCに送る
EV3に繋がれたカメラからフレームを取得しソケット通信でPCに送る．行数が多くなるのでimport文は省略しています．（ちなみに，著者はeclipseを使っているので使用しているので，import文はほぼ全てeclipseにまかせています．）
## EV3側のプログラム
```java:Stream.java
public class Stream {
	private static final int WIDTH = 160;//フレームの横幅
	private static final int HEIGHT = 120;//フレームの縦幅
	private static final String HOST = "192.168.2.1";//サーバーのアドレス（ここではPCのIPアドレス）
	private static final int PORT = 55555;//ポート番号（任意）

	public static void main(String[] args)  {

        EV3 ev3 = (EV3) BrickFinder.getLocal();//EV3本体
        Video video = ev3.getVideo();//カメラ

        try {
         //カメラをオープンして使える状態にする.
	        video.open(WIDTH, HEIGHT);
	    } catch (IOException e) {
                e.printStackTrace();
	        return;
	    }
        //フレームを取得する．
        byte[] frame = video.createFrame();

        //ソケットの準備
        Socket sock;
        BufferedOutputStream bos;
        try {
            sock = new Socket(HOST, PORT);
            bos = new BufferedOutputStream(sock.getOutputStream());
        }catch (IOException e) {
            e.printStackTrace();
            return;
        }

        //Escapeボタンが押されるまで繰り返す．
        while(Button.ESCAPE.isUp()) {
            try {
                video.grabFrame(frame);//現在のフレームを取得する
                bos.write(frame);//BufferedOutputStreamに書き込む
                bos.flush();//バッファの内容を出力する
            } catch (IOException e) {
                break;
            }
        }
        //プログラムの最後にはopenしたものをすべてcloseする．
        try {
　　　　　　　　bos.close();
　　　　　　　　sock.close();
　　　　　　　　video.close();
　　　　　} catch (IOException e) {
　　　　　　　　return;
　　　　　}
    }
}
```
shellscriptのwhile文が1回目のループで終了してしまう。。。

#何が起こったか
jenkinsサーバを構築していてwhile文で躓いてしまったので備忘を兼ねて記事にします。

githubでjenkins job用のconfig.xmlを保存しておき、新しいファイルがpushされたらデプロイするようなjobを作成しておりました。既存jobのconfigに修正が入った場合もうまくデプロイできるように一度jobをjenkinsCLIコマンドにて全て削除し、新しいconfigファイルで全てgitからcloneしなおし、作成しなおすようなことをしています。

その削除コマンドがうまくいきませんでした。
実装は下記のようない感じです。

```sh
while read line 
do
    java -jar jenkins-cli.jar -s http://localhost:8080/ delete-job ${line}	
done < delet_target_jobs.txt
```

```delet_target_jobs.txt
job_a
job_b
job_c
```
jobが3つ全てある状態から上記を実行するとなぜかjob_aしかdeleteされません。

```sh
java -jar jenkins-cli.jar -s http://localhost:8080/ list-jobs
#job_b job_c
```

#原因
調べてみるとどうやらwhile文で実行されるコマンドによってはファイル全体が標準入力になってしまい、whileループが1回で終了してしまうケースがあるらしいです。標準入力、標準出力については[こちら](https://qiita.com/angel_p_57/items/03582181e9f7a69f8168)で非常にわかりやすく解説されております。

javaコマンド以外だとsshコマンドもそれに該当するようで、下記もroop_me.txtが何行存在していても1回でroopが終了してしまいます。

```sh
while read line
do   
    ssh 192.168.0.1 touch $line
done < roop_me.txt
```

#対策
sshコマンドの場合はオプション`-n`を付けることで想定通り実行できます。
※-n は標準入力からの読み込みを禁止するオプションです。

```sh
while read line
do   
    ssh -n 192.168.0.1 touch $line
done < roop_me.txt
```
javaコマンドでは標準入力からの読み込みを禁止するオプションがないのでwhile文を使用しないことで対応できます。

```sh
for line in `cat delete_target_jobs.txt`
do
  java -jar /var/lib/jenkins/jenkins-cli.jar -s http://localhost:8080/ delete-job ${line}
done
```
上記のようにすれば行数分確実にループが行われるので問題なく実行できました！


#おわりに
ループが1回で終わってしまう現象が本当に分からず上記の内容にたどり着くまでとても時間を使ってしまいました。。。
linuxの標準入力/標準出力/標準エラー出力についてはもっと深い理解が必要だと感じたのでまた記事にしたいと思います。
Windows Defender ATP の検証　～下準備編

備忘録的な投稿です。
検証の目的としては、Windows Defender ATPでウイルスを検知した際のアラート画面を見たい

まず用意するもの
・仮想マシン（Azure のIaaSで構築）
　OSはWindows 10 Enterprise Ver.1909
　サイズ：Standard DS2 v2 (2 vcpu 数、7 GiB メモリ)
・Microsoft 365 E5　ライセンス
　試用版を利用
・ウイルス（EICARのテスト用ウイルスを使用）
https://ja.wikipedia.org/wiki/EICAR%E3%83%86%E3%82%B9%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB

・神の思し召しサイト様
■ライセンスのプロビジョニングを検証し、Microsoft Defender ATP のセットアップを完了する
https://docs.microsoft.com/ja-jp/windows/security/threat-protection/microsoft-defender-atp/licensing
■動画3部作
https://www.bing.com/videos/search?q=windows+defender+atp&&view=detail&mid=89C15D87CD7334BCED9F89C15D87CD7334BCED9F&&FORM=VRDGAR&ru=%2Fvideos%2Fsearch%3Fq%3Dwindows%2Bdefender%2Batp%26FORM%3DHDRSC3

https://www.bing.com/videos/search?q=windows+defender+atp&&view=detail&mid=DB6EDFD28CACA3E7B381DB6EDFD28CACA3E7B381&&FORM=VRDGAR&ru=%2Fvideos%2Fsearch%3Fq%3Dwindows%2Bdefender%2Batp%26FORM%3DHDRSC3

https://www.bing.com/videos/search?q=windows+defender+atp&&view=detail&mid=B9C25DC170B3410A9E16B9C25DC170B3410A9E16&&FORM=VRDGAR&ru=%2Fvideos%2Fsearch%3Fq%3Dwindows%2Bdefender%2Batp%26FORM%3DHDRSC3

■チュートリアルの概要: ATP セキュリティ アラート ラボ
https://docs.microsoft.com/ja-jp/azure-advanced-threat-protection/atp-playbook-lab-overview


スタート
１．Azure上に仮想マシンを作成する
メモ
　Adminユーザーとパスワードは控えておく
　RDPで接続できるようにネットーワークからポート3389を開けておく
　自動シャットダウンを設定しておく
　IPを静的に変更しておく
　Windows Updateをする

２．AzureADに参加させる
　~~参加させなくてもいいかもしれない・・・~~
→参加させなくても大丈夫でした


３．Microsoft Defender セキュリティセンターに初めてアクセスする
下記にアクセス
https://securitycenter.windows.com/
データセンターのリージョンを設定する
とりあえず全てイエスで進める

次はPCにデータセンターに投げられてる情報はこのPCですよーって設定をする
https://docs.microsoft.com/ja-jp/windows/security/threat-protection/microsoft-defender-atp/configure-endpoints
ローカルスクリプトを使用した Windows 10 マシンのオンボード
https://docs.microsoft.com/ja-jp/windows/security/threat-protection/microsoft-defender-atp/configure-endpoints-script

Microsoft Defender セキュリティセンターからオフボードパッケージを取得する。

a. ナビゲーションウィンドウで、[設定 > オフボード] を選択する。

b. オペレーティングシステムとして Windows 10 を選択する。

c. [展開方法] フィールドで、[ローカルスクリプト] を選ぶ。

d. [パッケージのダウンロード] をクリックして、.zip ファイルを保存。

.Zip ファイルのコンテンツを、コンピューターからアクセスできる共有の読み取り専用の場所に抽出します。 WindowsDefenderATPOffboardingScript_valid_until_YYYY-MM-DD.cmd という名前のファイルが抽出されます。

コンピューターで管理者特権のコマンドラインプロンプトを開き、次のスクリプトを実行する。

a. [スタート] に移動し、「cmd」と入力します。

b. [コマンド プロンプト] を右クリックし、[管理者として実行] をクリックします。

スクリプト ファイルの場所を入力する。 デスクトップにコピーしている場合は、「%userprofile%\Desktop\WindowsDefenderATPOnboardingScript.cmd」と入力します。

Enter キーを押すか、[OK] をクリックします。

コンピューターの構成を監視する
Microsoft Defender セキュリティセンターに移動する。

[Machines list] (コンピューター一覧) をクリックする。

マシンが表示されていることを確認する。
→マシンリストに上がってこない・・・191226
今回はここまで、次回に続く

200108追記
上記手法でコマンドを実行してもマシン一覧に上がってこなかったので別の方法を実施

ダウンロードしたパッケージファイルを展開し、右クリック→ [管理者として実行] をクリックする
コマンドが実行されるので [Y] キー→Enterを押下

これでマシンリストに上がってくるようになりました。

Windows Defender ATP 画面の見方については追々・・・
find_or_initialize_byの使い方


#find_or_initialize_byって？
モデルから渡したパラメーターに該当するものを探してくれるんだけど、あればそれを返すし、なかったらnewしてインスタンスを作ってくれるみたい！

似たようなメソッドで`find_or_create_by`という`create`してくれるものもあるらしい。

`profile.update_attributes(profile_params)`でバリデーションにひっかからなかったら(`create`or`update`してくれる！)

```reviews_controller.rb


  def create_profile
    session[:review_area_key] = params[:review_area_key]
    session[:review_provider_id] = params[:review_provider_id]
    session[:review_plan_id] = params[:review_plan_id]

    profile_params = params.permit(:nickname, :gender, :icon, :icon_color, :age, :prefecture,
                                   :provider_id, :plan_id, :month, :price, :from, :is_hikkoshi)
    session[:profile_params] = profile_params
    profile = UserProfile.find_or_initialize_by(user_id: current_user.id)　⭐️ここ
    if profile.update_attributes(profile_params)
      if (alerts = alerts_if_no_review_plan(params)).blank?
        redirect_to reviews_new2_path
      else
        redirect_to reviews_new_path(f: params[:from]), alert: alerts
      end
    else
      redirect_to reviews_new_path(f: params[:from]), alert: profile.errors.full_messages
    end
  end

```





いい記事 !https://qiita.com/taimuzu/items/0a21738d018f475d63ae
nuxt.jsで Vue packages version mismatch:  vue@2.6.10 vue-server-renderer@2.6.11 と怒られる

何もしてないのに壊れた。

```
Vue packages version mismatch:

vue@2.6.10
vue-server-renderer@2.6.11
```

## 解決方法

package-lock.jsonを消して、npm updateする

```bash
$ rm package-lock.json
$ rm -rf packnode_modules
$ npm update
```

## 参考

https://github.com/nuxt/nuxt.js/issues/6796
API作成の run_server.py　苦戦....

**postするjsonのデータは{"name":"monkeydaichan"}です．**
**戻り値は{"data":{"id":1,"name":"monkeydaichan"}}こうしてます．**

```py
# coding:utf-8
import json
from flask import Flask, jsonify, make_response, request, Response

app = Flask(__name__)

@app.route('/post', methods=['POST'])# POST形式で送られたデータのみ受け取る
def post_json():
  json = request.get_json()  #HTTPリクエストのMIMEタイプがapplication/json場合、 request.get_json()を呼び出すと解析されたJSONデータが返されます
  NAME = json['name'] #辞書型変数jsonのキー”name”の”value”の値を変数NAMEに投げる
  result = {
    "data": {
      "id": 1,
      "name": NAME
      }
    }
  return jsonify(result) 

if __name__ == "__main__":
  app.run()
```

#①@app.route()とは？
```py
from flask import Flask
app = Flask(__name__)

@app.route('/') # http://xxx 以降のURLパスを '/' と指定
def index():
    return 'Index Page'
```
上記の例では、 http://xxx/ と index() を紐付けています。http://xxx/ にアクセスすると、「Index Page」とだけ書かれた質素なページが表示されます。
つまり、"/aaa"と書けば、http://xxx/aaaというリンクを探すと、以下の処理を行うということ！ 

#②HTTPメソッドに応じたルーティング
```py
@app.route('/edit', methods=['GET', 'POST'])
def edit():
    # 共通の処理
    if request.method == 'get':
        # GET時の処理
    else:
        # POST時の処理
    # 共通の処理
```

#POSTとGET
Webサーバーに情報を渡すための「**手段**」!
**POST形式**で送るのか？　**GET形式**で送るのか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475270/89edc13a-dd53-6cc3-c2f1-2109b53d89aa.png)


**GET形式**はURLの後ろに**入力データ**がくっつく形式!
つまり、入力データを送信した次のページのリンクに入力データが見える形に載ってる！
（個人情報とか載るって怖い）＊どうでもいい情報なら問題なし
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475270/63eb91d6-ce8b-8124-8ba1-c455ea45367e.png)


**POST形式**はURLの後ろに**入力データ**がくっつかない形式!
個人情報も隠れるのでok!
ShopifyApp gem(ShopifyApi)の操作方法

## はじめに

ShopifyのAPIをRubyで操作するために公開されているShopifyApp（ShopifyApi gem含む） gemの操作方法をメモしておきます。
公式のドキュメントの情報が少なすぎて萎えてる人が多いのかなと思ったので、参考になれば幸いです。
ちなみにShopifyApp gemにはShopifyApi gemが内包されているので、ShopifyApp gemをインストールすれば、ShopifyApiモジュールも使えます。

全部を網羅的に書くのではなく、あくまでもなかなか調べても出てこないものだけを書いておきます。

以下が情報ソース
* [shopify_app gem](https://github.com/Shopify/shopify_app) 
* [APIリファレンス](https://help.shopify.com/en/api/reference)

> この記事では導入方法については触れません

## webhookの登録

webhookを登録することでストアでのイベントを取得して任意の処理を任意のタイミングで行うことが出来ます。

```ruby
ShopifyAPI::Webhook.create(:format => 'json or xml', :topic => 'topic', :address: 'POST先のエンドポイント')
```

上記が基本のフォーマットとなります。
例えば注文が作成された時に指定のエンドポイントへjson形式でデータをPOSTしてほしい時は以下のようになります。

```ruby
ShopifyAPI::Webhook.create(:format => 'json', :topic => 'orders/create', :address: 'https://hoge.com/order_create')
```

トピック一覧（イベント一覧）は[こちら](https://help.shopify.com/en/api/reference/events/webhook)から確認することが出来ます。

## Product

### 取得

```ruby
# 全取得（max50件まで）
ShopifyAPI::Product.all

# idを元に取得
ShopifyAPI::Product.find("#{product_id}")

# 商品タイトルで該当商品を取得
ShopifyAPI::Product.where(title: "#{オリジナルTシャツ}")
```

参考：[REST Admin API - Product](https://help.shopify.com/en/api/reference/products/product)

### Metafieldの追加

```ruby
# metafieldを追加したい商品を取得しておく
product = ShopifyAPI::Product.find('商品id')
# metafieldのインスタンスを生成する
update_metafield = ShopifyAPI::Metafield.new(
  key: 'hoge',
  namespace: 'product_info',
  value: 'ほげ', # metafieldに保存する値
  value_type: 'string or integer or json_string' # 文字列型 or 整数型 or JSONか
)

# 以下で追加完了。saveは必要ない
product.add_metafield(update_metafield)

```

## Order

### 取得

```ruby
# 全件取得（max５０件まで）
ShopifyAPI::Order.all

# idを元に検索
ShopifyAPI::Order.find('12412121')

# 支払い済みの注文だけを取得
ShopifyAPI::Order.where(financial_status: 'paid')

# 出荷済みの注文だけを取得
ShopifyAPI::Order.where(fulfillment_status: 'shipped')
```

参考：[REST Admin API - Order](https://help.shopify.com/en/api/reference/orders/order#show-2019-10)

### NoteAttributeの追加

注文詳細の詳細項目に追加出来るAPIです。

```ruby
# idを元に注文を取得
order = ShopifyAPI::Order.find('12224121')
# noteattributeを追加
order.note_attributes.append(ShopifyAPI::NoteAttribute.new(name: 'ほげ', value: 'テストテスト'))
# 変更を保存
order.save
```

### Metafieldの追加

商品とほぼ変わりませんが、一応書いておきます。

```ruby
order = ShopifyAPI::Order.find('12224121')
update_metafield = ShopifyAPI::Metafield.new(
  key: 'hogekey',
  namespace: 'hogename',
  value: 'hogevalue',
  value_type: 'string'
)
order.add_metafield(update_metafield)
```
シェルのメモ

#shellとは
- shellはユーザーから読みやすいコマンドをカーネルが理解できるものに変換するツール。
- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396969/dcd7425b-1e03-8050-907f-69ce429ba6f6.png)1


##shellにはCommand Line ShellとGraphical shellがある。2
- **Command Line Shell**は黒い画面に文字があるツール。
    - 例えば、Windowsの**コマンドプロンプト**、linux,Macの**ターミナル**。
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396969/22214ce6-2733-d548-ca54-4906045a40e8.png)
  
- **Graphicall shell**は直感的に使えるツール。
    - 例えばMacOSの最初の画面、Ubuntuの最初の画面
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/396969/54b88719-c6f8-96ab-d36a-fd13430ae076.png)


##shellscriptとは
- **shelscript**はコマンドをまとめたもの。
- MS-DOS(コマンドプロンプト)のバッチファイルに似ている。3


参考サイト
1.https://orizuru.io/blog/os/shell/
2.https://www.geeksforgeeks.org/introduction-linux-shell-shell-scripting/
3.http://www.tohoho-web.com/ex/draft/msdos.htm
[JavaScript][ES2017]0埋め（ゼロパディング）をするシンプルな記法（padStart, padEnd）

## 概要
ググるとまず`slice()`を使う方法が出てくるが、ES2017が使える環境ならば`padStart()`の方が良い。

Node.jsならば8.0.0から使用可能。

## 使い方
一つ目の引数にパディング後の桁数、二つ目にパディングに使う文字（デフォルトは半角スペース）を指定する。

```javascript
> '123'.padStart(5)
'  123'

> '123'.padStart(5, '0')
'00123'

> '123'.padStart(10, '*')
'*******123'
```

右側を埋める`padEnd()`もある。

```javascript
> '123'.padEnd(5, '0')
'12300'
```


## ドキュメント
* https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
*  https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
PugでHTMLタグを含む文字列を変数展開したい


#`#{}`ではなく`!{}`を使う

### 失敗例
`#{}`を使うと上手くいかない。

```javascript:sample.pug
-
  const text = `これは<span class="red">サンプルテキスト</span>です。`

p #{text}
```

コンパイルしてみると、、、

```html:sample.html
<p>これは&lt;span class=&quot;red&quot;&gt;サンプルテキスト&lt;/span&gt;です。</p>
```

<img width="376" alt="スクリーンショット 2019-12-26 12.51.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274280/6607281c-7b02-5619-0777-3376c0f12631.png">

`#{}`記法は特殊文字をエスケープ処理してから出力するため、このような結果になります。


### 成功例

ここではエスケープせず、HTMLタグとして出力したいので、`!{}`を使ってエスケープせずに出力させます。

```javascript:sample.pug
-
  const text = `これは<span class="red">サンプルテキスト</span>です。`

p !{text}
```
```html:sample.html
<p>これは<span class="red">サンプルテキスト</span>です。</p>
```
<img width="376" alt="スクリーンショット 2019-12-26 12.51.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274280/487b0752-15b2-1a2b-4758-9c99860bc512.png">

上手くいきました:thumbsup:

## 参考
https://pugjs.org/language/interpolation.html
コンポーネント / props / emit の基本


# コンポーネントとは

ウェブアプリケーションはたくさんの機能を持つUI部品で構成されている。
その要素を**再利用可能な部品化したもの**を`コンポーネント`と呼ぶ。
再利用可能というのは、例えばボタンを1つをコンポーネントとして作成することによって、様々なページでボタンの実装をしなくても、コンポーネントを呼び出すだけで、何度も使えるということ。

この時、`単一ファイルコンポーネント`(SFC: Single File Components)を利用する。
**単一ファイルコンポーネント**とは`マークアップ(html)`、`スタイル(CSS)`、`ロジック(js)`すべてを1つのファイル内に記述できる仕組みのことで、以下の様に HTML + CSS + js を `.vue`という拡張子で**まとめて管理する。**

```index.html
<template>
  <div class="app">
    <h1 class="title">{{ text }}</h1>
  </div>
</template>

<script>
export default {
  data(){
  }
}
</script>

<style scoped>
 .title {
   color: #fff;
 }
</style>
```

これにより**ファイルの拡張子毎で管理するのではなく、ページを構成する部品毎にファイルを管理できる。**これらのコンポーネントを組み合わせて1つのウェブページを作成し効率化を図る開発を「**`コンポーネント指向開発`**」といいます。

# コンポーネントの定義方法

```
├── src
│   ├── components
│   │   ├──App.vue
│   │   ├──ExampleComponent.vue・・・追加
│   └── index.js
```

まず新しいファイルを作成して以下の記述をしておく

```ExampleComponent.vue
<template>
  <h1>例</h1>
</template>
```

「App.vue」内でインポート

```app.vue

<template>
  <div>
    <h1>{{message}}</h1>
    <hr>
    <example-component></example-component> //これで読み込み完了
  </div>
</template>

```

パスカルケースはケバブケースへの変更。

| 名称 | 記述例 |説明|
|:--|:--|:--|
| ケバブケース | my-componet | 単語をハイフンでつなげてすべて小文字|
| キャメルケース  | myComponent | すべて単語をつなげて、最初の単語の頭文字を小文字、後に続く単語の頭文字を大文字|
| パスカルケース | MyComponent | 単語ごとの頭文字を大文字にしてすべてつなげる|

```app.vue
<script>
  import ExampleComponent from 'Components/ExampleComponent';

  export default {
    data() {
      return {
        message: '<span>Hello Vue</span>',
      }
    },
    components: {
      ExampleComponent: ExampleComponent, // カスタムタグ名: 上でimportした変数名
      // 上記の様に、変数名をそのまま使う場合は
    }
  }
</script>
```

これでコンポーネントの読み込みは完了！


# 親子コンポーネント間のデータフロー

### `props` :データの流れ 親 → 子

親 → → → → → → → → → → → →→ → → → 子
属性で渡す　　　　　　　　　　　　　　`props`で受け取る



### `emit` : データの流れ 子 → 親

子コンポーネントが持っているデータを**親コンポーネントに渡したい時**

子 → → → → → → → → → → → →→ → → → 親
`$emit`でイベント発火　　　　　　　　onで受け取る　　　　　　　　　　　　　　



# アトミックデザインとは

**UIデザイン手法** でモジュラーデザイン（製品の各モジュールを作成した後、それらをつなげて完成品を作る手法）を元に考案された。アトミックデザインでは以下の**5つのステージに分けて、管理する。**


小
　　Atoms (原子)　　　　　　　　　例） label / button 
　　Molecules(分子)            例） 検索フォーム、メニュー、リスト
粒　Organic(有機体)                header / footer
度　Templates（テンプレート）
　　Pages
大
サービスメッシュとGoogle Cloudサービスメッシュの紹介

**Microservices マイクロサービスとは**

皆さんは既にマイクロサービスアーキテクチャ知識を持ってるかもしれませんが、簡単にマイクロサービスの特徴をまとめます。

- ビジネス機能に 沿って複数小さい「マイクロサービス」に分割し、 それらを連携してアプリケーションを構築する
- 各サービス、独立したプロセスで動作する
- 各サービス、よく定義されたオープンなインタフェースを備える (REST(JSON/HTTPS), gRPC 等)

**マイクロサービスのメリット**

- 複数技術で対応可能
- 障害耐性・保守性が高い
- スケール・冗長化が容易
- 開発チームを小さくできる
- サービス一つ一つが小さいため把握が容易
- サービスの再利用性が高くなる

**マイクロサービスの課題***

- サービスディスカバリ：　サービスや存在関係の発見
- セキュリティ：　安全に接続する
- トラフィック制御： レートリミットやリトライ設定
- 負荷分散：複数サービスインスタンスにルーティング
- 可観測性：全体がどように連携しているかわかりにくい
- 分散トレース： サービス間、内部動作把握が困難
- 高度なデプロイ：安全に効率よくデプロイする

**ServiceMesh サービスメッシュとは**

サービスメッシュはマイクロサービスが抱える問題解決のための1つのアップローチです。

サービスメッシュの主な機能は：

- サービス認証
- 負荷分散
- タイムアウト、リトライサーキットブレーカー
- コネクションプールサイズ管理
- きめ細かいルーティング
- テレメトリ
- リクエスト追跡
- 意図的なエラー実施

サービスメッシュ機能を提供する製品はたくさんありますが、代表的なソフトウエアはIstio、 Linkerd、Consul。次に一番人気のIstioを紹介します。

**Istioとは**

Istio」は、IBMやGoogle、Red Hatなどが力を入れている取り組みであり、クラウド上のさまざまなシステムをまたがったマイクロサービス群をサービスメッシュという単位で管理できるようにするものだ。

![about-architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/543441/15171645-55bc-3850-fbda-8b87a3e4ba09.png)

 
Istio では、Envoy ベースのサイドカーから成るデータ プレーンが提供されます。 これらのインテリジェント プロキシは、メッシュ化されたアプリとワークロードの内外のすべてのネットワーク トラフィックを制御します。
コントロール プレーンは、次のコンポーネントを使用して、構成、ポリシー、およびテレメトリを管理します。

- Mixer - アクセス制御と使用ポリシーを適用します。 Prometheus にプッシュされたプロキシからテレメトリを収集します。
- Pilot - プロキシのサービス検出やトラフィック管理のポリシー/構成を提供します。
- Citadel - サービス間の mTLS を可能にする ID とセキュリティの機能を提供します。
- Galley - コンポーネントに対する構成の抽象化と提供を行います。

 
**Google CloudのAnthos Service Mesh (ASM)**

Anthos Service Mesh（ASM）は、Google Cloudの複雑なマイクロサービス アーキテクチャ向けのフルマネージド サービス メッシュです。マイクロサービスの複雑な環境を管理し、それらが保証するメリットをすべて享受できるようにするための、フルマネージド サービス メッシュです。このプラットフォームにより、トラフィック管理やメッシュ テレメトリ、サービス間の通信保護に至るまで、全体的なサービス運用をシンプルにして、運用チームや開発チームの負担を大幅に軽減します。

 

次の記事、Anthos Service Mesh利用してマイクロサービス実装と運用のデモを紹介する予定ですので、少々お待ち下さい。
ABC147 C - HonestOrUnkind2[Python]

・[ABC147 C - HonestOrUnkind2](https://atcoder.jp/contests/abc147/tasks/abc147_c)

正直者と不親切な人、計N人の証言が入力される。この内正直者は最大で何人存在し得るか、という問題。
12月からPython慣れのためにAtCoderやり始めて最新の方からぼちぼち過去問を触っているのだけど入力見て頭がフットーしそうになった
（ノートに文や図を雑書きしながら問題を読み込むのは大事ですね）。

---
解法
---
正直者は「1」、不親切な人は「0」で表すので、状態をNbit持つ事にして、全ての通りについて検証していけばいいです。
bit全探索ってやつですね。

N=3ならbitを000から111まで順に回していきます。
「001」なら1人目の証言について検証、「010」なら2人目の証言について検証、「011」なら1人目と2人目の証言について検証する....といった具合。
（何を血迷ったか「今001で1人目が2人目を正直としてるからこの時2人目の証言についても検証しないといけなくてそうなると2人目の証言によってn人目の証言も....ぐああ！」ってなってました。bit全探索の意味が無い....
その時点でbitが立ってる人の証言と各人の状態が正しいかどうかだけを確かめればいいですね）

解答
---

        import sys

        def kensho(x,tlist):
            for lis in tlist:
                if((x>>(lis[1]-1)) & 1 == lis[2]):
                    pass
                else:
                    return -1

            return bin(x).count('1')


        def main():
            N = int(input())
            xy=[]
            ans=0

            for i in range(1,N+1):
                An= int(input())
                for j in range(0,An):
                    x, y = map(int, input().split())
                    xy.append([i,x,y])

            cnt = N
            tlist=[]
            for x in range (2 ** cnt):
                for y in range(cnt):
                    if((x>>y) & 1):
                        for z in xy:
                            if(z[0]==y+1):
                                tlist.append(z)

                anstack = kensho(x,tlist)
                if(ans<anstack):
                    ans=anstack

                del tlist[:]

            print(ans)

        main()


相当冗長なクソコードになっている感じは否めないですがとりあえずACしたコード。
C問さえ理解するのに1～2時間掛かってしまっている・。・
解けた時の解放感は良いんですけど実際のコンテストではささっと解ける段階に至れていないのでまだまだ努力が必要ですね
v・。・v
#Stripe の  サブスクリプションでのスケジュール登録と請求日・支払い日=billinc_cycle_anchorとトライアル期間=trial_endの違い

# スケジュール登録

ダッシュボードから登録できる
サブスクリプションのライフサイクルを司れるようだ
スケジュール登録したタイミングではサブスクリプション本体は作成されず、スケジュールイベントだけが登録されるようだ

![image](https://user-images.githubusercontent.com/13635059/71431389-e56a4e80-2714-11ea-9fec-24b12d5cdc72.png)

![image](https://user-images.githubusercontent.com/13635059/71431297-2c0b7900-2714-11ea-8f4f-4318602fa510.png)

Stripe API Reference - Subscription Schedules
https://stripe.com/docs/api/subscription_schedules

# billing_cycle_anchor = 支払い日

API経由で登録できる
billing_cycle_anchorを指定した場合、サブスクリプション自体はすぐ作成するが、その支払い日の起点を指定できるようだ
billing_cycle_anchorはタイムスタンプで指定する
例えば毎月3日を支払い・請求日にするのであれば、3日のいずれかの時刻のタイムスタンプを指定する
請求はすぐには行われず、billing_cycle_anchorに指定した未来時刻が最初の請求日になるようだ
スケジュール登録のための機能ではないため、例えばプランの請求サイクルが1ヶ月であれば、1ヶ月を超えた未来の指定はできないようだ

# trial_end = トライアル期間

trial_endを指定した場合は、その終わりの時刻がそのまま、支払い日の起点になるようだ
例えばtrial_endが3日3日であれば、3日3日請求サイクルが始まり、そのあとは毎月3日が請求日・支払い日として、サイクル請求されるようだ
billing_cycle_anchorとは違い、プランのサイクルによる制限はないようだ、なぜなら、トライアル期間は請求そのものをしないのだから、指定の縛りがありようもない

# billing_cycle_anchor trial_end を組み合わせて使う

prorate = 日割り支払いの指定が必要になるようだが、詳細は未確認








# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/2870








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
【失敗談】自宅でも簡単にできるはずだったニッケルメッキ

今回はこの錆びきったヤスリをニッケルでメッキしてビッカビカのヤスリに仕上げたいと思います。![iOS の画像 (14).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/fa6c4ba2-732b-b0b8-f9a6-c203388b8f99.jpeg)
メッキの過程でサンポールを使用します。
独特の臭いがするので屋内で作業される場合は注意してください。
#そもそもニッケルメッキとは？
実は、中学校の実験で誰しもが金属の電気分解（メッキ）を経験しています。
改めて概要を説明すると、陽極側（今回の場合ニッケル側）では電気をかけることにより、酸化反応が起こります。
すると、ニッケル板がニッケルイオンとなり電解液中（サンポール）に溶け出します。
溶け出したニッケルイオンを陰極側（今回の場合ヤスリ側）が受け取り、陰極側の表面にニッケルが生成されます。
#用意するもの
![iOS の画像 (5).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/39d8b495-4da5-cc82-ecc0-178394197461.jpeg)
・サンポール
電解液です。

・パーツクリーナー
メッキする対象（今回は錆びきったヤスリ）の脱脂に使用します。

・電池ケース
別にあってもなくてもいいんですが、あると非常に便利です。

・電池
今回はリチウムイオン電池の単３を２つ使用します。

・純ニッケル板
なるだけ純度の高いものを用意してください。

・容器
メッキする対象が余裕を持って収まるサイズが好ましいです。

・ワニ口×２本
上記画像の青と白のコードです。電池ケースとニッケル板、メッキする対象をつなぎます。

・銅線
ワニ口とメッキ対象を直接つなぐと、ワニ口が電解液に浸かってしまい、ワニ口の金属部分もメッキされてしまいますので、ワニ口とメッキ対象を銅線を用いて中継します。
#メッキ対象の脱脂
メッキ対象に脂が付着していると、その部分のメッキが剥がれやすくなってしまいます。
パーツクリーナーをメッキ対象に吹きかけて脱脂します。![iOS の画像 (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/4850015e-a03d-4c61-7c09-78522109d8a0.jpeg)
素手で脱脂してしまうと、手の脂がメッキ対象に付着してしまいます。
脱脂の際はビニール等の手袋を着用し、一度脱脂を行ったらメッキが終わるまでメッキ対象を素手で触れてはいけません。
#電解液の作り方
サンポールと水を1:4の割合で混ぜます。
![iOS の画像 (9).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/9d474978-c010-ae1a-6c86-935049d9f4b1.jpeg)
メッキ対象を容器に入れたときに、全体が浸り切るかさになるよう注意しましょう。
#配線
電池の+側にニッケル板をつなぎ、-側にメッキ対象をつなぎます。
![iOS の画像 (7).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/e057605e-8686-606c-4937-deea72d21f67.jpeg)

#メッキを開始
ニッケル板とメッキ対象を電解液に浸します。
このとき、ニッケル板につないだワニ口の金属部分が電解液に入らないよう注意しましょう。
電解液に入ってしまうと、ワニ口の金属部分が溶けてしまいます。
![iOS の画像 (10).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/3e6112ec-ab8a-94a9-403f-cb9b0e91f2b2.jpeg)
このようにメッキ対象から泡が吹き出せばメッキが始まっています。
泡が出ない場合はニッケル板とメッキ対象が触れていないか確認してください。![iOS の画像 (6).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/d8772e7b-f751-42f7-443c-6b81a7025dd6.jpeg)
正常にメッキが始まったら、1時間半〜2時間ほど待ちましょう。
#完成
慎重に取り出し、付着した電解液を水で洗い流した後、丁寧に水を拭き取ります。![iOS の画像 (15).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553438/324ee222-f75f-57ab-852e-8318bcc610af.jpeg)
ご覧の通りビッカビカのビの字もありません。
なんのための二時間だったのかと考えてしまいそうですが、前を向くことにします。
強いて言うなら少しだけ錆びが目立たなくなりましたが、
それならメッキ前の方が味があって格好良かったのではないかとすら思えます。
#敗因
おそらくメッキ対象をメッキ前にある程度磨いて、ビッカビカにしてからメッキに取り掛かるべきだったのでしょう。
医療画像用Deep Learningツール「NiftyNet」


NiftyNetはTensorFlowをベースにした医療画像用Deep Learningツールであり、プラットフォームです。
日本語資料が無かったため、ここで紹介します。

特徴としては、
NIfTIファイルなどの医療画像特有のファイル形式に対応している点
設定をconfigファイルに書くだけでDeep Learningができる点
が挙げられます。

自分でコードを書いて実装する必要が無いため、KerasやPyTorchよりも簡単にDeep Learningをすることができます。

使用できる機能として４つありました。
１．image segmentation (3D U-NetやV-Netなど)
２．image regression (segmentationのような分類ではなく画素値予測。例えばMRI画像からCT画像を予測する) 
３．image model representation (VAEによる)
４．image generation (GANやVAEを用いる）

**参考文献**
開発者のサイト：https://niftynet.io/
論文：https://www.sciencedirect.com/science/article/pii/S0169260717311823?via%3Dihub
Gibson E, Li W, Sudre C, Fidon L, Shakir DI, Wang G, Eaton-Rosen Z, Gray R, Doel T, Hu Y, Whyntie T, Nachev P, Modat M, Barratt DC, Ourselin S, Cardoso MJ, Vercauteren T. NiftyNet: a deep-learning platform for medical imaging. Comput Methods Programs Biomed. 2018 May;158:113-122. doi: 10.1016/j.cmpb.2018.01.025. Epub 2018 Jan 31. PMID: 29544777; PMCID: PMC5869052.

#インストール
Anacondaでpipを使ってインストール出来ました。（niftynetのversionは、0.6.0がインストールされました。）

```
pip install tensorflow-gpu==1.14.0
pip install niftynet
```

###エラー情報
RTXの2070、2080のGPUを使用した場合では、なぜかCUDAエラーで出来なかったため一応書いておきます。


#デモを動かす
インストールしたら、以下コマンドでデモをダウンロードできます。

```
net_download dense_vnet_abdominal_ct_model_zoo
```
ホームフォルダに「niftynet」というファルダができ、そこにダウンロードされました。
extensionsのdense_vnet_abdominal_ctフォルダの中にあるconfigファイル(config.ini) を見てみると、設定を見ることができます。

configファイルの項目の説明と書き方についてはサイトに丁寧に書かれています。
では、セグメンテーションを実行してみましょう。

```
net_segment inference -c ~/niftynet/extensions/dense_vnet_abdominal_ct/config.ini
```
segmentation_outputフォルダが作成され、セグメンテーション結果が出力されました。

## 追記(2020/07/01)
残念ながら2020年の4月にNiftyNetの開発は中断され、現在はPyTorchベースのMONAIに移行したようです。
https://monai.io/
これからはこちらに注目していきたいです。
UnityのTransformのように、SCNNodeのchildrenをfor-inで回せるようにする

UnityのTransformはIteratorによって自身のchild-Transformをfor-inで回すことができます

```csharp
using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    void Example() {
        foreach (Transform child in transform) {
            child.position += Vector3.up * 10.0F;
        }
    }
}
```
https://docs.unity3d.com/ja/2017.4/ScriptReference/Transform.html より

SceneKitのSCNNodeはchildNodesを持っているためchildNodeを取り出すときは次のようにfor-inします

```swift

for childNode in node.childNodes {
  // do anythings
}

```

UnityC#のようにfor-inしたい場合はカスタムイテレータを定義することで実現できます。


```swift

extension SCNNode: Sequence {
    public func makeIterator() -> some IteratorProtocol {
        SCNNode.ChildNodeIterator(self)
    }
}

extension SCNNode {
    struct ChildNodeIterator: IteratorProtocol {
        private let node: SCNNode
        private var index: Int = 0
        
        init(_ node: SCNNode) {
            self.node = node
        }
        
        mutating public func next() -> SCNNode? {
            defer { index += 1 }
            guard index < node.childNodes.count else { return nil }
            return node.childNodes[index]
        }
    }
}
```

これで

```swift
for childNode in node {
  // do anythings
}
```

と実行できるようになりました
Mindを紹介していただきありがとうございます

Snowman-sさん、こんにちは。スクリプツ・ラボの片桐です。
Mindを紹介していただきありがとうございました。
細かなことで恐縮ですが、
「Mindによるバイナリ操作」の投稿で気がついたことがありました。
処理単語「バイト数指定で文字列化」の定義中、「変換後文字列」という文字列実体変数を定義されておられますが、局所変数での定義なので、この処理単語から上位に戻ったときに値が不定となってしまいます。（たまたまうまく動作することもあるかも知れませんが）
この変数は処理単語の外（処理単語の直前など）に出して大域変数としていただけると良いかと思います。
【iOS Firebase Dynamic Links】アプリインストールを経由するときに注意すること

# はじめに
Firebase Dynamic Links を用いて、 Web ページ等から AppStore を経由して新規インストール、その後の起動でリンクに基づいた処理をアプリで行う場合の注意点等をまとめます。
基本的な実装方法に関しては日本語ドキュメント等もありますのでそちらをご覧ください。

なお、 iOS10 以降をサポートバージョンとするアプリを前提として話を進めます。
また、 Firebase Dynamic Links によって流れてくるリンクのことを別のリンクと区別するためにここでは Dynamic Links と呼ぶことにします。

# 呼ばれるメソッドが違う
> 最後に、application:openURL:sourceApplication:annotation: メソッド（iOS 8 以前）と application:openURL:options: メソッド（iOS 9 以降）で、アプリのカスタム URL スキームを通して受信したリンクを処理します。これらのメソッドが呼び出されるのは、アプリが iOS 8 以前でリンクを受信したときと、iOS（バージョンを問わず）にアプリをインストールした後にアプリが初めて開かれたときです。

[iOS でダイナミック リンクを受信する](https://firebase.google.com/docs/dynamic-links/ios/receive?hl=ja)

iOS のサポートバージョンによって実装方法も異なってくることは多くの方がご存知だと思いますが、「アプリをインストールした後にアプリが初めて開かれるとき」も関係してくる話であることを見落としてはいけません。

```swift
// インストール後2回目以降で Dynamic Links が来る
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    // DynamicLinks.dynamicLinks().dynamicLink(fromUniversalLink: URL) で判別
}
// インストール後初起動で Dynamic Links が来る
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
    // DynamicLinks.dynamicLinks().dynamicLink(fromCustomSchemeURL: URL) で判別
}
```

ここでもう一つ注意したいのが、 **インストール後初起動では必ず Dynamic Links が流れてくる** という点です。なので、インストール後初起動のタイミングで行う処理がなかったとしても Dynamic Links を受信可能にしたアプリの場合、 url のパース処理で Dynamic Links が流れてくることを考慮しなければなりません。
ちなみに、Web ページ等から該当のリンクを経由していない場合に流れてくる Dynamic Links は url が nil になっています。

# プレビューページスキップは非推奨
AppStore もしくはアプリに直接遷移させることもできますが、推奨しないとコンソールにも書かれています。実は、 AppStore を経由した場合に、アプリ側に流れてきた Dynamic Links がどれだけ信頼できるものであるかを決めるのに役立っているのがこのプレビューページとのことです。
また、スキップした場合に挙動が安定しない（リンクが渡ってこない）などが発生する可能性もあるため、基本的にはプレビューページは有効にしておくべきと言えます。

プレビューページを通過後にクリップボードを確認してみると、その仕組を少し覗き見ることができるかもしれません。

# 動作確認
AppStore からインストールされる必要はありません。
一度対象のアプリをデバイスから削除しておき、その状態でリンクを踏んで AppStore まで遷移後、 Xcode でいつもどおり実機インストールを行うことでインストール後初起動の挙動を再現することが可能です。
また、 TestFlight などでインストールするときも Xcode でインストールするときとタイミングは同じなのでテスターさんも比較的簡単に動作確認を行うことが可能です。

# おわりに
実際のプロダクトで動作確認を行ったときの挙動をもとに記事にしているため、環境によっては異なる挙動を見せるかもしれません。挙動の違いや、間違いと感じる箇所があればコメント等よろしくお願いします。
rubocop --auto-gen-config で、LineLengthのMaxを変更せず指摘に該当するファイルをexcludeする

# 困ったこと
RuboCopで既存コードへの指摘を除外することができる `--auto-gen-config` コマンドにおいて、生成されるrubocop_todo.yml内の **Layout/LineLength** のMax値はプロジェクト内の最大文字数が指定されてしまうので困った。

プロジェクト内の最大文字数が309文字の行があった場合、以下のようなルールが作られる。

```rubocop_todo.yml
Layout/LineLength:
  Max: 309
```

そうすると、rubocop.ymlでMaxを120などに設定していても実際に適用されるのは309文字の制限となってしまい、将来的にガンガン120文字以上の行が作られてしまう😭😭

# 解決策

以下のオプションで実行すればいい。

```
rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 999999
```

# 何をやってるの

## --auto-gen-only-exclude
このオプションを追加することで他のcopのようにファイルを除外することができる。しかし除外されたファイルの数後述する `exclude limit` よりも多い場合、デフォルトのようにMaxパラメータを変更してしまう。

## --exclude-limit 999999
このオプションを追加することで除外するファイル数を変更することができる。デフォルトは15で、このlimitより除外するファイルが多い場合はcop自体をfalseしてしまう。999999は適当な数字で、プロジェクト内のファイル数より十分に大きい数を指定すればいい。

ドキュメントに全て書かれています。
https://rubocop.readthedocs.io/en/latest/configuration/#automatically-generated-configuration

# 終わり
これで一行が長いコードが生まれる前に消し去ることができる…！そして平穏が訪れた。

## 実行環境
```
ruby: 2.6.1
rails: 6.0.2.1
rubocop: 0.78.0
```
Vuexでアプリケーションの状態を管理する


# Vuexとは

データとその状態を一括管理する「**状態管理**」のための拡張ライブラリ。

アプリケーション規模が大きくなり、コンポーネントの数も増えていくと、
同じ「data」にアクセスしたいという場合、`親->子->孫->曽孫`のように「props」「emit」を受け渡していかなければなりません。Vuexを使用すると**どのコンポーネントからでも同じ「data」にアクセスできるようにアプリケーション全体としてのリアクティブプロパティを定義し管理することができます。**


┏━┳━┳━┳━┓
┃　　　　　　　┃
┃ コンポーネント ┃
┃　　　　　　　┃
┗━┻━┻━┻━┛

自動的に反映　↑   

┏━┳━┳━┳━┓
┃　　　　　　　┃
┃　**Vuex**　　　　┃
┃　　　　　　　┃
┗━┻━┻━┻━┛

↓　自動的に反映

┏━┳━┳━┳━┓
┃　　　　　　　┃
┃ コンポーネント ┃
┃　　　　　　　┃
┗━┻━┻━┻━┛

# storeの作成

storeはアプリケーション内に作った「**仮想のデータベースのようなもの**」と思ってください。

```index.js
import Vue from 'vue';
import Vuex from 'vuex';
import store from './store'; //追加

Vue.use(Vuex);

new Vue({
  el: '#app',
  store, // ストアをVueアプリケーションに登録する
  components: {
    MyApp
  },
});

```

**単一コンポーネント毎に「store.js」を読み込む必要がある**ため、ストアのインスタンスを
Vueアプリケーションのルートに登録することで、コンポーネントのインスタンスプロパティを `$store`としてどこからでも使用できるようになる。

`Vue.use(プラグイン名)` **プラグインを使うための記述**
new インスタンス生成前に読み込まれる。

```store.js

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state){
      state.count ++
    }
  }
})
export default store
```

# Vuexの基本的なイメージ


![vuex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446210/67ed1d8f-230d-6002-aa69-8321a3c669da.png)


コンポーネントから`State（状態)`を更新したい場合、`アクション` と `ミューテーション` を使います。

## State （ステート)

ステートは、ストアで管理している状態そのもの。
**ステートは`ミューテーション`以外の場所から変更することはできません。**


## getter （ゲッター）

**ステートを取得するため**の算出データであり、`computed` `muthods` の中間の様な存在。
JavaScriptファイル内で使える`computed`というイメージ。

## Mutations （ミューテーション)

**ステートを変更できる唯一のメソッド**であり、コンポーネントでいうところの`methods`
引数として、`state`と`payload（コミットからの引数）`

### Commit (コミット）
ここでのコミットは、**登録されている`ミューテーション`を呼び出す`インスタンスメソッド`。**
コンポーネントでいうところでいうところの`$emit`と似ていて、任意で引数を取れる。

## Actions （アクション)

非同期処理を含めることのできるメソッド。
**データの加工や非同期処理を行い、その結果をミューテーションへコミットする。**

### dispatch (ディスパッチ)

登録されているアクションを呼び出す`インスタンスメソッド`。
別のアクション内からも使用できる。任意で引数を持たせることができる。
【Metabaseチュートリアル】Question・Collection・Dashboardを理解しよう

[次 >(Collectionを作ってみよう)](https://qiita.com/k_0120/items/4606f56854aebd4e0348)

# このチュートリアルで理解できること

- Metabaseを利用する上で出てくる、Question・Collection・Dashboardの用途について


# Metabase(メタベース)とは

- MetabaseはOSSのBIツールです。
- 抽出したデータを様々な形式のグラフに変形させたり、定点観測のためのダッシュボードを作成することができます。


# Question(クエッション)とは

- データ抽出した結果の1つ1つをQuestionと呼びます。
- Metabase上でのデータ抽出は、SimpleQuestionやCustomQuestionのようなGUIを利用するパターンと、NativeQueryによるSQLを直接書くパターンが存在します。
- 前者のGUIを利用する場合、作成されたグラフの一部を切り取り、更に深堀りすることもできます。(残念ながらNativeQueryの場合、この機能は使えません。2019/12/27現在)
- 抽出したデータは、デフォルトでは表形式になっていますが、様々なグラフ形式に変換することができます。
- 結果が表示された時点でSQLがハッシュ化され、URLのパラメータに追記されます。
- 利用を続けていくと、用途不明のQuestionが増えていくため、アドホックな分析のためのデータであれば、保存せずにURLを共有する運用をおすすめします。


# Collection(コレクション)とは

- フォルダのようなもので、作成したQuestionやDashboardを保存する場所です。
- Collectionの中に、Collectionを作り、階層構造を作ることもできます。
- 初めてMetabaseにログインすると、そのユーザーしか参照することができないパーソナルCollectionが自動生成されます。


# Dashboard(ダッシュボード)とは

- 作成したQuestionをカード形式で複数枚並べられるページです。
- 参照権限が付与されているCollectionの中にあるQuestionであれば、どの階層に存在してもDashboardに追加することができます。
- 追加したQuestionのサイズを変更したり、位置を変更することもできます。


# イメージで復習する

- Questionはデータ抽出した1つ1つの結果のことを指します。
- Collectionの中にQuestionやDashboardを保存することができ、階層構造を作ることもできる
- 参照権限が付与されているCollectionの中にあるQuestionであれば、どの階層に存在してもDashboardに追加することができます。

![無題のプレゼンテーション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311026/765ed491-9157-08d0-7aca-e93dde7fbe75.png)
moment.jsでY歳Mヶ月で年齢計算をする

2020/01/31: バグ発見によりコード修正

# JavaScriptで何歳何ヶ月を計算させたい

### 問題提起
年齢計算で検索するとたいてい`moment().diff(birthday, 'years')`でn歳です！
だけの記事が出てくる。（探せば色々あるけど）

### やりたいこと
moment.jsを使って生年月日から何歳何ヶ月という情報を出したい

## 結論

```javascript
import moment from 'moment'

const birthday = '1996-01-01' // 本来はここに生年月日データを当てはめる
const today = moment()

// 12で割って年を出す
let y = Math.floor(moment.duration(today.diff(birthday)).as('months') / 12)
// 12で割った余りで月を出す
let m = Math.floor(moment.duration(today.diff(birthday)).as('months') % 12)

// マイナス値であれば0を再代入する
y = Math.sign(y) !== -1 ? y : 0
m = Math.sign(m) !== -1 ? m : 0

const age = y + '歳' + m + 'ヶ月'
console.log(age) // y歳mヶ月
```

## 応用 死んだ日付を加味した年齢

```javascript
import moment from 'moment'

const birthday = '1996-01-01' // 本来はここに生年月日データを当てはめる
const deathday = deathday // 死んだ日付はNULLの可能性あり

// 生きている最新の日付
const latestDate = deathday ? moment(deathday) : moment()

// 12で割って年を出す
let y = Math.floor(moment.duration(latestDate.diff(birthday)).as('months') / 12)
// 12で割った余りで月を出す
let m = Math.floor(moment.duration(latestDate.diff(birthday)).as('months') % 12)

// マイナス値であれば0を再代入する
y = Math.sign(y) !== -1 ? y : 0
m = Math.sign(m) !== -1 ? m : 0

const age = y + '歳' + m + 'ヶ月'
console.log(age) // y歳mヶ月
```

## 結論:::修正前の駄目な例

1ヶ月のズレやマイナス値など様々なバグが出る
特に`const y = today.diff(birthday, 'years')`はきっちり1年を出せないなどの問題が発覚

```javascript
import moment from 'moment'

const birthday = '1996-01-01' // 本来はここに生年月日データを当てはめる
const today = moment()
const y = today.diff(birthday, 'years')
const m = Math.floor(moment.duration(today.diff(birthday)).as('months') % 12)
const age = y + '歳' + m + 'ヶ月'
console.log(age) // y歳mヶ月
```

## 解説:::修正前の解説

```javascript
import moment from 'moment'

const birthday = '1996-01-01' // 本来はここに生年月日データを当てはめる
const today = moment()
const y = today.diff(birthday, 'years')
const m = Math.floor(moment.duration(today.diff(birthday)).as('months') % 12)
const age = y + '歳' + m + 'ヶ月'
console.log(age) // y歳mヶ月

// #### それぞれ時点の値 ####

// 1996年1月1日0時00分00秒からのミリ秒差分
today.diff(birthday) // 756908681438 ミリ秒

// momentのduration関数で期間オブジェクトに変換
moment.duration(today.diff(birthday))

// 期間を月指定で出力
moment.duration(today.diff(birthday)).as('months') // 287.82577535549063 ヶ月

// 上述の値を12で割った余りを出します。
// 小数点がありますがこれがy歳mヶ月のmになります
moment.duration(today.diff(birthday)).as('months') % 12 // 11.82605117984781

// Math.floorで小数点を省いて月の完成
Math.floor(moment.duration(today.diff(birthday)).as('months') % 12) // 11 ヶ月
```
slickのブレークポイント変更時に処理を追加する方法

## やりたいこと
ブレークポイントでスライダーの数が変わった時に高さを再調整したい

jquery.matchHeightで高さを調整しているが、デフォルトでは読み込んだ時にしか適用できていなかった。
そのため、画面サイズが切り替わってスライドの表示数が変わった時に高さが揃っていなかった。

ソース全体

```js
/*
 * This file is use slick
 *
 * http://kenwheeler.github.io/slick/
 *
*/
import 'slick-carousel'
import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'
import '../../lib/jquery.matchHeight-min'

document.addEventListener('DOMContentLoaded', function () {
  itemSlider()
})

function itemSlider () {
  const slider = $('.slick-slide-list')
  slider.slick({
    accessibility: true,
    autoplay: false,
    speed: 400,
    draggable: true,
    fade: false,
    arrows: true,
    slidesToShow: 5,
    slidesToScroll: 5,
    swipe: true,
    vertical: false,
    adaptiveHeight: true,
    responsive: [
      {
        breakpoint: 1200,

      },
      {
        breakpoint: 896,
        settings: {
          slidesToShow: 4,
          slidesToScroll: 4,
        },
      },
      {
        breakpoint: 768,
        settings: {
          slidesToShow: 3,
          slidesToScroll: 3,
        },
      },
      {
        breakpoint: 480,
        settings: {
          slidesToShow: 2,
          slidesToScroll: 2,
        },
      },
    ],
  })
  $('.slick-slide .tw-item-list-body ').matchHeight()

  slider.on('breakpoint', function (event, slick, breakpoint) {
    $('.slick-slide .tw-item-list-body ').matchHeight()
  })
}

```

今回追加したのはレスポンシブ設定時のブレイクポイントに達した際にある任意の処理をするようにした。

```js
slider.on('breakpoint', function (event, slick, breakpoint) {
    $('.slick-slide .tw-item-list-body ').matchHeight()
  })
```
Flutter iOS dark modeが設定された環境では起動画面を黒くする

iOSの起動画面のdark mode対応について、LaunchScreen.storyboardで、背景色の設定を System Background Colorに設定しておけば自動的に通常時👉白、ダークモード時👉黒になるとのこと。

手順をまとめます。

### 1. XCodeを開く

プロジェクトディレクトリの

> ios/Runner.xcworkspace

をダブルクリックしてXCodeを開きます。

### 2. LaunchScreen.storyboardを選択します。
![a1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6327/143f8546-32f0-1419-9a3f-324d46ec099c.jpeg)

### 3. Viewを選択します。
![a2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6327/8cc5f70b-3ce2-509f-1118-31cd32b949a0.jpeg)

### 4. Backgroundの設定を変更
![a3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6327/cd27af52-0afc-508f-91ce-64fba10d86c5.jpeg)

プルダウンでカラーリストを開き、**System Background Color** を選択します。

以上

### まとめ
dark modeで使っていると、起動画面だけ白く表示されるアプリがまだ多いです。目がやられそうになるので自分が作るアプリではそうならないようにしたいと思い対応しました。

Flutterプロジェクトの説明として書きましたが、通常iOSプロジェクトの設定部分です。
希望のカラーを指定したいところですが、ひとまずこの調整で簡単対応できるので良さそうです。

### 参考ページ
- [Dark mode launch screen in iOS 13](https://stackoverflow.com/questions/56892053/dark-mode-launch-screen-in-ios-13)
JSON形式のtailfみたいなログストリームをjqで表示する

ログファイルを`tail -f`や`docker logs -f`で見たりする事ありますよね。もし、そのログファイルがjsonフォーマットだったら各ログ毎にjqしたものを見たいですよね。

```shell:logs.sh
docker logs --tail 10 -f greatapp | while read line
do
  echo "$line" | jq -R -r '. as $line | try fromjson catch $line'
done
```
これがその答えです。

### see also
How to skip non-json lines?
https://github.com/stedolan/jq/issues/884

𝑸: Is there a way to have jq keep going after it hits an error in the input file? Can jq handle broken JSON?
https://github.com/stedolan/jq/wiki/FAQ#general-questions
Cloud RunにCLIでDockerコンテナをお手軽にデプロイする方法

Google Cloud Platformのステートレスコンテナ実行サービスであるCloud Runに、DockerコンテナをCLIでお手軽にデプロイするスクリプトを作ってみました。
コードはGitHubにあげています。
https://github.com/yolo-kiyoshi/cloud_run_cli

```:ディレクトリ構成
root/
　├ .env
　├ .param
　├ Dockerfile
　└ scripts/
　　 └ build_and_push.sh
　　 └ deploy_cloud_run.sh
```

# 前提

- `gcloud`コマンドをインストールしていること
- Cloud Run APIを有効化していること

参考：[こんなに簡単にコンテナをデプロイできちゃって良いんですか？GCP Cloud Runでコンテナを爆速デプロイしてみた](https://dev.classmethod.jp/cloud/gcp/gcp_cloud_run_tutorial_sys/)

# Cloud Runとは
Classmethodさんが簡潔にまとめてくれていますので引用します。

> Cloud Runは､HTTPでリクエストを受ける事が可能なステートレスコンテナを実行できるサービスです｡
Knativeによって構築されており､Cloud Runによる管理または､GKEクラスタ上にデプロイする事が可能です｡前者の場合､開発者がインフラストラクチャを意識する必要が無く､スケールアップ/ダウンも自動で行われます｡この為､開発者はコア機能の実装にのみ集中できます｡
(引用：https://dev.classmethod.jp/cloud/gcp/gcp_cloud_run_tutorial_sys/)

# DockerイメージのビルドとGCRへのプッシュ
DockerfileをもとにDockerイメージをビルドし、Container Repositoryにイメージをプッシュします。
（今回は軽量な`alpine`をベースにテキトーなDockerfileを用意しています。）

```:Dockerfile
# for sample
FROM alpine:latest
```


```sh:build_and_push.sh
#!/usr/bin/env bash

set -eu

project=${1:-}
image=${2:-}
tag=${3:-latest}

fullname="gcr.io/${project}/${image}:${tag}"

# read setting file
docker build -t ${image}:${tag} .
docker tag ${image}:${tag} ${fullname}
docker push ${fullname}
echo "image successfully pushed to: ${fullname}"
```

上記をもとに、イメージ名とタグを引数に`build_and_push.sh`を実行することで、ビルドとGCRへのプッシュができます。

```
./scripts/build_and_push.sh <project_name> <image_name> <tag>
```

# Cloud RunへのDockerコンテナのデプロイ
環境変数`.env`とCloud Run実行引数`.param`を用意します。

```:.env
PATH1=hoge
PATH2=fuga
```

```:.param
# set platform from [managed, gke, kubernetes]
PLATFORM=managed
# set region from [managed, gke, kubernetes]
REGION=us-central1
```


```sh:deploy_cloud_run.sh
#!/usr/bin/env bash

set -eu

service=${1:-}
project=${2:-}
image=${3:-}
tag=${4:-latest}

fullname="gcr.io/${project}/${image}:${tag}"

# read param
env=$(cat ./.env  | tr '\n' ',' | sed -e 's/,$/\n/g')
. .param

gcloud run deploy ${service} \
    --image=${fullname} \
    --platform=${PLATFORM} \
    --region=${REGION} \
    --update-env-vars ${env}
```
上記をもとに、サービス名、イメージ名、タグを引数に`deploy_cloud_run.sh`を実行することで、GCR上のDockerイメージからCloud Runにコンテナをデプロイできます。

```
./scripts/deploy_cloud_run.sh <service_name> <project_name> <image_name> <tag>
```

## 注意
+ .envに記載した環境変数は、./scripts/deploy_cloud_run.sh実行時に--update-env-varsパラメータにカンマ区切りで展開されので、コメントは入れないこと
+ --clusterなどの実行パラメータを追加する場合は./scripts/deploy_cloud_run.shにも合わせて追加すること
「現場で使える！TensorFlow開発入門」本の検証用GCP環境構築(ひよっこ向け)

[現場で使える！TensorFlow開発入門](https://www.seshop.com/product/detail/21540)という本を読みながら深層学習モデルの作り方を学んでいます。

その[補足資料](https://www.shoeisha.co.jp/book/download/9784798154121)に、GCPの環境構築の手順とスクリプトがあるのですが、こいつを進めていくと `nvidia-docker` コマンドのところで以下のエラーにぶつかってしまいました😭

```
docker: Error response from daemon: create nvidia_driver_440.33.01: VolumeDriver.Create: internal error, check logs for details.
```

GCP&Dockerド素人の私には、なんのこっちゃです🙄

が、幸いにもNVIDIAの中の人が[NVIDIA Docker って今どうなってるの？](https://qiita.com/ksasaki/items/b20a785e1a0f610efa08)という記事を書いてくれていたおかげで、サンプルが動作する環境を構築することができました🙇‍♂️

同じように躓いている人もいるかもしれないので、手順のメモを残しておきます。

## 1.とりあえず補足資料に従って環境構築する

上記のエラーが起こるところまで、環境構築をしてください。
Docker 19.03がインストールされた状態になっているはずです。

## 2.Docker 19.03 以降の環境で前だけを見て生きる

NVIDIA中の人記事にある、Docker 19.03を前提とした手順を実行します。

### 2.1.最新のNVIDIAドライバをインストール

下記リンク先に表示される手順をそのまま実行します。

https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1604&target_type=debnetwork

### 2.2.nvidia-container-toolkit パッケージをインストール

下記リンク先のセクション `QuickStart > Ubuntu 16.04...` にある手順をそのまま実行します。

https://github.com/NVIDIA/nvidia-docker/tree/master#quickstart

## 3.Dockerを起動

補足資料にある `nvidia-docker run` ではなく、通常の `docker run` を使います。このときオプション `--gpus all` を加えて実行します。

```
$ sudo docker run --gpus all --rm --name tfbook -p 80:8888 -p 6006:6006 -v /notebooks:/notebooks tfbook
```

## お試し

Chapter.11のサンプルを動かしてみました。

<img width="45%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/10cdae0d-e8c8-cb52-5f4e-78b90dc8032d.png"><img width="45%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25727/23afb64c-553c-377b-2a48-c79a2a092b83.png">

うん、着々と学習が進んでいるように見える...！！

## おまけ？

GCPでGPUありのVMインスタンスを作る場合、使用制限の解除(割り当て)が必要なのですが、以下のエラーがでる場合、GPU個別の割り当てと、グローバルな割り当ての両方を申請する必要があるようです。

```
Quota 'GPUS_ALL_REGIONS' exceeded. Limit: 0.0 globally.
```

> あと、本にはGCPのGPU使用は無料クレジットの対象外〜みたいなことが書いてありましたが、使えるようになっている気がする。。。