OmniAICreator/Qiita-1.07M huggingface dataset:
Google Colabが良すぎる（使い始めが簡単すぎ！三手ぐらい。。。。）お薦め！！！

#概要
（日付2020/12/26、WindowsPCにて）
**Google Colab**が良すぎる（まず、使い始めが簡単すぎ！三手ぐらい。。。。）
なので、記事にする。

「google colab」とかで検索すると、以下などが出るはず。

https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja

そこに入ると、あとは、使うだけ。

導入しやすさ競争の時代の全力の企業努力なんだと思いますが、
**簡単すぎますーー。**


#動かしてみた例

最初の画面は、こんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635153/accea3f8-6ea4-6605-70a6-bfc85584507a.png)

少し、スクロールすると、以下のような処理のpythonが、プレイボタン（左向き▲ボタン）を押すと実行される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635153/2494650e-bdd0-5bb1-ef82-5edb03d1af62.png)

#まとめ
使うとか、使わないとかは、一回、動かしてからがいいかも。
三手で動きます。


  * 最初、「google colab」で検索
  * 次、検索された（たぶん最初？の)候補をクリック
  * その次、どれか、画面内のプレイボタンを押す。
  （そこに記載されているPythonが実行される）

**すげーーー、ちょっと、めちゃくちゃですね。。。簡単すぎ。**
（心を強くもたないと、お金、払いそうーーー。ドライブ容量とかで。）

実際に役立つかは、ボチボチつかってみます。
（同日、日付2020/12/26、
ボチボチ、github公開の深層学習の例とかを動かすとかで、使っていますが、それなりに難しい。。。そこは、パターン毎のやりやすい方法を身に着ける必要があるかと。。。
簡単なのは簡単に動くと思います。）
あれ、認証・認可についてちゃんと説明できるっけ。。

# はじめに
* なぜこんな記事を書こうと思ったかというと、認証・認可について僕がなあなあになっている気がしたので、改めて自分の中で整理したかった為です。

## 認証 (Authentication)
あるウェブサイトにユーザがログインした際に誰なのかを確認することを指します。
本人確認をイメージすると分かりやすいかもです。

## 認可 (Authorization)
認証したユーザに対して、リソースのアクセス権限をどこまで与えるか決定することです。

## フェデレーテッドログイン
これは、例えばあるサービスがあった場合にそのサービス以外が管理するIDを利用してログインする方法です。

# 実装パターン
以下に実装パターンをいくつか紹介しますが、だいたい今はOAuth 2.0かOpenID Connect(別記事で書きます。)ですねw

## シングルサインオン
シングルサインオンは、各システム間のアカウント管理を別々に行わず、一度認証したら全システムが全て有効になるような仕組みです。
また、シングルサインオンにはいくつか方法があります。
一つ例にあげるのであれば、あるサービスがあった時、そのサービスが認証サーバにユーザIDとパスワードを利用しアクセスしにいく方法があります。

## kerberos認証
kerberos認証は、はじめにユーザIDとパスワードを使って認証し、以降は身元証明書（チケット）を使って認証する方法です。
もう少し詳しく説明すると、ユーザがサービスを使用するときは、まずチケット保証サーバ(TGS)へのアクセストークンである**TGT(Ticket Granting Ticket)**と**セッションキー**を取得します。
その後、**TGT**と**セッションキー**をチケット保証サーバに送り、クライアントからサーバにアクセスするための**チケット**と**セッションキー**をもらいます。
そして、ユーザはこの**チケット**と**セッションキー**をサービスに送るということで認証をする仕組みとなります。

## SAML
* SAML(Security Assertion Markup Language)は、HTTP/SOAPを前提としたシングルサインオンの仕組みです。

* この仕組み、奥深いので実装方法については、HTTP POST /Redirect バインディングに絞り説明します。

* 特徴としては、クッキーを使ってセッションを管理するウェブの仕組みに準拠しており、ドメインをまたぐサービス間でシングルサインオンができます。

### 構成要素
#### ユーザ
ブラウザを操作している人

#### 認証プロバイダー (IdP)
ID管理をするサービス


#### サービスプロバイダー (SP)
ログインしたいサービス

### HTTP POST /Redirect バインディングによる実装

下記のような流れで認証/認可を行います。

* **1.ユーザはログインされていない状態で、ユーザがSPにアクセスします。**

* **2.SPはブラウザにRedirectのhttp status(3XX)を返します。**

* **3.IdPはユーザが認証の要件を満たすログインコンテキストをIdpに持ってるかを判断します。
もしなければ、ユーザに正しいID/Passwordを提供するようにブラウザから要求します。**

* **4.ユーザはID/Passwordを入力し、IdPでそのユーザに対するセキュリティコンテキストが作成されます。**

* **5.IdPはユーザのセキュリティコンテキストを表すSAMLアサーションを作成します。**

* **6.ブラウザは5で取得したSAMLアサーションをSPに送信するHTTPリクエストを発行します。**

* **7.ユーザがリソースにアクセスする権限があるかチェックをし、通ればリソースをブラウザに返します。**

[引用](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/60f2a0a9-80e9-8089-902a-3543fadfdcf6.png" width="500px">


## OAuth2.0
### 構成要素
#### 認可サーバ
ユーザ情報を持つウェブサービス。
ユーザは、この認可サーバのアカウントを持っています。

#### リソースサーバ
ユーザが許可した権限で自由にアクセスできる対象。

#### クライアント
ユーザが新たに利用したいサービスやアプリケーション

### 認証フロー
**いくつかフローがあるのですが、今回は抜粋して通常のフローのみに絞り説明します。**

#### Authorization Code
通常のフローです。
今回はPKCEは置いておきます。

流れとしては、認可サーバに認可リクエストを送信し、認可コードを受けとります。
その認可コードを再度、認可サーバに送信してアクセストークンと交換するようなフローになります。


* アクセストークン発行までのやり取り
![図１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/214e6ef2-f255-9fca-cd0e-73d4acdc3932.png)

* リソース返却までのやり取り
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/596e7500-e5af-0501-e4fc-c793af18683f.png)



# 参考文献
https://dev.classmethod.jp/articles/authentication-and-authorization/
http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html
https://blog.cybozu.io/entry/4224
ChromebookでVScodeを入れて日本語が入力できるまでの話 in 2020/12

##書いてあるように2020年12月現在の話です
###使っているもの
HP Chromebook x360 14b
###やったこと
まずChromeOSの設定よりLinuxをインストール。これは簡単。
次にターミナルを立ち上げて、

```bash
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install fonts-ipafont fonts-ipaexfont fcitx-mozc
```

とし、とりあえずなんか日本語フォント(NotoがいいならNotoを入れたり)とMozcを入れます。そうするとLinuxアプリケーションの中にFcitxができるので起動させ、

```
$ fcitx-config-gtk3
```
(2022/12/30編集 fcitx-configtool だと言語が設定できなかったため。）

でMozcを追加します。標準ではトリガーキーがCtrl+Spaceなのでそれが嫌いだったらトリガーをお好きなように変えましょう。その後に

```
$ fcitx-autostart
```

とします。次にVScodeを入れます。

```
$ wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
$ sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/
$ sudo sh -c 'echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
```

として、

```
$ sudo apt update
$ sudo apt install code
```

で入ります。Fcitxが入ったLinuxアプリケーションの中にVScodeもできあがります。立ち上げます。UIが英語ですね。そのままでいい人はそのままで、「日本語じゃないといやー」って人は、日本語UI拡張機能(拡張機能を探すところで「japanese」で探すとすぐ出てきます)を入れます。

これで終了！終了です！MyricaやHackGen等入れたい人はターミナル上でwgetした後にunzipし、fontファイルを`~/.fonts`ディレクトリに入れて`$ fc-cache --force --verbose`をすれば使えるようになります。

クラウド上でなんでもしてやるところのOSのChromeOSでローカル上でなんやかんやする身も蓋もない話でした。今ではクラウド上でエディター環境があるのでよっぽど強い動機がなければクラウド上のエディターを使って慣れるのが一番なのではないでしょうか。

参考にしたところ
[ChromebookのLinux開発環境構築](https://qiita.com/suzuki_sh/items/adead0fd9adefec112af)
Redis の WebAPI (Nuxt.js)

こちらで定めた仕様を満たすサーバーサイドのプログラムです。
[Redis の WebAPI を作成](https://qiita.com/ekzemplaro/items/1746b31beef382023f2d)

こちらのプログラムを改造しました。
[Nuxt.js で簡単な WebAPI を作成](https://qiita.com/ekzemplaro/items/a259a4652108b838a62c)

```js:api/index.js
// ---------------------------------------------------------------
//	api/index.js
//
//						Dec/26/2020
// ---------------------------------------------------------------
const express = require("express")
const app = express()

app.use(express.json())
app.use(express.urlencoded({ extended: true }))


const redis = require("redis")
const client = redis.createClient(6379,'localhost')

// ---------------------------------------------------------------
app.get("/", function(req, res) {
	const json_str = '{"en": "Hello World","ja": "こんにちは","fr": "Bonjour"}'
	res.send(json_str)
})

// ---------------------------------------------------------------
app.post('/read', function(req, res) {
	const key = req.body.key

	client.get (key, async function (err, reply)
		{
		console.log ("*** read *** start ***")
		var dict_aa = {}
		dict_aa[key] = reply

		const str_out = JSON.stringify(dict_aa)

		await res.send(str_out)

		console.log ("*** read *** end ***")
		})	

})

// ---------------------------------------------------------------
app.post('/list', function(req, res) {

	client.keys ('*', async function (err, reply)
		{
		console.log ("*** list *** start ***")

		const keys = reply
		const str_out = JSON.stringify(keys)
		await res.send(str_out)

		console.log ("*** list *** end ***")
		})	

})

// ---------------------------------------------------------------
app.post('/insert', function(req, res) {

	client.keys ('*', async function (err, reply)
		{
		console.log ("*** insert *** start ***")

		var key = ""
		var value = ""

		if (req.body.key) {
			key = req.body.key
			}

		if (req.body.value) {
			value = req.body.value
			}

		client.set(key, value, redis.print)
		await res.send(value)

		console.log ("*** insert *** end ***")
		})	

})

// ---------------------------------------------------------------
module.exports = {
	path: "/api/",
	handler: app
}

// ---------------------------------------------------------------
```

サーバーの実行

```bash
yarn dev
```

curl によるテストスクリプト

```bash:go_read.sh
#
curl -X POST http://localhost:3000/api/read -d key="t1851"
echo
#
curl -X POST http://localhost:3000/api/read -d key="t1852"
echo
#
curl -X POST http://localhost:3000/api/read -d key="t1853"
echo
#
curl -X POST http://localhost:3000/api/read -d key="t1854"
echo
#
```

```bash:go_list.sh
curl -X POST http://localhost:3000/api/list
echo
#
```

```bash:go_insert.sh
curl -X POST http://localhost:3000/api/insert \
	-d key="t1851" \
	-d value="{"name": "宇都宮", "population": 49236, "date_mod": "2003-5-22"}"
echo
#
```
EX2:Google Recruit



# 講義サイト

[Google recruit problem, exp and prime](https://qiita.com/daddygongon/items/ba94b0f2a73990fc6a07)


# お題：数字の変換

自然対数の底eの値で連続する10桁の数のうち、最初に登場する素数を求める.
ただし、eは200桁までのみ使用する(それまでに出てくる).

```ruby
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190
```

これをテキストファイルに入れてプログラムから読み取る


# 実装例

```ruby
def prime?(n)
  (2..Math::sqrt(n).to_i).each do |i|
   if n % i == 0
     false
     break
   end
   true
  end
end

exp = File.read('napier.txt').delete('.')
digit = 10

(0..exp.length).each do |i|
  slice_dig = exp.slice(i, digit).to_i
  if prime?(slice_dig)
    puts slice_dig
    break
  end
end
```

1～9行目が素数判定用メソッド prime?
2,3,&#x2026;と少しずつ数を割っていく
割り切れたら素数じゃない、割り切れなかったら素数

11,12行目がファイルの読み取り.
e 200桁分は napier.txt に入れている.　小数点.は処理の邪魔なので削除.

14行目～最後の行がe分割用
200桁ある e を先頭から1つずつずらしながら切り抜いて素数判定を行う

ほぼ講義ページ通りに実装した

実行結果はこんな感じ.

```ruby
7427466391
```


# note

・素数判定を行うプログラム部分で、loopの範囲の記述を Math::sqrt(n).to\_i+1 としている.
　(2..n-1).each do |i| とするよりも計算時間が早くなる. ループ回数が減るため、高速化する
　のは自明だが、どうして Math::sqrt(n).to\_i+1 までのループでいいのかはよくわからなかった.
・ちなみに、この問題はGoogleのリクルート問題らしい.



------
 - **source** ~/grad_members_20f/members/batamon-427/Google_Recruit.org
react-chartjs-2でreduxのデータを表示する際、詰まった話

react-chartjs-2で２つのグラフを反映する際、useSelectorで各データの情報を取得できているのに何故か2個のデータがくっついて前週のデータのみグラフに表示された。

```javascript:Javascript
//...
const week_info = useSelector((state) => state.oneWeekDots); //今週の勉強時間 
//[3, 0, 0, 3, 0, 5, 0]
const lastweek_info = useSelector((state) => state.lastWeekDots); //前週の勉強時間
//[0, 0, 6, 0, 0, 0, 2.5]

return (
   //省略
        <Line
          data={{
          //...
            datasets: [
              {
                label: " # 今週の学習状況 ",
                data: week_info,
                //...
　　　　　　　　},
              {
                label: " # 前週の学習状況",
                data: lastweek_info,
              },
              //...
```
![react-chartjs-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875900/4d1d6ce6-af4f-6228-1bd5-5b4d3169af9b.png)

そこで変数week_info/lastweek_infoを空配列にスプレッド構文で展開して、変数
week_info_array/lastweek_info_arrayをグラフに与えたら反映された

```javascript:Javascript
//...
const week_info_array = [...week_info]
const lastweek_info_array = [...lastweek_info_array]

  return (
   //...
        <Line
          data={{
          //...
            datasets: [
              {
                label: " # 今週の学習状況 ",
                data: week_info_array,
                //...
　　　　　　　　},
              {
                label: " # 前週の学習状況",
                data: lastweek_info_array,
              },
              //...
```

![react-chartjs-2-B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/875900/dd65754a-4161-456d-ffe9-6ff2af6e8416.png)

react-chartjs-2のライブラリは、配列型の数値だと読み取ってくれるが、なぜスプレッド構文で展開した状態だと読み取ってくれるかは解決しなかった。まだまだ精進しないといけない。
MetabaseのHerokuへのDeploy方法

# 概要
簡易的なBIツールとしてのボタンをぽちぽちするだけですぐ使えるようになるHeroku&Metabaseは便利です。
Metabaseは少し重たい気もしますが、
各種DBやBigQueryなどのDWHとの接続も可能で
社内の人向けのView画面をすぐに構築したい時や
工数が足りなさそうな時などにオススメです。
ボタン一回押すだけで手軽に試せますのでよかったら是非触ってみてください！

# 手順
Heroku上にmetabaseをデプロイする方法を記載します。

１、下記サイトにいくと、
https://www.metabase.com/docs/latest/operations-guide/running-metabase-on-heroku.html

こんな感じのボタンがあるので、
![スクリーンショット 2020-12-26 1.51.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/0c5a8481-646b-86ca-8769-9eaaa6224a99.png)

押します。

２、アプリ名を聞かれるので入力し、Deploy Appを押します。
![スクリーンショット 2020-12-23 12.51.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/06c92fc4-354c-0b89-92bc-4d5eab3e0524.png)

３、ボタンの下にデプロイのプログレスが表示されるので待ち、
一番下のDeploy to Heerokuに濃い緑のチェックがついたら完了です。
viewやManageAppから普段のHerokuのアプリの画面に入ります。
![スクリーンショット 2020-12-23 12.54.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/950f67d7-c3bc-5b19-0d7c-8d7622853a40.png)

４、SettingのDomainsのところにURLがあるので、それを押すとmetabaseにアクセスできます。
![スクリーンショット 2020-12-26 1.55.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/c3f89bfd-5fb8-a290-fa84-4636f76eaeac.png)
ローディングが行われ、
![スクリーンショット 2020-12-23 12.55.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/2822f10b-e0aa-ff41-229d-ce5f0b63e18e.png)
完了すると、このような画面になります。
![スクリーンショット 2020-12-23 12.55.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320677/80c63f1b-6c2b-17fb-680f-01535a199c52.png)

５、あとはmetabaseの案内に従って、DBとの接続情報を入力すれば使用可能です。

# 終わりに
metabaseは便利で多機能なツールながらデプロイ作業は一瞬でした。
この記事が参考になりましたらと幸いです。
パスワード登録欄についてのイライラまとめ

# はじめに

こちらは[ベリサーブ Advent Calendar 2020](https://qiita.com/advent-calendar/2020/veriserve)の22日目の記事です。

現地時間は、12/22(月)の109時を回ったところです(爆)

みなさんはパスワード登録をしたことがありますか？この記事を読めている方に、今まで登録したことがない人はおそらくいないでしょう。

パスワード登録欄でイライラした経験はありますか？日頃から多数のシステムと向き合ってきたテスターの職業病故、よくイライラしています。

そこで、私がイライラするパターンを紹介し、最近出会った良い例も紹介します。

本稿は、イライラしたパスワード登録欄を共有することで、より良いデザイン、仕様に繋がることを目的としており、特定のシステムを批判するものではありません。

また、なぜパスワードでイライラしてしまうのかについて考えてみました。おまけにしましたので、興味があれば読んでいただければと思います。

前置きが長くなりましたが、いってみましょう！

# 本稿で扱わない内容

- 二段階認証、多要素認証など認証プロセスに関する議論

# イライラパターン

## 1. 許容する文字種が登録前にわからない

![Screenshot_20201226-124641723.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89320/e792f81b-6258-8564-f7d3-f53f9c4d1e38.jpeg)


何を入力すればいいねん。

入力して「続ける」ボタンを押してみるまで、許容される文字種や文字長をわかりません。


そして、入力後に怒られる... ぴえん

![Screenshot_20201220-190139155.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89320/62cbcdc1-78ef-df2f-77b9-a5ca1395e861.jpeg)



## 2. エラーの意味がわからない

何を入力すればいいねんパート２。

エラーが出るところまでは良いのですが、何をどう直せばいいのかわかりません。
海外系サービスにありがちなパタンです。


![Screenshot_20201128-030329234.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89320/af107239-b82e-2f53-86ea-b13993fc554e.jpeg)



## 3. コピペ禁止

最近では少なくなりましたが、未だにこうしたページは特に古くからあるWebサービスだと見られます。

手打ちで入力することしか想定されていないため、私のようなパスワードマネージャを使用したいユーザからすると、とても不便です。

個人的体感としては、金融系のサービスでよく見られる気がします。金融系サービスでは尚の事、コピペ禁止はやめていただきたいです^^;

## 4. パスワードを内部の最大文字長で切って登録するシステム

こちらも少ないですが、たまに見かけます。例えば、20文字のパスワードをユーザが設定しようとしていて、システムが16文字までであった場合、末尾4文字が無視されてしまいます。

実装の例としては、inputのmaxlengthタグで文字数を制限していると思われます。

こちらは、通常の入力をしていると気づく場合が多いのですが、上述のパスワードマネージャを使用している場合など直接入力を行わない場合、気づけず、ログインしようとした時点で入れない事態になったことがあります。

# いいパスワード登録画面の一例

これらのイライラを解消するには、どういったUIが良いのでしょうか。

最近見つけた好例をご紹介します。

![Screenshot_20201220-190321816.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89320/5909dcf8-ae52-ab6f-c5dd-da8434d8f941.jpeg)

入力欄にフォーカスを当てると、ヒントが表示されます。

また、メッセージも簡潔でわかりやすく良いですね。


__流石Qiitaさんや！__

# まとめ

いかがでしたか？

たかがパスワード、されどパスワード。

文字を入力するというセキュリティもあと10年後にはなくなっているといいなあ。。。


# おまけ:パスワード登録を難しくする理由を考えてみた

## 1.パスワードは複雑さが求められている


[IPAの記事](https://www.ipa.go.jp/security/anshin/mgdayori20160803.html)にもある通り、パスワードは使いまわししないことが求められています。

そこで、[同記事](https://www.ipa.go.jp/security/anshin/mgdayori20160803.html)では、共通する文字列である「コアパスワード」とサービス毎に識別子組み合わせることが推奨されています。


## 2.システム毎に許容する仕様が異なる

システム毎に許容されるパスワードは異なります。

例として、比較的多くの人に使われている、MicrosoftとAppleのアカウントのパスワードの要件を見てみましょう。

![Capture.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89320/46aff392-b6b8-6ee4-8b15-2a7e6351b34e.jpeg)

ご覧の通り、条件が異なります。この程度であれば、まだ、工夫次第でどうにかなりそうですが、記号を許容しないシステムや最大文字数が20以下のものも数多く存在します。

困ったものですね！

最後まで読んでいただきありがとうございました。
なでしこv3ブラウザ版用　HTMLテンプレートとplugin_mediaプラグイン

日本語プログラミング言語「なでしこ」のv3ブラウザ版で，音声，静止画，動画を扱うためのプラグイン。ローカルにあるファイルも扱える。ChromeBookでも使える。
そのためには，簡易エディタではなく，下のHTMLテンプレート内に「なでしこ」のプログラムを記述する必要がある。

## テンプレ

次のリンクを右クリックして「リンク先を保存」する。
テンプレート内の「プラグインを読み込む」辺りを修正すれば，他のプラグインを利用することももちろん可能。

- HTMLでなでしこを使うためのテンプレ [template.html](https://oza-jv.github.io/template.html)
- [plugin_media.js](https://oza-jv.github.io/plugin_media.js)

## 使い方

###（１）同一フォルダに全てのファイルを入れる。
自分のPC上（WindwosでもChromebookでも同じ）に，プログラムを保存するためのフォルダを作り，その中に，次のファイルを入れておく。

- なでしこのプログラムを実行するためのHTMLファイル（テンプレを修正したもの。ファイル名は適宜修正しておく。）
- 利用したい音声ファイル，画像ファイル，動画ファイル。　※ファイルサイズが小さい方がスムーズに動作する。
- plugin_media.jsをダウンロードして同じフォルダへ入れておけば，ネットワーク環境が無くても実行可能。

> フォルダの例
![pic1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/942750/f6580745-5d3e-797d-dd9a-f2f51781ef2d.png)

###（２）テンプレHTMLを修正して，自分のプログラムファイルを作る。

- テンプレ内の`<body>`要素の前半で，画面レイアウトをあらかじめhtmlで作っておく。
- `<body>`要素の後半では，なでしこ3のエンジンを読み込む処理と，今回作ったプラグイン`plugin_media`を読み込む処理がある。
- `//初期設定`にある2行は，必ず実行する。その続きから，なでしこのプログラムを書いていく。

###（３）プログラムを実行したいときは，作成したHTMLファイルをブラウザで開く。

- プログラムに誤りがあると，画面上に何も表示されない（consoleにエラーが出ている）ので，HTML内の「なでしこ」プログラムを見直す。
- よくある間違いとして，「、（読点）」を「，（カンマ）」で入力しているとエラーになる。

###（４）学校の授業などでプログラムを提出するときは

- 上記（１）で作成したフォルダ内のファイルを全て提出する。作成したHTMLファイルだけを提出しても，音や画像等が表示されない。

## plugin_mediaを使ったサンプル

- [音関連のテスト](https://oza-jv.github.io/sample-audio.html)
- [画像関連のテスト](https://oza-jv.github.io/sample-pic.html)
- [動画関連のテスト](https://oza-jv.github.io/sample-movie.html)
- [数当てゲーム拡張版](https://oza-jv.github.io/kazuate-v3.html)

## 参考

- なでしこv3 [ユーザープラグイン](https://nadesi.com/v3/doc/index.php?%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3&show)
- [プラグインAPIの仕様](https://github.com/kujirahand/nadesiko3/blob/master/doc/plugins.md)
Laravelの初期画面を表示させるまでのエラー解決

# はじめに
Homestead環境でLaravelのプロジェクトを作成しました。

# 手順

コマンドで以下を実行すると

```
composer create-project 〜
```

下記のエラー

```
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - phpunit/phpunit[6.0.0, ..., 6.5.14] require php ^7.0 -> your php version (8.0.0) does not satisfy that requirement.
    - Root composer.json requires phpunit/phpunit ~6.0 -> satisfiable by phpunit/phpunit[6.0.0, ..., 6.5.14].
```

phpのバージョンを7.0に変更

##### コマンド

```
php70
```

ブラウザで

```
failed to open stream: No such file or directory
Failed opening required '/project/vendor/autoload.php' 
```
のようなエラーが表示

##### コマンド
```
composer install
```

下記のエラー

```
No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
```

##### コマンド
```
composer update
```

下記のエラー

```
Install of kylekatarnls/update-helper failed
                                                                               
  [RuntimeException]                                                                  
  Could not delete /home/vagrant/code/Laravel/vendor/kylekatarnls/update-helper/src:  
```

##### コマンド
```
composer install --no-plugins
```

ブラウザを確認すると下記のエラー

```
No application encryption key has been specified.
```

##### コマンド
```
php artisan key:generate
```

もう一度ブラウザにアクセスするとLaravelの画面が表示！

# 参考リンク
https://www.84kure.com/blog/2019/09/03/laravel-homestead%E3%81%A7php%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%92%E5%88%87%E3%82%8A%E6%9B%BF%E3%81%88%E3%82%8B/
https://qiita.com/chimayu/items/82d9d457a056829b3b2e
https://qiita.com/hppRC/items/9f09d123f08dc87992f8
https://qiita.com/ponsuke0531/items/197c76fcb9300d7c5f36
Chrome devtoolでロケールを変更する

多言語対応のサービスでは開発中にテストのためにブラウザのロケールの切り替えを行うことがありますが、ブラウザの言語設定からではなくchrome devtoolで変更することもできます。

**1. 開発者ツールを開き、メニュー→More tooles→Sensorsを選択**
![スクリーンショット_2020-12-26_12_35_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260824/4305a6e3-022c-318c-b69f-21a64ba6a577.png)


**2. 変更したいエリアを選び、画面をリロードする**
![スクリーンショット_2020-12-26_12_38_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260824/e2f2c366-5bd8-3fd7-4442-14819be3ffb8.png)
MATLABの図を論文に載せるに耐えうる外見にする

#はじめに
一般にプログラムを回した結果出力されるグラフはあくまで結果確認用でありそのままどこかに出せるものではないので外見の調整が必要になります。MATLABで出力される図も例にもれずお世辞にも綺麗とは言えません（超個人的感想）（異論は認める）。かといって図を取得するたびに外見を整えるのは非常に面倒なので楽にしたいと思いました。そんなすごいことはしてませんが数値計算屋さんの自分の備忘録兼卒論修論に臨むMATLABerさんたちに役立てばと思い書きました。

#外見を整える関数
以下のような関数を作成しました。x軸(x)とプロットしたいデータ(data)を引数として与えます。

```matlab:plotforpaper.m
function [] = plotforpaper(x, data)

p = plot(x,data,'LineWidth', 2);            %2ptでプロット

p(1).Color='r';
p(1).MarkerIndices = 1:100:length(data);    %100点ごとにマーカーを打つ
p(1).Marker='o';                            %マーカー記号
p(1).MarkerSize=7;                          %マーカーサイズ
p(1).MarkerFaceColor='w';                   %マーカー塗りつぶし
p(2).Color='b';
p(2).MarkerIndices = 1:100:length(data);
p(2).Marker='^';
p(2).MarkerSize=7;
p(2).MarkerFaceColor='w';

grid on;
axis([0, 10, -1.2, 1.2]);
xlabel('$x$', 'Interpreter', 'latex','FontSize',14);    %論文に合わせてTimes New Romanに&文字大きめ
ylabel('$y$', 'Interpreter', 'latex','FontSize',14);
legend('$\sin(x)$','$\cos(x)$', 'Interpreter', 'latex','FontSize',14);
ax = gca;
ax.XTick = 0:2:10;                      %x軸の目盛りは2ごと
ax.TickLabelInterpreter = 'latex';      %軸の書式
ax.FontSize = 14;                       %軸のフォントサイズ
ax.GridAlpha = 0.5;                     %グリッドの濃さ(0~1)
ax.MinorGridAlpha = 0;                  %細かいグリッドの濃さ(0~1)
ax.LineWidth = 0.7;                     %グラフ枠のライン幅

end
```
上の関数を通したもの↓
![fig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802861/154bbd45-f384-9c25-5116-de87eb830f86.png)

初期の`plot(x, data)`に最低限のグリッド、凡例、軸の名前を追加したもの↓
![initial.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/802861/0c5a489c-a2de-9a49-b284-35426a364d80.png)
初期のままだと結果の確認には十分ですが対外的に出すには明らかにちゃっちいですね。

#参考文献
https://jp.mathworks.com/help/matlab/ref/matlab.graphics.chart.primitive.line-properties.html
https://jp.mathworks.com/help/matlab/ref/matlab.graphics.axis.axes-properties.html
ユーザー定義

![EpqQhllUYAArbtx.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/b9c21d79-a85f-ac5e-dd45-22dc7f21a475.jpeg)
![EpqQh8zVEAIph5Z.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/54027116-8a61-7ce1-5f09-2ac53d0fd50e.jpeg)
![EpqQiTlUcAAC_mD.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/daa07b74-b0f0-792b-b472-72bb94f36e7e.jpeg)
![EpvtiJyVgAEqJ7r.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/2eebe729-8c9c-3478-dd34-ff6ca9ffc224.jpeg)
![EpvtimwUcAA4nWw.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/8ba2e8b7-1a8f-3664-cc6b-1a8ba23f96b3.jpeg)
![EpvtjEEVEAEty9V.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/6cafa59f-3b68-2733-ab8e-6227e3f63ad0.jpeg)
![EpzPK3UVgAEkwiZ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/3df2815b-8585-616b-186f-79618e01e6db.jpeg)
![EpzPLLsVgAIMnih.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/b8526672-fac6-119c-7311-ea4bcd756a7f.jpeg)
![EpzPLmhUwAAKkO7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565276/e02c0a5e-cf57-232f-fe22-a3d310056698.jpeg)
データサイエンティストのための最強のフロントエンド Streamlit を Azure にデプロイ！

この記事は、[求ム！Pythonを使ってAzureで開発する時のTips！【PR】日本マイクロソフト Advent Calendar 2020](https://qiita.com/advent-calendar/2020/azure-python) の 5 日目です（後から追っかけで書いています）。

----

データサイエンティストのための最強のフロントエンド Streamlit を Azure にデプロイ！って話です。

## What is Streamlit?

[Streamlit](https://github.com/streamlit/streamlit) は Python でサイトを構築できる OSS のフレームワークです。

## Why Streamlit?

**Streamlit にする理由は何があるんでしょうか？　最近流行りの React.js や Vue.js じゃダメなんでしょうか？**

**まったく、そんなことはありません！**

むしろ、表現力や柔軟性などの要素を比べると React.js や Vue.js の方がずっと優れています。

じゃぁ、なぜ表現力や柔軟性などの要素で劣っている Streamlit を使うのか？というと、構築する人の適性からです。

React.js や Vue.js の場合、データサイエンティストが分析データをプロダクトに組み込むために必要となる以下のように作業コストがかなり高くなります。

* Python: データ分析にほぼ必須
* JavaScript(React.js や Vue.js): フロントエンドに分析データを組み込むのに必要

かといって、「分析データをプロダクトに組み込む」作業をエンジニアが代行するのは生産性の観点、コストパフォーマンスの観点、責任の観点からもいびつになってしまいます(*1)。

*1:とはいえ、「分析データをプロダクトに組み込む」に限らず、デザイナーの作った「デザイン（PDF、HTML）をプロダクトに組み込む」作業をエンジニアにやらせている東証一部上場企業はいくつもありますが…。

このデータサイエンティストにとっては高い作業コストを軽減させるために Streamlit は開発されました。

データ分析には Python を使うことが多いため、 Streamlit は Python さえあればフロントエンドを構築できるようになっています。

つまり、React.js や Vue.js を使った時に得られる表現力や柔軟性などをある程度妥協し、Python さえ分かっていれば構築できる作りになっているのです。

## How to use Streamlit

インストールは以下で OK です。

```shell
$ pip install streamlit
```

詳しい使い方は[公式のチュートリアルページ](https://docs.streamlit.io/en/stable/index.html)がわかりやすいです。

以下のコマンドを実行すると、ローカルでデモアプリを立ち上げて、ブラウザで確認することができます。

```shell
$ streamlit hello
```

Streamlit はリモートリソースの実行もでき、以下のコマンドを実行すると、リモートリソースの実行ができます。

```shell
$ streamlit run https://raw.githubusercontent.com/streamlit/demo-self-driving/master/streamlit_app.py
```

## Deploy to Azure VM

ここまでは手元のデータをローカルでさくっと確認するやり方でした。以下の手順で Azure VM にデプロイすることも可能です。

1. Azure VM を立てる
2. インスタンス内で、ローカルと同様に、streamlit をインストールする
3. streamlit run [your_streamlit_app.py]

上記で、Azure VM にデプロイし、公開自体は可能となります。
よく使うショートカット一覧

## vi/vim

__dd__ - 1行消去
__dG__ - カーソルのある行以下全てを消去（k8sでyamlファイル扱うときによく使う）
__yy__ - 1行コピー
__<n>yy__ - n行コピー(5yyだと5行コピー）
__p__ - (ddで1行消去したものやyyでコピーしたものを）貼り付け（Paste）

__:set nu__ - 行番号表示
__:<行番号>__ - 指定した行番号に移動

__/<文字列>__ - 文字列を上から検索
__?<文字列>__ - 文字列を下から検索
__n__ - （検索後Enterが必要）次の検索結果へ移動
__N__ - （検索後Enterが必要）前の検索結果へ移動

__%__ - 対となる括弧へ移動

## Visual Studio Code

__Ctr+Shift+k__ - 1行消去
__Ctr+]__ - 複数行のインテンド
__Alt+マウスで複数個所をクリック__ - キャロット増殖（同時に複数個所に同じ文字を入力）
__Ctrl + d__ - 同じ文字列の箇所まで一つ一つカーソルを増殖。Ctrl+Shift+右矢印キー でそのカーソルから右にある単語を一気に選択


## Jupyter Notebook

__Esc+l__ - 行番号追加


## Linux

__Ctl+A__ - カーソルをプロンプトの先頭に持っていく
__Ctl+E__ - カーソルをプロンプトの最後に持っていく
【初心者】PHPで文字列の文字の入れ替えについて

## str_replaceはどうだろう？
以前、「文字列の中の特定の文字を削除したい」という記事を書いた。
https://qiita.com/wakiyo1017/items/ace7ddc7b364edc7ccff
その時知ったのは、特定の文字を特定の文字に入れ替えるということ。
つまり、本当は1文字だけ入れ替えたくても、他の同じ文字まで入れ替わってしまった。
この関数が使えないという訳ではないし、この関数を使用してできることの幅が広がった。
でも、今回は指定した場所の文字だけを変えたい。

## substr_replace関数を使う。
```php
substr_replace("文字列", "入れ替え後の文字", "入れ替え前の文字が何文字目か", "何文字目まで入れ替えたいか")
```
ううむ。日本語で書くとわかるようなわからないような、不思議な感じになってしまった。

```php
$str = "abcdefg";
$replace = substr_replace($str, "&", 3, 2);
echo($replace);
// 以下出力結果です。
abc&fg
```
実際に使用してみるとこのような感じ。
3文字目（スタートが0文字目なので、見た目は4文字目）から2文字が、入れ替えたい文字"&"に入れ替わっていることがわかる。
1文字ずつ入れ替わるのではなくて、"de"が"&"に入れ替わっていることに注意して、この関数を覚えておきたい。
IDトークン 検証方法 メモ

* Open ID Connect IDトークン検証方法についてメモする。

## IDトークンとは

* ユーザー認証情報を含んだ改竄検知用の署名付きトークンを指す。
  * JWT（JSON Web Token）形式でエンコードして表現される。
     * ピリオド（"."）区切りのHeader部、Payload部、Signature部から構成される。
     * 各部位はBase64URLエンコードされる。
     * 署名生成には、RSA-SHA256などが用いられる。
  
    * ユーザー認証の結果、発行される。


## IDトークン検証方法

* クライアントは、以下の処理を行うことで、IDトークンの正当性を検証する必要がある。
  * 検証に失敗した場合、認証処理を中断し、エラー処理などを行う。

### 1. Base64デコード

* 取得したIDトークンをBase64デコードする。

### 2. IDトークンをヘッダー部、ペイロード部、署名部へ"."で分割

### 3. トークンの復号

*  (暗号化されている場合、)OpenID Providerにクライアント登録した際に、利用した鍵とアルゴリズム情報を使用して、復号する。

### 4. JWT 署名検証

* `alg`(署名アルゴリズム)ヘッダーパラメータを使用して、署名を検証する。
  * 検証には、issuerが発行した公開鍵を利用する。
    * 公開鍵の取得方法は、Header部に含まれる`kid`値を用いて、issuerが提供するエンドポイントからから対になるものを取得するパターンがある。
  * `HS256`、 `HS512` のような MAC ベースのアルゴリズムを利用する場合、 `aud` (audience) Claim に含まれる `client_id` に対応する `client_secret` を署名の検証に利用する。

### 5.Issuer Identifier(ISS)クレーム検証

* `iss`がOpenID Provider(issuer)と一致するかを確認する。

  * `iss`
     * IDトークン発行者 (issuer) を識別するための識別子。
     * `https://` で始まる URL (クエリー部とフラグメント部は含まない)。

      ```http
      https://example.com
      ```

### 6.audience(aud)クレーム 検証

* `aud`と自身に発行されたクライアントID(`client_id`)が一致するかを確認する。
* `aud`
     * 誰を対象として発行されたのかを示す識別子。
     * ID トークン発行をリクエストしたアプリケーションのクライアント ID 。

### 7. expiration time(exp)クレーム 検証

* `exp`クレームが現在時刻より未来であることを確認する。
* `exp`
    * IDトークン の有効期限。
    * Unix エポック からの経過秒数で表現する。

### 8. nonceクレーム 検証

* (認証リクエストに`nonce`が付与されている場合、)`nonce`クレームの値と認証リクエスト時に指定した値が一致することを確認する。
* `nonce`
  * 主にリプレイアタック防止目的の文字列。
  * issuerは認証リクエストに付与された値をそのままクレームに埋め込む。

###  9. Authentication Context Class Reference(acr) クレーム 検証

* `acr`クレームの値とクライアント側で定義された認証コンテキストの値が一致することを確認する。
* `acr`
  * ユーザー認証が満たした認証コンテキスト(パスワード認証した、などどのように認証したかを示す情報)。

### 10. auth_timeクレーム 検証

* `auth_time`クレームが最新のユーザー認証時刻から指定時間以上経過していないことを確認する。
* `auth_time` 
  * ユーザーが認証された時刻。

## 参考情報

* [Yahoo! ID連携 ID Token](https://developer.yahoo.co.jp/yconnect/v2/id_token.html)
* [IDトークンが分かれば OpenID Connect が分かる](https://qiita.com/TakahikoKawasaki/items/8f0e422c7edd2d220e06)
* [OpenID Connect Core 1.0 incorporating errata set 1 3.1.3.7. ID Token Validation](http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#IDTokenValidation)
アブソリュートエンコーダIC datasheetからic specについて調べてみた

 #概要
モータのシャフト角度検出に用いられるアブソリュートエンコーダICについて調査した。
今回参考にしたのはMPS社製のma730と旭化成のak7452。
データシートを参考に以下項目について調べた

・reflesh rate 下画像の赤線部
・latency      下画像の赤線部

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700308/837582be-c5fe-9d5c-e0b6-ffaf53a50f38.png)

#アブソリュートエンコーダとは
マグネットの磁束から角度を算出するエンコーダである。
サーボモータに取り付けられ、シャフト角度検出によく用いられている。

#1：reflesh rate, latency lagの意味

refresh rate：	
1usec毎に最新角度情報がバッファに入っている。バッファに入った角度情報をマイコンからの通信コマンドによって出力する

latency	:
磁束から角度計算までの時間。ICが計算した角度は実際のシャフト角度に対してlatency前の角度になる。ma730ではlatencyが10usecのためICが計算した角度は実際のシャフト角度に対して10sec前の角度になる。この遅れ角度はlagとして説明していた
下画像参照

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700308/d85643f6-80c7-92a6-7428-addc4894c269.png)


実際にma730とak7452のlatencyからLagを計算してみた。※モータ速度を3000rpm(18000deg/sec)とした場合
※lag[deg] = latency[sec] × モータ速度[deg/sec]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700308/93690af7-f069-c2d8-8b92-00d1dec36ba3.png)

latencyの小さいak7452の方がlagが小さいことがわかる。つまり角度追従性能はma730よりもak7452の方が高い。

#参考
https://www.monolithicpower.com/en/documentview/productdocument/index/version/2/document_type/Datasheet/lang/en/sku/MA730/document_id/3563
今日から技術書典10が始まるよーって話


![bandicam 2020-12-26 10-41-09-423.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295760/46fd08ff-d146-9972-ccec-a356915727f0.png)



いよいよ今日から

技術書典10がスタートします。　

今日から、オンラインストアで　

本が買えるようになっています。

私も先ほど、ストアを見てましたけど　

前回の技術書典９と比べると、本の数はかなり減ってますよね。

前回は結構Unity本もあったと思うけど　
今回はあんまりないですね。
　

まぁ時期が時期ですから、これはしょうがないと思う　

１２月はみんな忙しいですもんね。　

私も今回は、参加しようかどうかぎりぎりまで悩みましたが　

なんとか、期限内に執筆を終えました。　

私の出している本はこちら　


[UnityアニメーションTIPSです。](https://techbookfest.org/product/5149751706124288?productVariantID=5723292780462080)


　

前回は、入門用の内容でしたから　

今回は、技術書典らしく特化型の本にしてみました。　

タイトルの通り、アニメーションしか解説してません。　

Unityやるんだったら、これぐらいの　

アニメーションを覚えておいて損はないと思いますよ。　

アニメーションはどんなゲームでも使いますからね。










ＰＳ　

本当は２００ページぐらい出したかったけど　

まぁー時間的に厳しかったね。　Unityとかプログラミング関係の本を　

書いている人ならわかると思うけど　

文章よりも、スクショ用意するのがめんどうなのですよ。
GitHubDesktopにて誤った情報をpushしてしまった

#### GitHubデスクトップにて誤ったcommitをpush

revertコミットという方法を使用すれば、指定したコミットを再度コミットすることで取り消しことができる
revertしたい項目で右クリックをすると、Revert Changes Commitという項目が出てくるので、クリック！


#### pushする前に気がついたら

コミットしてすぐに間違えに気がついた時は、commit toボタンの下にundoというボタンが出てくるので直近のコミットを取り消せる！
【AWS】ログの確認方法について

#はじめに

ポートフォリオ作成中で、自動デプロイ時にUnicornに関するエラーが発生しました。
エラーの内容についてはファイルの場所が悪かったという簡単な内容だったのですが、ログの確認を怠り
（というよりログの確認方法を理解していなかった）、解決までに時間を要したので、コマンドを含め記述していきたいと思います。

#環境

Ruby on Rails '6.0.0'
Ruby '2.6.5'

#エラー内容

これまでCapistranoによる自動デプロイはできていました。ローカルでアプリケーションを編集後、いつもの
ようにデプロイのコマンドを実行すると、下記のエラーが出現しました。

```
01:12 unicorn:start
（省略）
SSHKit::Command::Failed: bundle exit status: 1
bundle stdout: Nothing written
bundle stderr: master failed to start, check stderr log for details
（省略）
```

#解決に向けて

エラー文を参照すると、**check stderr log for details**との記述があるため、ログの確認を
しようと試みましたが、どうやって確認するのだろうというところから始まったので、記述します。

```
#アプリケーションのディレクトリへ移動
[ec2-user@ip-172-31-23-189~]$ cd /var/www/アプリケーション名

#currentディレクトリへ移動
[ec2-user@ip-172-31-23-189 <リポジトリ名>]$ cd current

#logディレクトリへ移動
[ec2-user@ip-172-31-23-189 current]$ cd log

#logの中に入っているファイルの情報を表示
[ec2-user@ip-172-31-23-189 log]$ ls
production.log  unicorn.stderr.log  unicorn.stdout.log
```

```
tail -f unicorn.stderr.log
          or
cat unicorn.stderr.log
```

tail -f : 最新のログを10行表示する
cat : 全てのログを表示する

今回はまず「tail -f」を用いて見てみましたが、原因は分からず。
そのため「cat」コマンドを使用しログを全部表示させました。すると

```
（省略）
bundler: failed to load command: unicorn (/var/www/myapp/shared/bundle/ruby/2.6.0/bin/unicorn)
Zeitwerk::NameError: expected file /var/www/myapp/releases/20201226010858/app/controllers/users/users_controller.rb to define constant Users::UsersController, but didn't
```

上記のエラーログを発見！！とりあえず、uses_controller.rbに問題があるんだなということは把握。そこで解決に至りました。

#おわりに

ログを確認する大切さについて、改めて気付きました。エラー文をとりあえずコピペして、検索するということばかりやっていても
解決はできないことを今回のエラーを通して痛感しました。。。。引き続き学習に励みます！！！！
VSCode Remote Containerでコンテナからgitにssh接続する

VScode Remote Containerを使って、dockerコンテナをローカルで編集できるようにしたが、gitへの接続で少しつまづいたしまったのでメモ。
## ローカルのssh keyを利用して、git接続する方法
.devcontainer/docker-compose.ymlに下記を追記する

```yml:.devcontainer/docker-compose.yml
 volumes:
      - .:/workspace:cached
      - ~/.ssh:/root/.ssh #sshキー
      - ~/.gitconfig:/root/.gitconfig #gitの設定
```

これで、ローカルのssh keyをコンテナ内でも利用することができる。
Apple Silicon (ARM, M1) のmacOSにaarch64とx86_64のhomebrewを共存させる

ARM版Macで開発を始めている方も多いかと思います。
Rosetta2やDocker Desktop on macのおかげでウェブ開発において全く開発ができないということはないんじゃないかと思います。

一方で、Dockerを使わずにネイティブで開発するにはいくつかの問題もまだ残っています。
例えば、homebrewでpythonのいくつかのバージョンがインストールできないとか、Rustがインストールできないとか、そういう問題にぶつかることがあるでしょう。

そこで、本記事ではhomebrewのx86_64 (intel) 版と、aarch64 (ARM) 版を共存させ、どちらを使うか選べるようにします。

# Homebrewのインストール

## ARM版homebrewのインストール

こちらは普通にインストールします。ターミナルを開き、aarch64であることを確認してインストールしましょう。

```shell
uname -m # "arm64" が表示されることを確認する
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```

## Intel版homebrewのインストール

こちらは手動でインストールします。ARM版は `/opt/homebrew/` にインストールされると思うので、Intel版は `/opt/homebrew-x86_64/` にインストールすることにしました。

ターミナルをそのままで起動すると、aarch64アーキテクチャで動いてしまうため、x86_64で動くターミナルを作ります。

「ユーティリティ」フォルダにある「ターミナル」を「ターミナル(intel)」とでも名前をつけてコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73788/1fa2cdb0-ac64-5971-ce90-2005fec44bb2.png)

新しくコピーした方のターミナルの情報を開き、「Rosettaを利用して開く」にチェックをつけます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73788/2294a22c-3b44-b744-64f7-1cc901ff0645.png)

こちらのターミナルを起動すると、x86_64アーキテクチャで動作するターミナルを開くことができます。

ではこちらでもhomebrewをインストールしましょう。

```shell
uname -m # "x86_64" が表示されることを確認する
sudo mkdir /opt/homebrew-x86_64
sudo chown `whoami`:staff /opt/homebrew-x86_64
curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C /opt/homebrew-x86_64
```

## 起動スクリプトをいい感じにする

`~/.zprofile` で、稼働中のアーキテクチャによって使うhomebrewが変わるように次のように記述しました。

nodenvなどの初期化は、片方のアーキテクチャでインストールしている場合には片方のアーキテクチャでのみ実行されるようにしましょう。

```shell
arch=`uname -m`
if [ "$arch" = "x86_64" ]; then
    export PATH=$PATH:/opt/homebrew-x86_64/bin
else
    export PATH=$PATH:/opt/homebrew/bin
    # eval "$(nodenv init -)" などはどちらかインストールした方に書く
fi
```


# それでも完全ではない...

bisonはaarch64ならインストールできるのに、x86_64ではインストールできないとか...
x86_64でしかインストールできないものがaarch64でしかインストールできないものに依存してたりすると詰む
ファイル改竄チェックと操作履歴取得

外部公開しているので、セキュリティ対策をする。

- centos7

# 導入候補
## ファイル改竄チェック
- [aide](https://qiita.com/kooohei/items/bcf34fd82c2f98b0b559)
- [tripwire](https://github.com/Tripwire/tripwire-open-source)

があるが、trpiwireの方が小回りが効きそうなのでこれを利用する。ほとんどが[ここ](https://qiita.com/progrhyme/items/f65c3a56cf6e5e954f8d)の通りです。

## 操作履歴取得
### 操作ログとして取得すべきログファイル
- /USER/.bash/history
- /var/log/cron
- /var/log/secure
- /var/log/messages

### 操作履歴
[psacct](https://dev.classmethod.jp/articles/logging_operation_using_script_and_psacct/)
ログの保存先 : /var/account/pacct
このファイルの所有者はrootであり、バイナリ形式で保存されるため、簡単に改竄することはできないようになっています。実行されたコマンドを確認するには、次のようにlastcommコマンドを利用します（コマンドやユーザで絞り込むことも可能）。


# tripwire
## install&初期設定
この [tripwire](https://github.com/Tripwire/tripwire-open-source) のreadmeの通り。

### キーの作成
site-keyfileとlocal-keyfileを作成する。xxxx,yyyyはパスフレーズ。

```bash:
# tripwiresetup-keyfiles 
Enter the site keyfile passphrase: xxxx
Enter the local keyfile passphrase: yyyyy
```

### 設定ファイルの作成
tripwire自体の設定ファイルを作成する。

```bash:
# twadmin --create-cfgfile -S /path/to/site.key /path/to/twcfg.txt
```

### ポリシーファイルの作成（初回）
どこを監視して、どこを監視しないかなどの設定、ポリシーを作成する。

```bash:
# twadmin --create-polfile -S /path/to/site.key /etc/tripwire/twpol.txt
```

デフォルトで、テキスト版「/etc/tripwire/twpol.txt」とバイナリ版「/etc/tripwire/tw.pol」が作成された

### ポリシーファイルのカスタマイズ（随時）
ポリシーファイルには、監視対象のディレクトリ（ファイル）、監視対象外のディレクトリ（ファイル）が記載されているが、実環境とあっていないとエラーになるので、（テキスト版の）ポリシーファイルをカスタマイズする。

```bash:初期化
データベースの初期化
# tripwire --init 2> tripwire.txt 
tripwire.txtを確認しながら、ポリシーファイルの修正
# vi /etc/tripwire/twpol.txt 
テキスト版からバイナリ版の作成
# twadmin --create-polfile --cfgfile /etc/tripwire/tw.cfg /etc/tripwire/twpol.txt
再度初期化してエラー確認
# tripwire --init 2> tripwire.txt 
```

## Daily-Use
### 改竄チェック
 改竄チェック ```tripwire --chesk```をすると、```/var/lib/tripwire/report/***.twr```に結果格納されるので、レポート表示する。

```bash:
改竄チェック
# tripwire --check
レポート表示
# twprint --print-report --twrfile /var/lib/tripwire/report/***.twr
```

### ベースライン（データベース）の更新 = ファイル変更の受容。
ポリシーを変更せず、現在のファイルシステムの状態を正として、ベースラインを更新。

```bash:
# tripwire --update --accept-all --twrfile /var/lib/tripwire/report/***.twr
```

### ポリシー設定ファイルの再生成と、ベースラインの更新を行う
ポリシー変更の場合は、ベースライン（データベース）も更新する。

```bash:
1. ポリシー変更
# vi /etc/tripwire/twpol.txt 
2. ベースラインを再度初期化
○ # twadmin --create-polfile --cfgfile /etc/tripwire/tw.cfg /etc/tripwire/twpol.txt
  # tripwire --init
3. この手順はNG
✖︎ # tripwire --update-policy /etc/tripwire/twpol.txt 
```

### ベースラインの確認
ベースライン（データベース）の表示。

```bash:
# twprint --print-dbfile | grep xxxxx
```

### Daily-check, CRON
 cron.dailyで、毎日チェックが起動される。```/etc/cron.daily/tripwire-check```

# pacct
とてもシンプル

## install psacct

```bash:
yum install psacct
systemctl enable psacct
systemctl start psacct
```

## Daily-Use

```bash:
lastcomm --user=xxxx
lastcomm --command=yyyy
```
PHP 現在時刻を取得する

# 目的

- PHPで現在時刻を「YYYY/MM/DD HH:MM:SS」の形で取得する方法を簡単にメモする

# 前提情報

- 下記サービスを使用して確認した。
    -  [https://paiza.io/ja](https://paiza.io/ja)

# 詳細

- 下記を実行する。

    ```php
    echo date("Y/m/d H:i:s");
    ```
ローカル環境のJupitorNoteBookに別ホストからアクセスできるようにする設定

Windows環境にインストールされたAnnacondaのJupitorNoteBookを、別コンピュータから利用するために必要な手順です。

### Annacondaホスト側


* 以下の2点
  * jupyter notebookの設定
  * Windowsファイアウォールの設定

#### Anaconda Promptからjupyter notebookのコンフィグファイルを作成します。

<pre>
[Anaconda Prompt]
(test1) C:\Users\user01>jupyter notebook
</pre>

* Windowsユーザフォルダに[.jupyter\jupyter_notebook_config.py]が作成されます。VSCode等のテキストエディタから、以下を編集します。

<pre>
 [jupyter_notebook_config.py]

## The IP address the notebook server will listen on.
#c.NotebookApp.ip = 'localhost' --> 以下のように書き換えてください。
c.NotebookApp.ip = '0.0.0.0'

</pre>

* Anaconda Promptから、[jupyter notebook]を再起動します。  
  アクセスに必要なtokenを含んだjupitornotebookへのURLが表示されます。このURLにリモートＰＣのブラウザからアクセスすることができます。

<pre>
[Anaconda Prompt]

(test1) C:\Users\user01>jupyter notebook
[I 10:06:26.772 NotebookApp] Serving notebooks from local directory: C:\Users\user01
[I 10:06:26.772 NotebookApp] The Jupyter Notebook is running at:
[I 10:06:26.774 NotebookApp] http://[Host名]:8888/?token=c7bf022cfb45012eNo368b5d87b5366f57f01lq5a95f27b6
[I 10:06:26.774 NotebookApp]  or http://127.0.0.1:8888/?token=c7bf022cfb45012eNo368b5d87b5366f57f01lq5a95f27b6
[I 10:06:26.775 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 10:06:26.842 NotebookApp]

    To access the notebook, open this file in a browser:
        file:///C:/Users/user01/AppData/Roaming/jupyter/runtime/nbserver-5564-open.html
    Or copy and paste one of these URLs:
        http://[Host名]:8888/?token=c7bf022cfb45012eNo368b5d87b5366f57f01lq5a95f27b6
     or http://127.0.0.1:8888/?token=c7bf022cfb45012eNo368b5d87b5366f57f01lq5a95f27b6

302 GET /?token=c7bf022cfb45012eNo368b5d87b5366f57f01lq5a95f27b6 (192.168.xx.xx) 0.00ms

</pre>

### リモートＰＣ側

* リモートＰＣのブラウザから[http://[Host名]:8888]にアクセスし、tokenを入力します。

![Jupitor_remotepc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276932/6e1a763e-32f1-a181-301d-c6e7093309d8.jpeg)

* ログイン成功

![Jupitor_remotepc_loggedin.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276932/806160b8-3ab3-50e7-1e23-3b2d794ee0b4.jpeg)
(作成中)【RaspberryPi】WebIOPiでブラウザからモータ制御

#概要
自走式カメラを作成したく、関連する情報を覚え書き。
本記事ではモータードライバ用のGPIO、PWMのWebIOPiによる制御について記載。

#実行環境
MCU：Raspberry Pi Zero W
OS：Raspberry Pi OS(buster)
モータドライバ：TA7291P
駆動用モータ：FA-130(楽しい工作シリーズ（ユニット） No.168　ダブルギヤボックス・左右独立４速タイプ)

#WebIOPiでGPIO制御
##WebIOPiの導入
WebIOPiをダウンロード、解凍してフォルダに入る

```terminal
$ wget https://sourceforge.net/projects/webiopi/files/WebIOPi-0.7.1.tar.gz
$ tar xvzf WebIOPi-0.7.1.tar.gz
$ cd WebIOPi-0.7.1
```

ブラウザで表示するGPIOの表示を修正するパッチを適用
WebIIOPiは26ピンのころのRasberryPi用に作られているため、40ピンのもので使うときは必要

```terminal
$ wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch
$ patch -p1 -i webiopi-pi2bplus.patch
```

セットアップ用スクリプトを起動

```terminal
$ sudo ./setup.sh
```

WebIOPiサービスを ```systemctl``` で操作できるようにするため設定ファイルをダウンロード
下記のフォルダに保存して再起動

```terminal
$ cd /etc/systemd/system/
$ sudo wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi.service
$ sudo reboot
```

RasberryPi起動時にWebIOPiが自動起動するよう設定、WebIOPiのスタート、ステータス確認

```terminal
$ sudo systemctl start webiopi
$ sudo systemctl enable webiopi
$ sudo systemctl status webiopi
● webiopi.service - LSB: WebIOPi initscript
   Loaded: loaded (/etc/init.d/webiopi; generated)
   Active: active (running) since Sat 2020-12-26 08:07:38 JST; 2h 2min ago
     Docs: man:systemd-sysv-generator(8)
    Tasks: 2 (limit: 729)
   CGroup: /system.slice/webiopi.service
           └─791 /usr/bin/python -m webiopi -l /var/log/webiopi -c /etc/webiopi/config
```

ブラウザからhttp://<ラズパイのIP>:8000にアクセス
認証ダイアログが表示されるので、
ユーザー名：webiopi
パスワード：raspberry
でログイン
サンプルページが表示されれば導入完了


##GPIO制御に必要なライブラリのインストール

```terminal
sudo apt install pigpio
```

##作業フォルダの作成

```terminal
mkdir ~/raspi-tank/python/
mkdir ~/raspi-tank/html/
```

##pythonのコーディング
【3D OD LiDAR編】PV-RCNN : 1st stageはVoxel-base、2nd stageはPointNet-base

<h1>PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection</h1>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/e8466528-d790-3041-3d8e-8d7c7f4ef33a.png)


sparse convolutionを用いた3D voxel CNNは効率的で良い3D ObjectのProposalが出来る
PointNet-basedの方法はより詳細なcontext情報を柔軟な受容範囲で学習できる。

<h1>アルゴリズム</h1>
<h2>1st stageの3DBounding Boxの提案をしよう</h2>
anchor-baseの3D voxel CNN backboneはrecall率がpointnet-baseの手法より高いとです。
SECONDで提案されている3DのVoxelを畳み込んで2Dに落とし込んでから、RPNを行う方法です。



<h2>keyPointが物体のPointかどうか重みをつけよう</h2>

<h3>Keypoints Sampling</h3>
FurthestPoint-Sampling (FPS) algorithmを使って任意のn個の点を抽出する。

<h3>Voxel Set Abstraction Module</h3>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/c1bbd90b-dcf0-88bd-fb43-14ac4ec842ce.png)
各Voxelの領域内(ROI)で、複数の解像度から得られたFeature Vector結合し、PointNet++でVoxelの特徴を学習する。
*上図を見ると分かりやすい。

<h3>Extended VSA(Voxel Set Abstraction) Module</h3>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/375a08b8-32af-fe9a-9800-f09316fd37f9.png)
ちょっとまって〜。もっと入力の特徴量を増やしたいよということで
PointVoxel featureにraw point cloud feature と birdeye view featureを追加したら精度が上がったらしい。


<h3>Predicted Keypoint Weighting</h3>
従来と同じで、前景のpointをsemantic segmentationで見つけましょ〜


<h2>Grid Pointと重み付けされたKeyPointから3D Bouding Boxをrefinementしよう</h2>
<h3>RoI-grid Pooling via Set Abstraction</h3>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/7d3a3c75-50e8-ca4e-0eb7-cd37f09993e4.png)
従来手法では3D Bounding BoxのProposalの中のPointだけを取り出して、特徴量としていた。
この論文では、3D Bounding Boxを均等に分割して、6x6x6個のGrid Pointを定義する。そのGrid Pointから半径r以内のkeyPointの中から任意の数randomに選び、MLPで特徴を学習する。さらにGrid Pointに複数の半径rを定義する事で複数のreceptice fieldの情報を得られる。

<h3>3D Proposal Refinement and Confidence Prediction</h3>
従来研究と同じく得られた特徴量からBouding Boxを修正する

<h1>結果</h1>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/4d363c8e-0b81-66ac-64d1-3cd278437077.png)
全体的に精度が結構上がっとる。

<h1>結論</h1>
・1st stageにVoxel-baseメソッド、2nd stageにPointNet-baseメソッドを用いると精度が良くなる事がわかった。
・Voxel set Abstraction Moduleでマルチ解像度でVoxelの特徴を取るのはSemantic Segmentation(Keypoint weighting)に効果を発揮してる感じがする。

<h1>参考文献</h1>
PV-RCNN: Point-Voxel Feature Set Abstraction for 3D Object Detection
https://arxiv.org/pdf/1912.13192.pdf
年末年始にリポジトリの大掃除　DS.storeを削除にしよう

##概要
皆さん、今年１年お疲れまでした！！
1年間様々な、チュートリアルでアプリ作成しGithubにお供えしてるかと思いますが、gitignoreは後回しにしてDS.storeがガンガン上がってるリポジトリも多いのでは無いでしょうか？

そこで、ディレクトリ以下のDS.storeを大掃除しましょう！！



####確認自分のアプリのディレクトリに移動

```
cd <あなたのアプリ名>
```

####DS.store確認
```
find . -name ".DS_Store"
```

たくさんDS.storeがいましたか？（笑


####DS.storeを検索して削除
それでは消して行きましょう！これで綺麗サッパリです！！

```
find . -name ".DS_Store" | xargs rm
```

####一応、確認
```
find . -name ".DS_Store"
```
表示がなけれが、綺麗になってます！
では、origin main（master）にでもpushしましょう！！




##あとがき
GithubでDS.storeがたくさんあるリポジトリみると「あ！初心者の方やな！！」感が否めないので、新規プロジェクトでは最初にgitignoreに書いとくべきですよね。。まめな掃除とは現実含めて大切！！
[Ruby] 配列の中の文字列を数値に一括変換する方法

# はじめに

配列の中の数値を変換しようとしたときにつまずき書き方を調べたので
備忘録として自分用にメモしていきます。

## 問題

配列の中の文字列の数を変換しようとした際に、
エラーが出てしまいました。

```ruby:array.rb
Traceback (most recent call last):
array.rb:3:in `<main>': undefined method `to_i' for ["1", "2", "3", "4", "5"]:Array (NoMethodError)
Did you mean?  to_s
               to_a
               to_h
```

書き方の例についてのまとめです。↓↓↓

## whileメソッド

```ruby:array.rb
num = [ "1", "2", "3", "4", "5" ]

i = 0
while i <= 4
  num[i] = num[i].to_i
  i += 1
end

p num
#=>[1, 2, 3, 4, 5]
```

## for文

```ruby:array.rb
num = [ "1", "2", "3", "4", "5" ]

int = []
for i in num
  int.push(i.to_i)
end

p int
#=>[1, 2, 3, 4, 5]
```

## mapメソッド

1.

```ruby:array.rb
num = [ "1", "2", "3", "4", "5" ]

int = num.map{ |i| i.to_i }

p int
#=>[1, 2, 3, 4, 5]
```

2.

```ruby:array.rb
num = [ "1", "2", "3", "4", "5" ]
int = num.map(&:to_i)

p int
#=>[1, 2, 3, 4, 5]
```

# まとめ

なかなかうまく変換できなかったのでこれを機会に調べてみました。
これでもう少しわかりやすく記述できそうです。
(作成中)【RaspberryPi】WiringPiでPWM

#概要
自走式カメラのための覚え書き
本記事ではカメラのチルト用サーボモータの制御について記載。

#実行環境
MCU：Raspberry Pi Zero W
OS：Raspberry Pi OS(buster)
サーボモータ：SG-90

#SG-90の使い方
データシート
https://akizukidenshi.com/download/ds/towerpro/SG90_a.pdf
※データシート抜粋
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321914/7edbcbd5-a9f1-a7cf-af9c-784d58b12bb7.png)
・PWM周波数は50Hz
・0.5ms　→　Duty 2.5%
・2.4ms　→　Duty 12.5%

```RPi.GPIO``` の ```GPIO.PWM``` で制御したところ、出力されるDutyの精度が低く、Dutyを変えていないのにカメラが上下にピクピク動き続ける現象が発生
セグメンテーション(Deeplab)を使用し撮影写真の背景を変えられるスマホアプリを作ってみた


# 概要
ML-Kit For FirebaseでTensorFlow Liteを使ってAndroidアプリに組み込みこもうと、なにか良いネタないかと考え
コロナで旅行行けないので、家で旅行写真とれたらいいんじゃないかと思い付き、勢いで作ってしまいました
人物の写真を撮ると、背景がどこかの観光スポットの写真にいれかわります
※背景はフリー画像素材を使用しています
※ライブラリから背景画像を指定することも可能です

例のごとく、作成したはいいが、半年くらい放置しており、もったいないので、動作原理、開発手順等、簡単に説明をのこしておきます

下記で公開しています（Android 9以上）
https://play.google.com/store/apps/details?id=com.e.ittatumoricamera

ざっくりのアプリの動作は以下のとおりです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/19ae189b-5d04-98c9-03f2-0d863d47f1d3.png)

# 動作原理・技術情報
予想は付くと思いますが、大まかな動きとししては、以下の手順を踏んでいます

###１．人物写真撮影
モバイルアプリから人物写真を撮影
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/241487ad-5141-00f7-b8eb-fa0e370776de.png)

###２．人物画像範囲特定
撮影画像に対しセグメンテーションを掛け人物の箇所を特定
・セグメンテーションはDeepLabを使用
・モバイルから学習済みモデルの読み込みはML-Kit For Firebaseを使用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/eac75da1-d439-8897-f03d-3d868353797b.png)

###３．人物画像マスク作成
人物が写っている箇所を切り抜くためのマスクを作成
OpenCVを使用しセグメンテーションされた画像からマスク画像を作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/bd187ff5-03c1-f9e9-449e-614b62ddcd26.png)

###４．背景合成写真を作成
背景写真に対し、「３．」で作成した人物のマスク画像を使用し人物画像を合成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/9dd62e52-fea1-66e9-faba-de8c83232f96.png)

###　その他
Android開発について画像系まわりは特に初めて取り組んだため、かなり苦労しました
下記は開発の際に特にハマった箇所なので、後々記事を書ければと思っています
・Android(9以降)の画像ライブラへのリアクセス
・プライバシーポリシーのファイル配置場所

# 参考技術情報

セグメンテーション実施手順
https://www.tensorflow.org/lite/models/segmentation/overview

Android StudioへのOpenCVライブラリインポート手順
https://qiita.com/denjin-m/items/8b2f30b98ef4529b8f1f

ML KitにTensorFlowLiteを組みこむ手順
https://firebase.google.com/docs/ml-kit/android/use-custom-models?hl=ja