OmniAICreator/Qiita-1.07M huggingface dataset:
Mac-セキュリティ&プライバシーの制御-カメラ編

### はじめに...

調査した内容を時系列にまとめた資料なので、その点ご了承ください。

## 目次

1. 実行環境
1. 実現したいこと
1. CLIを使った制御
1. プロファイルを使った制御(ProfileCreator)
1. プロファイル生成
1. プロファイル適用
1. プロファイル動作確認
1. まとめ

--------

### 1. 実行環境

|  項目  |  情報 |
| ---- | ---- |
|  OS  |  macOS 11.3.1  |
|  HW  |  MacBook Air (11-inch, Mid 2013)   |
|  MDM環境  |  microsoft intune  |

### 2. 実現したいこと

**最終目標**

- GUIの操作なしにmacのセットアップが行える環境を整備したい


**今回の目標**

- GUIの操作なしにシステム環境設定 > セキュリティとプライバシー > プライバシー の設定をセットアップする

### 3. CLIを使った制御

調査したところ…関連した情報を3つほど得ることができたが、採用は見送りとした。

1. tccutilコマンド
1. /Library/Preferences/.GlobalPreferences の設定変更
1. /Library/Application Support/com.apple.TCC/TCC.db の設定変更


tccutil コマンドは、Big Sur(11.3.1)では、reset(削除)するオプションしか存在せず、「操作」に当たらない。また、Configやsqlite形式のファイルを操作し、設定する方法もあったが、継続利用できるかは不透明なため見送りとする。

<details><summary>参照リンク</summary><div>

https://stackoverflow.com/questions/37865439/terminal-command-to-change-security-and-privacy-advanced-setting

https://www.macobserver.com/tips/macos-disable-webcam-mac/

https://developer.apple.com/forums/thread/119373

https://apple.stackexchange.com/questions/178313/change-accessibility-setting-on-mac-using-terminal

</div></details>

### 4. プロファイルを使った制御(ProifleCreator)

CLIを諦めて、プロファイルにより制御できないか調査した。MDMのポリシー制御設定を使うことで制御できるとの情報があったが、手動で記載するのは辛いため、githubで公開されているProfileCreatorを使って、プロファイルの雛形を作成することにした。

https://github.com/ProfileCreator/ProfileCreator

https://support.apple.com/ja-jp/guide/mdm/mdm38df53c2a/1/web/1.0

### 5. プロファイル生成

今回は、テストのしやすさを重視し、Edge For macOSにてcameraデバイスの制御(拒否)を行なってみる。

プロファイルの生成は、ProifleCreatorを使って、Privacy Preferences Policy Control からCameraデバイスを選択、アプリの登録する。

また、アプリの登録については、Finderからアプリをドラッグ&ドロップすれば登録できる。

![ProfileCreater-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/9eaa9547-0165-9ebd-8c64-5dd1461e1767.png)

制御可能な内容は、設定毎に定められており、Cameraデバイスの制御は、「拒否」することしかできないため、注意が必要。

### 5. プロファイル適用

MDMプロファイルとなるため、何らかのMDMサービス経由で適用が必要。
今回はintuneを利用して、対象端末にプロファイルを適用する。
※ 端末は、すでにintuneへ参加しているものとする。

Microsoft Endpoint Manager Admin Centerにログインし、
デバイス > macOS > 構成プロファイルを開き、プロファイル作成ボタンをクリック。

![intune-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/4c5c6a12-b18d-651b-eff7-9718665aa29b.png)

今回は、以下のようにポリシーを作成/適用。

![intune-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/6a056cb9-864f-f5e3-f225-52bfaf6211ca.png)


適用した後、しばらくするとmacOSにプロファイルが適用される。

![intune-003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/2e159afa-54ea-93fc-1130-6ad277729d97.png)

### 7. プロファイル動作確認

webブラウザ経由でカメラの動作を確認したところ、プライバシー設定にてカメラを設定しているにもかかわらず、カメラが利用できない状態となった。無事成功。

![camera-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/46e9bdb5-9670-778e-5299-4c88614574b4.png)

<details><summary>設定-セキュリティとプライバシー</summary><div>

![camera-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461335/fc7af0f0-a102-f307-bc57-c6d33200c32b.png)

</div></details>

### 8. まとめ


- MDM + プロファイルを使って、GUIの操作なしにシステム環境設定 > セキュリティとプライバシー > プライバシーの設定をすることができた
- ただし、プロファイルを設定しても、システム環境設定の設定項目がグレーアウトするなどの機能はなかった

※なお、プロファイルを設定しない状態でカメラが利用できることは確認済
propsをscript内で扱う


```vuejs:App.vue(親コンポーネント)
<template>
<div>
  <LikeHeader></LikeHeader>
  <h2>{{ number }}</h2>
  <LikeNumber :total-number="number"></LikeNumber>
  <LikeNumber :total-number="number"></LikeNumber>
</div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";

export default {
  data() {
    return {
    number: 10
    };
  },
  components: {
    LikeHeader: LikeHeader
  }
};
</script>

<style scoped>
  div {
    border: 1px solid blue;
  }
</style>
```


```vuejs:LikeNumber.vue(子コンポーネント)
<template>
<div>
  <p>いいね({{ halfNumber }})</p>
  <button @click="increment">+1</button>
</div>
</template>

<script>
export default {
  props: ["totalNumber"],
  computed: {
    halfNumber() {
      return this.totalNumber / 2;
    }
  },
  methods: {
    increment(){
      this.number += 1;
      }
    }
};
</script>

<style scoped>
  div{
    border:1px solid red;
  }
</style>
```


![20210509-103929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/a77015d1-bef0-3087-5377-d1a61f89e2b6.png)
Rubyで解くAtCoder ABC 200 (A,B,C問題まで)

# はじめに
Rubyの勉強の一環として，[AtCoder](https://atcoder.jp/)のコンテストの過去問を解いています。
（割と楽しくて，半分くらい趣味になっていますが・・・）

# コンテストに出てみた
せっかくなら大会に出たほうが目標ができて楽しめると思ったので，初心者向けのコンテストに出てみました。

https://atcoder.jp/contests/abc200

結果としてはA~Fの問題のうち，A~Cまで解けて，DやEの問題を読んで考えてみている間に制限時間が終わってしまいました。
Rubyでの解説は少ないため，解説を読んで自分なりに考えたりしたことをまとめてみようと思います。


# A - Century
1~3000の数字 N が与えられるので，西暦 N 年は何世紀かを出力する問題。

https://atcoder.jp/contests/abc200/tasks/abc200_a

「金額にしたとき，何枚の100円玉で払えるか」という小学生向けの覚え方を思い出して，whileループで解いてみました。

```ruby:ABC200_A
n = gets.to_i
cnt = 0

while n > 0
  cnt += 1
  n -= 100
end

puts cnt
```

【解説を読んで】
「99を足して100で割って小数点以下を切り捨て」だともっと短く書けました。

```ruby:ABC200_A
N = gets.to_i
puts (N + 99)/100.to_i
```


# B - 200th ABC-200

整数 N に対し， 以下のいずれかの操作を K 回行った結果を出力します。

1. 整数 N が 200 の倍数であれば、N を 200 で割る。
2. そうでなければ、整数 N を、N の後ろに文字列として 200 を付け加えた整数に置き換える。

https://atcoder.jp/contests/abc200/tasks/abc200_b

操作 1 はそのまま 200 で割ればいいとして，操作 2 は文字列に変換して操作してから数値に戻す必要があるかなと思ったのですが・・・
操作 2 は「1000 をかけて 200 を足す」とすれば数値のまま同じことができるので，こうしました。

```ruby:ABC200_B
n,K = gets.split.map(&:to_i)

K.times do
  if n%200 == 0
    n /= 200
  else
    n = n*1000 + 200
  end
end

puts n
```


# C - Ringo's Favorite Numbers 2

N 個の数列内から 2 つの数値を選んだとき，その差が200で割り切れる ( 0 も含む) 組み合わせの個数を求める問題。

https://atcoder.jp/contests/abc200/tasks/abc200_c

知識がまだ少ないので，自分なりに考えてやってみました。
「200 で割った余りが同じ数字どうしの組は，差が 200 で割り切れる」ことが基本方針です。

まず，数列内の全ての数値を，200 で割った余りに変換します。
その後，group_byメソッドで値ごとにキーにしたハッシュを生成します。

(例)
[123, 223, 123, 523, 200, 2000]
↓
[123, 23, 123, 123, 0, 0]
↓
{123 => [123, 123, 123], 23 => [23], 0 => [0, 0]}

数字が 2 つあれば，その中から 2 つ選ぶ組み合わせは $ _2 C _2 $ 通り
数字が 3 つあれば，その中から 2 つ選ぶ組み合わせは $ _3 C _2 $ 通り
数字が 4 つあれば，その中から 2 つ選ぶ組み合わせは $ _4 C _2 $ 通り
...
数字が n つあれば，その中から 2 つ選ぶ組み合わせは $ _n C _2 $ 通り

ということで，キー値ごとの値の個数を $ _n C _2 = o
 $ で計算して足し合わせています。

```ruby:ABC200_C
N = gets.to_i
nums = gets.split.map(&:to_i)
nums.map! { |i|i%200 }

h = nums.group_by{|i| i}
cnt = 0

h.each_value do |arr|
  n = arr.length
  cnt += n*(n-1)/2
end

puts cnt
```


# 感想
初めての大会でしたが，非常に楽しめました。
D問題以降は基本的なアルゴリズムの知識が必要そうなので，解説を読んだりほかの過去問も解いてみて，少しずつできるようになりたいと思っています。
ほぼ毎週，初心者向けの大会があるらしいので，来週も出てみようと思います。
React状態管理ライブラリJotaiとValtioのGitHubスター数推移を見てみた

https://twitter.com/dai_shi/status/1391205851138908162


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63327/a7e5a3a8-8c15-af93-f188-5947f6f40518.png)

これを見ると、jotaiの順調な伸びに比べるとvaltioの伸びが良くないように見えます。valtioはv1リリースして、jotaiはまだv1リリースに到達していないのですが。ちなみに、valtioの初回リリースのスター数の伸びは凄まじかったです。このグラフでもほぼ直角に上がっているように見えますね。

それぞれのリンクはこちら。

https://github.com/pmndrs/jotai

https://github.com/pmndrs/valtio

どちらが好みですか？「React Fan」というコミュニティのSlackでjotaiやvaltioに関する質問や雑談ができますので、よろしければご参加ください。

[React開発者向けオンラインサロン「React Fan」の入り口ページ](https://react-fan.axlight.com/)

[React開発者向けオンラインサロン「React Fan」のTwitterアカウント](https://twitter.com/reactfanjp)
【Ubuntu入門】Ubuntuにて他のUbuntu端末のディレクトリをマウントするためのシェルを作成する【Jetson入門】

# 目次
1. [背景](#背景)
1. [使用した端末](#使用した端末)
1. [ゴール](#ゴール)
1. [ソフトウェアのインストール](#ソフトウェアのインストール)
1. [実行](#実行)
1. [確認](#確認)

# 背景
多くの方はPCではWindows OSを使用していると思います。
しかし、近年のIoTやAIの普及に伴い、自己学習用にjetsonシリーズを購入されている（もしくはしようと検討されている）方も多いかと思いますが、
慣れないOSのため上手く使いこなせていないことがあるかと思います。
ここではWindows OSでいうところの共有フォルダをUbuntuで実現するための方法をご説明します。

# 使用した端末
- 端末１：データ加工メイン
    - [NVIDIA JETSON AGX XAVIER](https://www.nvidia.com/ja-jp/autonomous-machines/jetson-agx-xavier/)
    - os：Ubuntu 18.04 LTS
- 端末２：データ収集メイン
    - [NVIDIA JETSON NANO](https://www.nvidia.com/ja-jp/autonomous-machines/embedded-systems/jetson-nano/)
    - os：Ubuntu 18.04 LTS

# ゴール
端末１に端末２のディレクトリをマウントするためのシェルを作成し、
シェルを実行して端末２のディレクトリをマウントします。

# ソフトウェアのインストール
他端末のディレクトリをマウントするためにはsshfsを使用します。
端末１にて下記コマンドを実行してsshfsをインストールします。

```
sudo apt-get update
sudo apt-get install sshfs
```

# 実行


+ 端末２にて共有用のディレクトリを作成します

```
mkdir hoge2
```

+ 端末１にてマウント用のディレクトリを作成します

```
mkdir hoge1
```

+  端末１にてファイルを作成します

```
touch mount_nano_hoge2.sh
```

+ 先ほど作成したファイルをvimエディタで開きます

```
sudo vi mount_nano_hoge2.sh
```

+ vimエディタで開いた状態でキーボードの"i"を押下し挿入モードにします
+ 下記コマンドを入力します
 
```
sshfs <端末２のユーザー>@<端末２のipアドレス or ホスト名>:/home/hoge2 /home/hoge1
```
+ 入力が終わったらエスケープキーを押下し、":w"を入力して保存後":q"を入力してvimエディタを閉じます
+ ターミナルから下記コマンドを実行します<br>
※端末２のユーザーのパスワードが聞かれると思いますので、パスワードを入力してください。

```
sh mount_nano_hoge2.sh
```

# 確認
下記コマンドを実行して"<端末２のユーザー>@<端末２のipアドレス or ホスト名>:/home/hoge2"が表示されていれば成功です。


```
df
```
LeafletでGPSログ(GPX)地図：GPXファイルをURLで任意指定可能に。

地図データを扱うJavaScriptライブラリ「Leaflet」を使って、GPSログ(GPXファイル)の地図表示を試している。

今回、GPXファイルをURLで任意指定できるようにした。

詳細は以下に。
https://2ndart.hatenablog.com/entry/2021/05/09/104740
![20210509101852.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374794/4bc6964d-a8ef-90f5-ca36-4227bc2cc2d4.jpeg)
JupyterLab 2.2.6→3.0.14 update方法

# はじめに
anaconda navigatorを起動直後に、『version 3にupdateしろ』と急かされるが、画面の指示通りでは、なぜかアップデートできなかった。
またanacondaパッケージのjupyterLabがデフォルトのversion2.2.6である。古すぎてほとんど拡張機能が使えないため、なんとかこれをversion 3.0.14に上げたい。anaconda navigatorからアップデートするも何故か上がらない。

そこでanacondaを再インストールして、やり直すことにしました。以下はその手順です。

# 手順
まずは、「プログラムと機能」からAnacondaアンインストール。
そして、
###  Anacondaインストール
インストール中に問われる、
"Add Anaconda to my PATH environment variable"のチェックは付けてインストールした。一般的にこのチェックは推奨ではない。なぜならチェックすると複数pythonのバージョンを切り替え操作などに影響がでるためらしい。でもpythonのバージョン切り替えなら、dockerでなんとかなるし、パッケージ毎に、いちいち足りないPATHをanacondaに入れるのは大変なのでチェックすることにした。

そして、Windows「システムの詳細設定」から、以下の環境変数を追加で設定。

C:\Users\(ユーザー名)\anaconda3
C:\Users\(ユーザー名)\anaconda3\Scripts
C:\Users\(ユーザー名)\anaconda3\Library\bin

###  anacondaインストール後のAnacondaバージョンアップ
いろいろ彷徨ってたら、

https://qiita.com/jordi/items/cd974b668e7ecf312543

の記事を見つけ、下記の通りこれを実行しました。

コマンドのうち、『conda update --all』はユーザ権限だと権限が弱すぎてエラーになるため、
Anaconda Promptを管理者モードで起動

```
conda install anaconda
```
　
jupyterLabだけでなく、他のパッケージも全てアップデートしてしまう。

```
conda update --all
```


### 実行後のバージョン確認
Anacondaバージョン：4.10.1
Pythonバージョン：3.8.8

無事jupyterLabのバージョンも上がった。
![スクリーンショット 2021-05-09 103023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554997/502c5dcd-f35e-6378-0bea-3497ff16db71.png)
【GoogleAppsScript】スプレッドシートIDの見方と取得

# 見方
スプレッドシートURLから確認できる。
`https://docs.google.com/spreadsheets/d/xxx.../edit#gid=0`
の`d`と`edit`に挟まれた`xxx...`の部分がスプレッドシートIDに当たる。


# 取得
- スプレッドシート
`var spreadsheet = SpreadsheetApp.openById("xxx...");`

- シート
`var sheet = spreadsheet.getSheetByName("【シート名】");`（シート1など）
親子コンポーネント間でのデータの受け渡し

親コンポーネントから子コンポーネントまで

```rb:main.js
import Vue from 'vue';
import App from './App.vue';
import LikeNumber from './components/LikeNumber.vue';

Vue.config.productionTip = false;
Vue.component("LikeNumber", LikeNumber);

new Vue({
  render: h => h(App)
}).$mount("#app");
```

```vuejs:App.vue
<template>
<div>
  <LikeHeader></LikeHeader>
  <h2>{{ number }}</h2>
  <LikeNumber number="6"></LikeNumber>
  <LikeNumber number="6"></LikeNumber>
</div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";

export default {
  components: {
    LikeHeader: LikeHeader
  }
};
</script>

<style scoped>
  div {
    border: 1px solid blue;
  }
</style>
```

```vuejs:LikeNumber.vue
<template>
<div>
  <p>いいね({{ number / 2 }})</p>
  <button @click="increment">+1</button>
</div>
</template>

<script>
export default {
  props: ['number'],
  methods: {
    increment(){
      this.number += 1;
      }
    }
};
</script>

<style scoped>
  div{
    border:1px solid red;
  }
</style>
```

```vuejs:LikeHeader.vue
<template>
  <p>いいね数合計</p>
</template>
```

![20210509-104027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/3117b27a-8e39-30f6-ce53-6fe22ab30e08.png)



###propsはpropertyの略
＜子＞
（１）numberという値を使いたいところが「子」コンポーネントになります。今回はLikeNumber.vue
(2)propsは配列で使いたい値の名前を書きます。[子コンポーネントで定義します]
＜親＞
（１）属性名で定義する[<LikeNumber number="6">]＊なるべくケバブケースで定義する

propsの命名規則
-

|キャメルケース|ケバブケース|
|--|--|
|totalNumber|total-number|

*ブラウザに直接渡すものはHTMLの慣例としてケバブケースで書くようにする。
理由）ブラウザは大文字を認識できずに全て小文字で解釈するため。


（参考）App.vueのnumberを動的に表現するためv-bindを使用

```vuejs
<template>
<div>
  <LikeHeader></LikeHeader>
  <h2>{{ number }}</h2>
  <LikeNumber v-bind:number="number"></LikeNumber>
  <LikeNumber :number="number"></LikeNumber>
</div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";

export default {
  data() {
    return {
    number: 10
    };
  },
  components: {
    LikeHeader: LikeHeader
  }
};
</script>

<style scoped>
  div {
    border: 1px solid blue;
  }
</style>
```

![20210509-103929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/d65881b6-a678-9158-b4cb-d2cfac48c6e8.png)
PhpStromで直前のコミットメッセージを修正する。

### 直前のコミットメッセージを間違えたので修正したい。

直前の「second commit」を「first commit」に修正したい。

![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/61f37709-fe1a-5920-8b40-12475cb89863.jpeg)

Amendにチェックを入れてコミットメッセージを修正後にAmend Commitボタンを押す。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/88c842be-0cd4-6607-63e7-e804f17b3845.jpeg)

コミットメッセージが無事修正された。

![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/651dcb62-c386-3bdc-5bb2-c1208f9c6842.jpeg)
PhpStormでコミットをまとめる。

# PhpStromでコミットをまとめる。

1,2,3のコミットがあったとします。
こいつらを統合させたい。
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/2fd293fc-6867-db6d-277f-6d0b9be7e94a.jpeg)

統合したいコミットを選択してSquash Commitsを実行する。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/e4f42335-6338-2494-3096-5def10a8b695.jpeg)

統合するコミットのメッセージを編集します。

![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/ea79fafe-a683-d18b-d61a-960ce094ca2f.jpeg)

↓ 好きなコミットメッセージを書いて

![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/13df8dca-d199-e4da-2413-ee93f335e7f0.jpeg)

2秒ぐらいするとコミットが統合されていまあす。

![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99731/8ab53937-1122-b81d-9c55-35a0b68f5057.jpeg)
[Python / pandas] SettingWithCopyWarning:  A value is trying to be set on a copy of a slice from a DataFrame. が発生するのはどんなとき

# 概要

タイトルの通りです。
以下の warning を再現させる最小限（のつもり）のコードを記録に残しておいたものです。

```python
/pandas/core/indexing.py:1743: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  isetter(ilocs[0], value)
```

長い記事を読みたくないので、サクッと読める分量にまとめました。
ほとんど何も考えなくても理解できる記事を目指しています。

# ソースコード

google colaboratory 上で発生させました。

## 1. まずは準備から

```python
import pandas as pd

numbers = pd.DataFrame({'number': [1, 2 ,3 ,4]})
numbers

	number
0	1
1	2
2	3
3	4
```

## 2. 原因

これが良くないらしい
つまりこれで再現する

```python
# 行方向にスライス
evens = numbers.loc[numbers['number'] % 2 == 0]
evens

	number
1	2
3	4
```

## 3. 再現

```python
# はい、発生
evens['str'] = ['2', '4']

/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  """Entry point for launching an IPython kernel.
```

## 4. 対策

「2. 原因」のところのコードで、スライス後に`copy()`を使えば解決する

```python
# 行方向にスライス、の後にcopy()を追加。これでOK。
evens = numbers.loc[numbers['number'] % 2 == 0].copy()
evens

	number
1	2
3	4

# 上記のように、スライス結果をcopy()したものに対してであれば、
# 行追加してもwarningは出ない
evens['str'] = ['2', '4']
```

## 5. 結論

スライスしたら`copy()`しよう。

いつもこのwarning出して、原因思い出せなくて時間食う....(´;ω;｀)ぐすっ

# 関連記事

詳細について知りたい場合はこの記事が詳しいです

https://note.nkmk.me/python-pandas-setting-with-copy-warning/
DETR(End-to-End Object Detection with Transformers)の解説でわかりやすいと思った記事N選(まだN=3)

#　概要
以下の論文の「<font color="orange">**DETR(End-to-End Object Detection with Transformers)**</font>」を理解するときに、わかりやすいと思った記事をリストアップする。
※すみません、記事と書いてますが、いまのところ、すべて、Youtubeです。
※実は、この論文が、重要なものだということを理解していなかった。なんとなく図が安っぽく見えたので。。。。

End-to-End Object Detection with Transformers
https://arxiv.org/abs/2005.12872
Carion, Nicolas, et al. "End-to-end object detection with transformers." European Conference on Computer Vision. Springer, Cham, 2020.

# 記事一覧

| |記事名|URL|評価(いいと思ったときに★を入れる)|
|:--:|:--|:--|:--|
|1|DETR: End-to-End Object Detection with Transformers (Paper Explained)|https://www.youtube.com/watch?v=T35ba_VXkMY|:star2:|
|2|DETR - End to end object detection with transformers (ECCV2020)|https://www.youtube.com/watch?v=utxbUlo9CyY|:star2::star2:|
|3|How I Read a Paper: Facebook's DETR (Video Tutorial)|https://www.youtube.com/watch?v=Uumd2zOOz60|:star2:|




# 【1】DETR: End-to-End Object Detection with Transformers (Paper Explained) [YouTube,Yannic Kilcher]

Youtube。Yannic Kilcherさんのチャンネル。たぶん、有名。

https://www.youtube.com/watch?v=T35ba_VXkMY



##なぜ、わかりやすいか

|なぜ、わかりやすいか（１ポイント）|
|:---|
|ロス関数のあたりの説明が詳しい。|

（出典：このYouTube)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342207/7763bd0e-44e9-8516-77e1-533f5ada700e.png)


# 【2】DETR - End to end object detection with transformers (ECCV2020)[YouTube,Nicolas Carion]

https://www.youtube.com/watch?v=utxbUlo9CyY

Youtube。Nicolas Carionさんのチャンネル

|なぜ、わかりやすいか（１ポイント）|
|:---|
|この**論文の執筆者**のチャネル|
|アニメーションで、データの動きがわかりやすい。|

（出典：このYouTube)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342207/89ee1bb9-3c0e-de22-dee2-d366b5aa1b41.png)


# 【3】How I Read a Paper: Facebook's DETR (Video Tutorial)[YouTube,Yannic Kilcher]

https://www.youtube.com/watch?v=Uumd2zOOz60

Youtube。Yannic Kilcherさんのチャンネル。たぶん、有名。
※【1】のYouTubeの人で、【1】の前の初見レベルというか、こんな感じで読みますという内容。

|なぜ、わかりやすいか（１ポイント）|
|:---|
|**論文を読んだ初見がわかりやすく提示される。**|

（出典：このYouTube)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342207/2d2cbe79-7024-11ee-a5de-1ea171879b2a.png)


# まとめ
特にありません。
React-Reduxとfirebaseで作成したアプリに全文検索機能を導入してみた　前編

##はじめに
　Qiita初投稿になりますので稚拙な文章、またコードもお見苦しい点が多いと思いますがご容赦ください。
またご指摘やご指導くださると幸いです。

##自己紹介
　私は昨年末からエンジニアを志し、現在勉強しつつ未経験からの転職を行っている20代後半の看護師です。最近はモダンフロント開発を行えるようになるべくReact-Redux及びFirebaseを用いてアプリの作成を行なっています。

##環境及び参考教材
 create-react-app redux material-ui 
 YOUTUBE　[とらゼミ　トラハックのエンジニア学習講座](https://www.youtube.com/user/1492tiger)
 Qiita [Firestore だけで Algolia を使わず全文検索](https://qiita.com/oukayuka/items/d3cee72501a55e8be44a)

##導入
 ポートフォリオ作成に当たり、全文検索機能を追加しようと調べているとFirestore（Firebaseにおけるデータベース）には全文検索機能は提供されておらず、公式のドキュメントではAlgoliaの使用を推奨していました。
[Algolia](https://www.algolia.com/)
調べてみるととてもイケているサービスですが、従量課金制で今回作成するポートフォリオには向いていないと判断しました。そこで何か他に良い方法はないかと調べてみると、上記の@oukayukaさんの「Firestore だけで Algolia を使わず全文検索」を見つけました。読んでみるとAlgoliaを使用せずn-grumというというものを使用することで全文検索を実装しているようでした。記事の中には実装の概要は記載されていますが、詳しい実装方法やサンプルコードなどはなくそちらは@oukayukaさん著書の[りあクト！ Firebaseで始めるサーバーレスReact開発](https://booth.pm/ja/items/1572683)を読んでね！という形でした。（自分は新装版のReact×Typescriptの本は拝読済み）自分はガッツリ初心者であるため中々に難しい課題であると思いますが、チャレンジしてみることとしました。
　またReact-Reduxについては虎ハックさんの「日本一わかりやすいReact-Redux実践編」を用いて学習させていただいた。

##n-grumとは
 >n-gramとは、任意の文書や文字列などにおける任意のn文字が連続した文字列のことである。

>1文字続きのものはunigram、2文字続きのものはbigram、3文字続きのものはtrigram、と特に呼ばれ、4文字以上のものは、単に4-gram、5-gramと表現されることが多い。 
　　　　　　　　　　　　　　　　辞典・百科事典の検索サービス - Weblio辞書より引用

例えばbigrumは「"注射は痛い"」 => 「"注射","射は","は痛","痛い"」のように区切られる。

##本題
###実施項目
1.textinputで受け取った名前をn-grum関数で分割し、firestoreにマップ型で保存する
2.検索用のフォームを作成する
3.検索フォームに入力された値(以下search)を用いてページ遷移する
4.searchをn-grum関数で分割し、firestoreにクエリを投げる

###実践
　まずはn-grum関数を作成することから始めます。

```nGrum.js
const nGrum = (name,n) => {
        const searchGrums = new Map();
        for(let i=0; i<name.length; i++){
            const results = [name.substr(i,n)]
            results.map(result => {
            searchGrums.push(result)
        })
        }return searchGrums
    };
```

以上。もっとリファクタリングできると思いますが、今はまだこれで十分...（つーかこれが限界)

この関数でnameをby-grumでFirestoreに保存する。のですがここでエラーが...
![スクリーンショット 2021-05-09 9.54.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1063240/283b78b5-ffb0-dcf6-3ef0-2c9b32432b3b.png)
Map型はサポートされていないデータ型だと怒られてしまいます。なので

```nGrum.js
const nGrum = (name,n) => {
        const searchGrums = new Map();
        for(let i=0; i<name.length; i++){
            const results = [name.substr(i,n)]
            results.map(result => {
            searchGrums.push(result)
        })
    }return searchGrums
};

"ここから追加"
const strMapToObj = (strMap) => {
            let obj = Object.create(null);
            for (let [k,v] of strMap) {
              obj[k] = v;
            }
     return obj;
}
```

オブジェクト型に変換して保存を再度試みると成功しました。
![スクリーンショット 2021-05-09 10.06.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1063240/49840d4f-b6f0-1b6f-4226-ed3f29a80a62.png)
これで1.の項目は完了です。
文を簡潔に説明するのが下手で、長くなってしまいましたのでこの記事は前編ということにします。
次回は後編ということで残りの2〜4.を実装していこうと思います。

##参考
[Firestore だけで Algolia を使わず全文検索](https://qiita.com/oukayuka/items/d3cee72501a55e8be44a)
【PyQt5】クリック可能なラベルを自作する

# 作った時の環境

~~~
Python 3.9.4

PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.8.1
sip==6.0.3
~~~

# コード

~~~python
from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt, pyqtSignal
 
class ClickableLabel(QLabel):
    
    clicked = pyqtSignal()
    
    def __init__(self, parent=None):
        super(ClickableLabel, self).__init__(parent)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.clicked.emit()
        return QLabel.mousePressEvent(self, event)
~~~

# 使い方

~~~python
cl = ClickableLabel()
cl.clicked.connect(スロット)
~~~

他は通常の`QLabel`と同じ。

現場からは以上です。

# 参考記事

[シグナルとスロット詳細 \- yu00’s blog](http://yu00.hatenablog.com/entry/2015/09/14/202729)
[python \- pyqtで隠しボタンのようなものを作りたい \- スタック・オーバーフロー](https://ja.stackoverflow.com/questions/43311/pyqt%E3%81%A7%E9%9A%A0%E3%81%97%E3%83%9C%E3%82%BF%E3%83%B3%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE%E3%82%92%E4%BD%9C%E3%82%8A%E3%81%9F%E3%81%84)
[Python 3\.x \- Python、Pyqt5でQLineEditで、クリックされたQlineEditのオブジェクト名をラベルに表示したい｜teratail](https://teratail.com/questions/217114)
【AWS】RDSについてまとめてみた

## RDSとは？
マネージド型データベースサービスです。
EC2にデータベースの高度な機能が付加されています。
リレーショナルデータベースの構築にはRDSが使われます。

Amazon RDSは、PaaSと呼ばれるサービスの１種です。
ApplicationとData以外はAmazonが“サービスとして提供”しています。
つまり、あらかじめセットアップされた状態で提供され、バックアップなどもAWSが運用してくれるため、
ストレスなく使うことができます。

## RDSはEC2の進化形
RDSはEC2に6つの要素を足したものです。

### ①エンジンの選択
計6つの中から選択できます。
・Amazon Aurora
・PostgreSQL
・MySQL
・MariaDB
・ORACLE
・SQLServer

### ②エンドポイント通信
障害が起きた際に親のRDSが使えなくなった際に、
自動で子のRDSの方に接続してくれる機能です。

### ③リードレプリカ
DBに参照用と更新用に分けて運用することができる機能です。
これを分けることによって、負荷分散が可能となります。

### ④自動スナップショット
バックアップの機能があります。

### ⑤自動パッチ
セキュリティの機能があります。

### ⑥パラメーター設定
パラメーターグループを作って設定値を変えることができます。
Rocket.Chat のサブディレクトリ運用サポートが終了予定の件

###作業メモ[21/5/9]


#####現在､サブディレクトリ運用を使用している、rocket.chat[Ver0.73]が古い為、rocket.chat.3.14.0[21/5/9最新] のインストールを試みるが、以前の様に上手くいきませんでした。

起動後のいきなりの初期画面でループしている様な感じです。

![2021-05-09_09h17_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/843085fe-2189-cee8-bdc9-1ea2a69a557a.png)


#####■調べると、どうも、Ver3.2.1から、この現象は起きていたらしい。

--- 現在この問題への対応は、消極的です。  ???

https://github.com/RocketChat/Rocket.Chat/issues/17515



#####■↑ここの情報から、↓以下の情報にたどり着く

Rocket.Chatのサブディレクトリ使用の公式サポートを（リリース4.0以降）で廃止する予定です。


https://forums.rocket.chat/t/support-for-installation-in-subdirectory-will-be-discontinued/6371


#####今後は、サブドメイン運用を検討してくださいとのアナウンスでした。

Rocket.Chatはサブディレクトリ運用が可能だったので非常に残念。
Vision Transformer - Attention = !? 全結合層だけで高精度を出した論文3分解説


#Vision Transformer から Attentionを取り除くとどうなるのか検証した論文"Do You Even Need Attention?"解説
![Do-You-Even-Need-Attention.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635267/b0ce372a-42ae-9c1b-9c09-6f50f6f4965f.png)
画像:["Do You Even Need Attention? A Stack of Feed-Forward Layers Does Surprisingly Well on ImageNet" Luke Melas-Kyriazi (2021)](https://arxiv.org/abs/2105.02723)

## 論文データ
arxiv : ["Do You Even Need Attention? A Stack of Feed-Forward Layers Does Surprisingly Well on ImageNet" Luke Melas-Kyriazi (2021)](https://arxiv.org/abs/2105.02723)
github : https://github.com/lukemelas/do-you-even-need-attention

## 概要
画像分類などの視覚タスクにおいてVision Transformerが優れた性能を発揮するのは、Multi-Head Attentionレイヤーの設計によるものが多い。しかしAttentionがどの程度パフォーマンスに寄与しているのかは明らかではない。この論文ではAttentionをパッチ方向の次元に適応される全結合層に置き換えて性能を確かめた。

## ViTとの違い
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635267/334b31af-01e1-658e-f702-ee49a31efb36.png)
画像 : ["An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale",(2021)](https://arxiv.org/abs/2010.11929)
Vitとの違いはMulti-Head Attentionを単純な全結合層に変更しただけである。

## コード
論文にPytorchのコードが載っているので見た方がイメージしやすいと思います。

``` python 
from torch import nn

class LinearBlock(nn.Module):
    def __init__(self, dim, mlp_ratio=4., drop=0., drop_path=0., act=nn.GELU,
        norm=nn.LayerNorm, n_tokens=197): # 197 = 16**2 + 1
        super().__init__()
        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()
        # FF over features
        self.mlp1 = Mlp(in_features=dim, hidden_features=int(dim*mlp_ratio), act=act, drop=drop)
        self.norm1 = norm(dim)
        # FF over patches
        self.mlp2 = Mlp(in_features=n_tokens, hidden_features=int(n_tokens*mlp_ratio), act=act, drop=drop)
        self.norm2 = norm(n_tokens)
    def forward(self, x):
        x = x + self.drop_path(self.mlp1(self.norm1(x)))
        x = x.transpose(-2, -1) #特徴量次元とパッチ次元を入れ替えている
        x = x + self.drop_path(self.mlp2(self.norm2(x)))
        x = x.transpose(-2, -1) #入れ替えたのをもとに戻す
        return x

class Mlp(nn.Module):
    def __init__(self, in_features, hidden_features, act_layer=nn.GELU, drop=0.):
        super().__init__()
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.act = act_layer()
        self.fc2 = nn.Linear(hidden_features, in_features)
        self.drop = nn.Dropout(drop)
    def forward(self, x):
        x = self.fc1(x)
        x = self.act(x)
        x = self.drop(x)
        x = self.fc2(x)
        x = self.drop(x)
return x
```
非常に単純ですね... (実際はPositional Encodingなどが必要なのでもう少し複雑ですが)

## 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635267/48e8c067-54b9-a022-0cd6-2b5124e2846b.png)
TinyではDeiTほどの性能は出ていないもののBaseにおいてはViTに近い性能が出ています。Largeの場合にはViT,FF Onlyともに性能は下がっているもののViTを超える性能を出しています。
また、Attentionのみの場合、Tinyモデルで28.2%の性能しか出なかった。

## まとめ

- ViTはAttentionなしでも驚くべき画像分類能力を持っている
- ViTiのパフォーマンスの凄さはAttentionよりもpatch embeddingや学習法によるものが多いのかもしれない
Learn Python In Seminar〜基本統計量と簡単なグラフ化〜

# 概要

今回は基本統計量を確認し、基本的なヒストグラムと棒グラフにする方法です。
他にも「[Learn Python In Seminar](https://qiita.com/k8m/items/4952894a80c1833e5501)」のシリーズとして書いているので、よければご覧ください！

## 基本統計量

### インポート

```
import pandas as pd
from matplotlib import pyplot as plt
```

### 基本統計量

Pandasのdescribe関数で基本統計量を確認可能

```
# 数値型以外の変数も合わせて見る場合はinclude='all'を渡す
DataFrame.decribe(include='all')
```

↓ 結果の例

```
            test1       test2 
count  198.000000  198.000000  # データの個数
mean     1.616162   23.961111  # 平均値
std      0.827289    8.399846  # 標準偏差
min      1.000000    9.000000  # 最小値
25%      1.000000   17.000000  # 第一四分位数
50%      1.000000   23.000000  # 第二四分位数(中央値)
75%      2.000000   30.375000  # 第三四分位数
max      3.000000   46.600000  # 最大値
```

中央値より平均値が大きい場合
→データの分布は値が小さい方向に偏っている可能性がある

# データの可視化

## DataFrameとSeries

**DataFrame**：横と縦に広がっているエクセルのような２次元データ
**Series**：１カラムを選択したデータ型であり、１列のみの１次元データ

```
type(data) # dataがDataFrameかSeriesか確認可能
```

## インポート

```
import pandas as pd
from matplotlib import pyplot as plt
```

## ヒストグラム

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542018/f0e5711a-a1d8-c476-9d63-920a80e32644.png" width=100 height=100></img>



- 度数分布
- 棒の間に間隔がない
- 量的データを確認

```
Series.plot.hist(title='ヒストグラムのタイトル')

plt.show()
```

## 棒グラフ

- 独立したデータの比較
- 棒の間に間隔がない
- 質的データを確認

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542018/9733e25a-6854-aff6-cb71-1759c9ed8aac.jpeg" width=100 height=100></img>

### 最頻値

Pandasライブラリのvalue_counts関数で最頻値が確認可能

```
Series.value_counts()
```

↓ 結果の例

```
Out[ 3 ] :  
FR     65
FF     52
4WD    31
RR     27
MR     23
```

### グラフの描画

Pandasライブラリのplotメソッド

```
Series.plot.bar()
plt.show()
```
↓ 最頻値がないとエラー

```
data = pd.read_csv('data.csv')
Series = data['カラム名'].value_counts()
Series.plt.bar()
plt.show()
```

### 縦軸と横軸に名前

```
plt.xlabel('X軸につけたい名前')
plt.ylabel('Y軸につけたい名前')
```
Linuc受験後の考察①

###はじめに
私は先日101、102を合格したばかりの初学者です。中身は抽象的なものも多いので、あくまで__参考__にする程度に留めておいてください。
###覚えておいた方が良い事象
####①コンピューターは１か０かでしか理解できない。
以前私は、コンピュータは、なんでも認識できる魔法使いだと思ってました。これは普段
コンピューターを無意識に使っている世界中を探しても同じことを思っている人は、意外と多いのでは？と思いました。

例えば
”ボタン”があったとしたら
人間様
”ボタン”と読めます

コンピューターからには
●●○●●、、、、、、（ボ）（第1589通り目）→人間様で言う”ボ”
●●○●●●●●●○、、（タ）（第２０００通り目）→人間様で言う”タ”
○●○●●●○●●○、、（ン）（第４０００通り目）→人間様で言う”ン”
と認識します
＊これは例なので、上記の通り目は適当に書いてます。

なので、人間とコンピューターの間で翻訳する”シェル”が必要になります。

####②コンピューターには指示書なしでは何もできない
これはコンピューターにはなんでこんなファイルがあるんかな？って思ってました。
実は起動する時もシャットダウンする時もいかなる時も、スクリプトファイルという指示書にそって動いているだけだったのです。
なぜそれに気がつかなったか？

それは日常使っていると縁がないからです。
縁がないってことは、このファイルは人間様が読める必要がないので、私たちには何が書いているかわかりません。

じゃあ私たちが指示書を作るにはどうすればいいのか？
1.指示書を作成する。2.翻訳してあげる＝コンパイル

ということです。

③流れで覚える
OSを起動するには
1.BIOS（UEFI）に電気が入る　＊SCSIチップというハード
2.ブートローダー（カーネルを呼び出す係）　＊スクリプトファイルの集合体
3.カーネルが起動（OSを起動する係）　＊スクリプトファイルの集合体

ざっとこんな感じです。
IT用語がどんどん出てきますが、
どんな役割があるのか
それは実体があるものなのか？それともファイルの集合体の名前なのか？
勉強するにあたり、実体のないものや概念に苦しむと思います。
【Golang】sort.Sliceでtime.Time型のスライスをソート

## 概要

`time.Time`の型を持ったstructのスライスをソートする方法
sort interfaceを満たすメソッド(Len, Less, Swapメソッド)を用意する方法よりこちらの方が簡単なので、メモ


## 実装

```go
package main

import (
	"fmt"
	"time"
	"sort"
)

type User struct {
	Name      string
	CreatedAt time.Time
}

func main() {
	us := []User{
		{
			Name:      "john",
			CreatedAt: time.Now().Add(time.Minute),
		},
		{
			Name:      "kenta",
			CreatedAt: time.Now(),
		},
	}
	fmt.Println(us)
	sort.Slice(us, func(i, j int) bool {
		return us[i].CreatedAt.Before(us[j].CreatedAt)
	})
	fmt.Println(us)
}
// Sort前
// [{john 2009-11-10 23:01:00 +0000 UTC m=+60.000000001} {kenta 2009-11-10 23:00:00 +0000 UTC m=+0.000000001}]
// Sort後
// [{kenta 2009-11-10 23:00:00 +0000 UTC m=+0.000000001} {john 2009-11-10 23:01:00 +0000 UTC m=+60.000000001}]
```
vue.jsでコンポーネントのfilterの引数を複数にする

調べててジャストミートな日本語の情報が見つからなかったので共有します。
ちなみに、マルチバイト、シングルバイト混在文字列をいい感じの長さで切りたかった時のものです。

以下のようにfilterのparameterを複数定義すればOKで、デフォルト値も設定可です。

```vue
<script>
// 文字列のByte長を取るfunction
function strByteLength(strSrc) {
  let len = 0;
  const escapedStr = escape(strSrc);
  for (let i = 0; i < escapedStr.length; i++, len++) {
    if (escapedStr.charAt(i) === '%') {
      if (escapedStr.charAt(++i) === 'u') {
        i += 3;
        len++;
      }
      i++;
    }
  }
  return len;
}

export default {
  name: '***コンポーネント名***',
  filters: {
    truncate: (value, len = 10, bytesize = 20, omit = '...') => {
      // stringかチェック
      if (typeof value !== 'string') {
        return value;
      }

      // 一旦、Maxバイト数(bytesize)で対象文字列を前から切り出し
      let str = value.slice(0, bytesize);
      // 切り出し文字数(len)にと半角文字数/2を足す ※大凡文字幅を全角:半角=2:1.5程度で計算
      let sliceLength = len + Math.floor(((str.length * 2) - strByteLength(str)) / 2);
      // bytesizeを超えてた場合は、bytesizeに差し替え ※全て半角のような場合
      sliceLength = sliceLength > bytesize ? bytesize : sliceLength;
      // 文字列を前から切り出した場合はomit文字列を末尾に付与
      str = value.length > sliceLength ? value.slice(0, sliceLength).concat(omit) : value;

      return str;
    },
  },
};
</script>
```

そして、呼び出し元はこう。
targetStringがtruncateのvalueとして渡ります。

```vue
{{ targetString | truncate(20,29) }}
```

なお、function strByteLengthは何処かからの頂き物です。※ソースを失念しました。。。
【ポートフォリオ作成・トラブル克服録】Railsのmaster.keyファイルをGithubに公開してしまった

ちょっと唐突ではあるが、
Qiitaへの投稿を再開。

タイミングとしては、
web appのポートフォリオを作るための学習がある程度一巡したことと、
ポートフォリオを作成＆ネット上にデプロイする過程で、
エラーとかトラブルに見舞われることが多くなったため、
どんなエラー起こして・どのように解決したかを記録していつか見返す備忘録を作っておこうと思ったから。

誰かに読んでもらうことを想定してないので、
とりあえず記録のために内容を書き殴って、
気が向いた際に文章構成等をみやすく整えていく予定。

#＜本日の作業 その１＞
TravisCIを経由してHerokuにAppをデプロイ

#＜本日のトラブル その１＞
GitGuardianというメルアドから「Rails Master Key exposed on Github」(対象アプリのRailsのMaster KeyがGithubに出ちゃってますけど…)というメールが来てる…。
あぁ、そういうことか。昨日、手動でmaster.keyをコピーして追加したから、
その後のプッシュで公開されちゃったのか…
いや、でもmaster.keyのファイルってgitignoreにリストされてなかったっけ？
↓
vs codeでフォルダを確認 → gitignoreファイルが無い…
↓
一応terminalからも確認 → やっぱり無い…
ありゃ、そもそも元々のファイルからGit Cloneしたときにgitignoreファイルが入ってなかったのか??
↓
clone元のGithubリポジトリ確認したら、きちんとGitignoreファイルは存在していた…
どこかで削除してしまったのか？

これ以上やると沼に嵌りそうなので、とりあえず原因調査はここで一時中止…
(またいつか時間があるときに再捜査するからね〜♪)
clone元のgitignoreファイルのコードをコピペして、ローカルディレクトリにも同じものを作成→再度push

よし、これでｍasterkeyはGithubから消えるはず!? いや待てよ、Githubで過去のコードって残るんじゃなかったっけ？そうだよね、だって過去のソースコードを辿って復旧できるんだもん…（これもあとで調べないと…）
調べた結果 → 一度コミットされたファイルは、あとから.gitignoreに追加してもGitに残り続け、git rmで削除しないと削除されたということがコミットで反映されない（）

下記の記事を参考にファイル削除後、Gitにフォースプッシュ
GitHubに誤って自宅WiFiパスワードをPushしてしまったので対処した件（https://thom.hateblo.jp/entry/2021/02/27/145223）
GitHub にパスワードとかセンシティブなファイルを push してしまったときの対処法
（https://qiita.com/dtan4/items/34e41e3bd40a43fd8cbf）

#＜本日の反省点＞
・gitignoreファイルを確認する習慣をつける
・パスワード関連の情報に対してもっと敏感になる

#＜今後の対策＞
・どんなに注意しても人為ミスが起きることを前提にインシデントが起きた際の対処策に対する理解を含める
・GitGuardianのような、リスク管理をサポートするソフト・アプリについて学ぶ
enebularでinfomotion

#概要

enebularでinfomotionやってみた。

#写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/09841b75-6352-9fa1-4f0e-61d621717672.png)

#参考にしたページ。

https://qiita.com/uhidekita/items/3e64413f1822b32d7bb7

#Data Sourceを作成

+ [左のメニューのData Sources]-[右下の+ボタン]
+ 表示されたダイアログのTitleを入力し、-roomdata
+ Data Source TypeにはAPI Gatewayを入力します。
+ EndpointとAPI Keyの入力欄が表示されますので、下記のように入力します。
+ Endpoint: https://**.herokuapp.com/apigw-datasource
+ API Key:test-api-key

#InfoTypeを作成

+ enebularの上部にあるDiscoverをクリックします。
+ 検索欄にline-chartと入力し、enebularの帯が表示されたInfoTypeをクリックします。
+ Preview画面が表示されますので、右上のImportボタンをクリックし、今回作成したProject名を選択してInfoTypeを取得します。

#InfoMotionを作成

+ 左のメニューからInfoMotionsをクリックし、右下の+ボタンを押下します。
+ 表示されたダイアログで、Nameと必要に応じDescriptionを入力します。
[Manage Graphsボタン]-[Create Graphボタン]を押下し、次のように設定します。

+ Name: temperature
+ Select InfoType: line-chart
+ Select Data Source: roomdata
+ label: roomname
+ value: temperature
+ timestamp: ts

+ Saveボタンを押下し、作られたGraphをAddするとDashboardにグラフが追加されます。
+ 上部の時刻表示をクリックすると表示する時間帯を選択できます。
+ Dashboardのレイアウトを保存したい場合はSave Layoutボタンを押下します。

以上。
STM32G4のDACをTIMで駆動してDMAで転送して2chの波形を出す


NUCLEO-G474REボードを使っています。


## CubeMXの設定

### DAC4の設定

今回はDAC4を使うことにしました(ピンアサインの競合の関係で)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/9dd11271-b10e-136f-be7a-35eea6d100ff.png)

今回はテストデータとして正弦波を使うので、Signed FormatをEnableに設定しています。またTriggerにTimer 7 Trigger Out eventを設定しています。

### DAC4のDMAの設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/35c8f7f1-a3a2-09dc-4b6d-2f04ef3c642f.png)

Data WidthのPeripheral側は必ずWordに設定してください。Memory側はフォーマットに応じて設定することになりますが、今回は8bitデータが2ch分でHal Wordを設定しました。12bit幅をフルに使う場合はMemory側もWordになります。
あとはModeをCircularに設定し、必要に応じてPriorityも設定しておきます。

### OPAMP4/5の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/919f0867-2573-3dc5-5818-40c1d5fac48a.png)

OPAMP4のModeをFollower-DAC4_OUT1-INPに設定します。それ以外はデフォルトです。
OPAMP5も同様に、Follower-DAC4_OUT2-INPに設定します。

### TIM7の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/c0762ac0-db00-7243-3489-56871625d755.png)

TIM7を適当に設定しておきます。
今回はコアクロックが170MHzなので、周期を170-1として、1MHzのトリガを発生させます。
Trigger Event SelectionにUpdate Eventを設定しておきます。

## DACの起動

適当な正弦波を作成し、DACを開始します。OPAMPも有効にして、最後にタイマをスタートします。
今回は1MHzトリガなのでポイント数を1000とし、1kHzの正弦波が出るようにしました。また、デュアルチャンネルなので、cosとsinで90度位相をずらした信号を設定しました。
sin/cosは-1から+1の範囲ですが、DACには8bitのデータを渡すので、127倍しています。DACは符号有りモードに設定しているので、オフセットは不要です。

```c++
constexpr uint16_t N = 1000;
static struct
{
    int8_t ch1;
    int8_t ch2;
} data[N] = {};

for (uint32_t i = 0; i < N; i++)
{
    constexpr float pi = 3.1415926535897932384626433832795f;
    data[i].ch1 = static_cast<int8_t>(cosf(i * pi * 2 / N) * 127);
    data[i].ch2 = static_cast<int8_t>(sinf(i * pi * 2 / N) * 127);
}

HAL_DACEx_DualStart_DMA(&hdac4, DAC_CHANNEL_1, reinterpret_cast<uint32_t *>(data), N, DAC_ALIGN_8B_R);
HAL_OPAMP_Start(&hopamp4);
HAL_OPAMP_Start(&hopamp5);
HAL_TIM_Base_Start(&htim7);
```

![2021-05-09_04-11-38 - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264452/e315c5e9-77c5-7daf-64a6-d8336233a6b8.png)

綺麗な波形が出ていますね。

## 雑記

今回、なぜかDMAが動かなくて、数時間溶かしました。結局、DMAのPeripheral側の幅をWordにしないとダメだった、という。リファレンスマニュアルを読めば「32bitでアクセスしろ」と書いてあるので、ちゃんと仕様書を読め、という話なんですが。。。
以前はDMAの幅は気にせずに動いていたような気がします。前にQiitaに投稿したF3でDACをDMAで転送するサンプルもHalfWordで転送しているようですし。もしかしたらG4はそのあたりが厳しくなったのかもしれません。F3でもF4でも同様に「32bitでアクセスしろ」と書いてあるので、仕様外なのにたまたま動いていただけかもしれませんが。

今回の教訓
「公式のドキュメントは隅から隅まで読み尽くせ」
アソシエーションが理解できなくて頭の中がおっぱぴー

<h1>アソシエーションを理解したい！！</h1>

プログラミング学習を初めて１ヶ月になります。
１ヶ月間、HTML/CSS,Rubyの基礎を勉強してきました。
今現在はRuby on Railsを勉強中です。
Ruby on Railsって覚えること多すぎて
理解が追いついていません。
アソシエーションの概念を十分理解していないまま
カリキュラムを読み進めると頭がおっぱぴーしてました。
ということでアソシエーションをより理解したいので
アソシエーションについての記事を書くことにしました。

<h1>アソシエーションとは？</h1>

Ruby on Railsにおける、複数のモデルの情報を
取得する際に使用する概念のことです。
一方のモデルからもう一方のモデルにアクセスできるようにします。
モデルに定義することで、そのモデルに紐付く別のモデルの情報へ
アクセスすることができます。

<h1>アソシエーション使用方法</h1>

twtterを例に例えてみましょう。
twitterにはユーザーを管理するモデル(User)とツィートを管理するモデル(Tweet)が存在します。
（テーブル数はもっとあると思いますが、まだ見習いなんで許してネ。）
(User)はTweetモデルに対して「１対多」、
Tweetモデルはモデル(User)に対して「１対１」の関係性になります。

これは例えばタカシというtwitterアカウント（Userモデル）は
複数ツィート（Tweetモデル）できますが、
ツィート（Tweetモデル）はタカシ（Userモデル）しか出来ないです。

「１対多」の関係性をUserモデルに　

```user.rb

has_many :tweets
```
と記述します。

この場合は
User has many Tweetsの状態といえ、
「Userモデル（タカシ）は多くのTweetモデル（ツィート）を持っている」
ということになります。

「１対１」の関係性をTweetモデルに

```tweet.rb

belongs_to :user
```
と記述します。
この場合は
Tweet belongs to Userの状態といえ、　
「Tweetモデル（ツィート）はUserモデル（タカシ）に属している」
ということになります。


これらの記述により、UserモデルとTweetモデルはテーブルの関連付けが
できます！！
コミットやSlack上の発言から日報を自動で生成するツールを作った

# 作ったもの

こんな感じで、指定したリポジトリに積んだコミットやSlack上の発言を収集して、日報を自動で生成できます。
[![Image from Gyazo](https://i.gyazo.com/473f48c6123473dc1c29fd919be0cc9f.gif)](https://gyazo.com/473f48c6123473dc1c29fd919be0cc9f)

毎日日報を書くのがめんどくさい方、自分がその日に積んだコミットをモニタリングしたい方、ぜひご利用ください。

<https://github.com/MasatoraAtarashi/nippo>

# インストール方法

### Homebrew

    $ brew install masatoraatarashi/nippo/nippo

### go get


    $ go get -u github.com/MasatoraAtarashi/nippo-generator

# 使い方

#### 1. SlackのAPIトークンを取得

[こちらの手順](https://qiita.com/ykhirao/items/3b19ee6a1458cfb4ba21)を参考にSlackのAPIトークンを取得してください。

User Token Scopesで`search:read`を指定してください。
[![Image from Gyazo](https://i.gyazo.com/102a89d9cc86631437cb42a108bfee28.png)](https://gyazo.com/102a89d9cc86631437cb42a108bfee28)

#### 2. 初期化する

    $ nippo init

上記のコマンドで$HOME/.nippo.yamlという設定ファイルが生成されます。

#### 3. 設定ファイルを編集する

生成された設定ファイルを編集してください。

$HOME/.nippo.yaml

```yaml
template:
    #日報に含めたい見出しを自由に設定してください。
    - 今日やったこと
    - 明日の予定
    - 所感・連絡事項
    - git
    - slack
git:
    heading: "git"
    repositories: 
        #コミットを取得したいディレクトリの絶対パスを記入してください。
        - "Users/MasatoraAtarashi/workspace/hogehoge"
        - "Users/MasatoraAtarashi/workspace/hogehoge2"
slack:
    token: "" #Slack APIトークンを記入してください。
    username: "" #Slackのユーザ名を記入してください。
```

#### 4. 日報を生成する

    $ nippot generate

出力例

```md
# 2021-05-06
## 今日やったこと


## 明日の予定


## 所感・連絡事項


## git
### hogehoge(3 commits)
 - 7ae0175 Add count option
 - 7335722 Add slack to default config
 - 3bcd230 Add slackname option

### hogehoge2(1 commits)
 - 3bcd230 Update README

## slack
 - `よろしくお願いいたします！` (random)
 - `こんにちは` (テスト)
```

# オプション

- templateを編集すれば日報に含める内容を自由に変更できます。
- repositoriesを編集すれば日報に含めたいリポジトリを自由に指定できます。

# 使った技術

golangとcobraというライブラリを使って作りました。
<https://github.com/spf13/cobra>
【数理考古学】群論とシミュレーション原理④群導出演算としてのオイラーの公式(Eulerien Formula)

[【数理考古学】群論とシミュレーション原理①これまでの投稿内容のまとめ。](https://qiita.com/ochimusha01/items/a7c824df4b5fd85a2788)
吃驚するほどグチャグチャになってしまったので整理を試みます。

#オイラーの公式(<font color=green>Eulerien Formula</font>)

これもその**演算結果集合**が「<font color=purple>半径1の単位円</font>」を観測結果集合とする**円周群**=**リー群**[tex:S_0]=**1次元トーラス**に対応する演算の一つで、角度θによって円周上の位置を指定する事が出来ます。

```math
e^{iθ}=(1±\frac{πi}{n})^n=\cos(θ)+sin(θ)i\\
特にθ=πラジアン(180度)の時\\
e^{πi}=-1
```

この演算からは**三角関数**と**指数・対数関数**の関係、**微積分の意味**などを明らかにする事が出来ます。
[等速円運動についての物理学と数学の立場の違い?](https://qiita.com/ochimusha01/items/d5526764c7d1199671df)
![image.gif](https://camo.qiitausercontent.com/fa349dd64b85d0da84f63ee475e79148eb983e70/68747470733a2f2f73746167696e672d71696974612d757365722d636f6e74656e74732e696d6769782e6e65742f687474707325334125324625324671696974612d757365722d636f6e74656e74732e696d6769782e6e6574253246687474707325323533412532353246253235324671696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2532353246302532353246353937373732253235324631616332643937622d323937382d346230362d623131382d3930363732613536663538392e67696625334669786c696225334472622d312e322e322532366175746f253344666f726d61742532366769662d71253344363025323671253344373525323677253344313430302532366669742533446d61782532367325334430663266393364333236343937366634393336343733393831346436336537383f69786c69623d72622d342e302e30266175746f3d666f726d6174266769662d713d363026713d373526773d31343030266669743d6d617826733d3834616130626365363061653733623835616234313639343162323261653264)

* $\cos(θ)=\frac{e^{θi}+e^{-θi}}{2}$
* $\sin(θ)=\frac{e^{θi}-e^{-θi}}{2i}$

![image.png](https://cdn-ak.f.st-hatena.com/images/fotolife/o/ochimusha01/20210504/20210504162020.png)
![image.png](https://cdn-ak.f.st-hatena.com/images/fotolife/o/ochimusha01/20210504/20210504162153.png)

* $e^{ix}\frac{d^n}{dθ^n}=(i e^{i x}(-\log ix),-e^{ix},-i e^{i x}(\log ix),e^{ix},…)$
* $\int \int \int … \int e^{ix}(dθ)=(- i e^{i x}(\log ix),- e^{i x},i e^{i x}(-\log ix),e^{ix},…)$

式形$(1±\frac{πi}{n})^n$は**ベルヌーイがネイピア数を導出するのに使った式**の応用です。
[【数理考古学】冪乗算(Exponentiation)の微積分](https://qiita.com/ochimusha01/items/0bd18370cfa96e83347a)

```math
lim_{n \to \infty}(1±\frac{1}{n})^n=lim_{t \to 0}(t+1)^{1/t}=e^1(2.71828)
```
この式を用いれば「(実数冪のそれとは明らかに異なる)**虚数冪の対数写像の振る舞い**」を観測する事が出来るのです。
[【Rで球面幾何学】「世界で一番美しい公式」オイラーの等式の罠？](https://qiita.com/ochimusha01/items/7fa9d091f7ba9414dfc3)
![image.gif](https://camo.qiitausercontent.com/93de38a37ab4e4e18ccf1be2f6d2008d6ff331e7/68747470733a2f2f63646e2d616b2e662e73742d686174656e612e636f6d2f696d616765732f666f746f6c6966652f6f2f6f6368696d7573686130312f32303231303432392f32303231303432393133313434352e676966)
![image.gif](https://camo.qiitausercontent.com/9febf32e2ce7db077b26accc5edede453cfef226/68747470733a2f2f63646e2d616b2e662e73742d686174656e612e636f6d2f696d616765732f666f746f6c6966652f6f2f6f6368696d7573686130312f32303231303432392f32303231303432393132333834352e676966)

逆を言えば、この**演算結果集合**が「<font color=purple>半径1の単位円</font>」を観測結果集合とする**円周群**=**リー群**[tex:S_0]=**1次元トーラス**に対応するのはネイピア数の近似が十分な精度以上の場合に限られるという事になります。

そんな感じで以下続報…
System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Discount_Percent__c]: [Discount_Percent__c]

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)

# このエラーはよく見ますね

前のチャレンジの設定が残っているんですね。
必須のチェックを外せば大丈夫です。


# 解答例
Answersの回答は秒を争います。私のように母国語が英語でない、かつ英語は大嫌い（すみません、外国語全般が大嫌いです）の場合にいちいち英文を考えていたら回答できません。そのためのコピペ用です。

I think the settings for the previous challenge remain.

Setup-->Object Manager --> Choose (Opportunity)-->Fields-->Edit(Discount_Percent__c]) and uncheck required 


+ [Can someone explain me this error ?](https://trailblazers.salesforce.com/answers?id=9063A000000l0JWQAY)
+ [unexpected error in your org](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000A8d42)

# 類似の質問を整理します。

[Anyone else experienced this error message? I've attempted three times each time deleting everything and still getting the same response](https://trailblazers.salesforce.com/answers?id=9064S000000kFBMQA2)
[RPA]UiPathのOCR機能を利用して複数のPDFの内容をExcelに転記してみた(For Each処理)

## [0]はじめに
Zitanです。
今回は複数のPDFファイルに対してOCR処理を行い、内容をExcelファイルに書き込むまでの処理を実装しました。
前回は、「Microsoft OCRエンジン」を使用していましたが、
日本語を抽出する精度は「OmniPage OCR」の方が高いということで実際使ってみたところ、たしかに抽出する精度が高かったです。

## [1]結果
まずは、結果の動画をご覧下さい。雰囲気も動画の方が分かりやすいのではないかと。。

<br>

<iframe width="560" height="315" src="https://www.youtube.com/embed/nEErYzKKnBo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

<br>

## [2]作り方
重複する内容もあるので、基本は前回の記事を参考にして下さい。

<b>テストデータ作成マクロ</b>

https://qiita.com/Zitan/items/c3b0ec36dc82676b94c0

<b>UiPathでOCR処理（請求書1件分）してみた</b>

https://qiita.com/Zitan/items/4e2ee5006bf800ffa29f

### 今回使った変数の一覧
変数は[Ctrl+K]で作成すると自動的に型を判別してくれますが、修正が必要な場合もあります。
※入る型が一意に決定するアクティビティに対して便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1408638/466c1bd3-03bc-d4bb-d7c4-bbca7e853d65.png)

<b>＜代入アクティビティの式＞</b>
ファイルコレクション=Directory.GetFiles(選択されたフォルダ)
ドキュメントパス=item.ToString
count=count+1
"A" & count+1

### <b>必要なパッケージ一覧</b>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1408638/0dded506-733a-65a1-b454-8406f98708ea.png)
<font color="Blue"><b>「UiPath.IntelligentOCR.Activities」のバージョンは「v4.10.0」です。</b></font>


<font color="Red"><b>※説明は長くなるので、プロセスの中身は動画をご覧下さい。</b></font>

<b>大きな流れとしては、こんな感じです。</b>
１．OCR処理するファイル一覧が入っているフォルダを選択する（ユーザーが選択）
２．フォルダ内のファイル群をArray型(配列)の変数に格納する
３．For Eachループ処理を行う（ファイル名は、配列を文字列型に型変換して取得します）
４．ファイルに対してOCR処理を行う
５．OCR処理されて抽出されたデータセットをExcelに書き込む



## [3]まとめ
いかがでしょうか。
今回は、タクソノミーは請求書だけを読み込んでいますが、
いくつかのドキュメントを自動判別するプロセスについても検討しようと思います。
あとは、手書きとタイピング文字の混在パターンも興味深いですね。
【javascript】filterMap()をジェネレータ関数を使って作ってみた

#前置き

```javascript
//これを
array.filter().map()
//こうしたかった
array.filterMap()
```

ざっくり言いますと
`filter()`や`map()`の**callback関数**を**ジェネレータ関数**で処理するようにしただけです。
引数は同様。
最初の`yield`で`filter()`、2回目の`yield`で`map()`。

「こんなのがあったらいいな」というものなので、実用に耐え得るか否かはわかりません。



#function

```javascript
const filterMap = (arr, generator) => {
	try {
		if (!Array.isArray(arr)) {
			throw new Error('1st arg : not Array');
		}
		if (typeof generator!=='function'
			 || generator.constructor.name!=='GeneratorFunction') {
			throw new Error('2nd arg : not GeneratorFunction');
		}
		const array = arr.slice();
		const result = [];
		for (let i=0,m=array.length; i<m; i++) {
			const gen = generator(array[i], i, array);
			if ( gen.next().value ) {
				result.push( gen.next().value );
			}
			gen.return();
		}
		return result;
	} catch(e) {
		console.error(e);
	}
};
```


```javascript
const arr = [0,1,7,2,1,3,4,5,9,4,5,0,8,3,5,7,4,4];

//値が5のindexを出力
const idx5 = filterMap(arr, function*(x,i,a) {
	yield x===5;	//filter
	yield i			//map
});
console.log( idx5 );	//  [7, 10, 14]


const chunk = filterMap([...Array(10).keys()], function*(x,i,a) {
	yield i%3===0;			//filter
	yield a.slice(i, i+3);	//map
});
console.log( chunk );	//[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
```


#Array.prototype

```javascript
if (!Array.prototype.filterMap) {
	Object.defineProperty(Array.prototype, 'filterMap', {
		value: function(generator) {
			try {
				if (typeof generator!=='function'
					 || generator.constructor.name!=='GeneratorFunction') {
					throw new Error('arg is not a GeneratorFunction');
				}
				const array = this.slice();
				const result = [];
				for (let i=0,m=array.length; i<m; i++) {
					const gen = generator(array[i], i, array);
					if ( gen.next().value ) {
						result.push( gen.next().value );
					}
					gen.return();
				}
				return result;
			} catch(e) {
				console.error(e);
			}
		},
	});
}
```


```javascript
const arr = [0,1,7,2,1,3,4,5,9,4,5,0,8,3,5,7,4,4];

//値が5のindexを出力
const idx5 = arr.filterMap(function*(x,i,a) {
	yield x===5;	//filter
	yield i			//map
});
console.log( idx5 );	//  [7, 10, 14]


const chunk = [...Array(10).keys()].filterMap(function*(x,i,a) {
	yield i%3===0;			//filter
	yield a.slice(i, i+3);	//map
});
console.log( chunk );	//[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
```

#補足

```javascript
const idx5 = arr.filterMap(function*(x,i,a) {
	yield x===5;	//filter
	yield i			//map
});
```

処理が単純であれば`yield* [filter, map]`でまとめられます。

```javascript:
const idx5 = arr.filterMap(function*(x,i,a) {
	yield* [x===5, i];		// [filter, map]
});
```

#私感・妄想
ジェネレータ関数にもアロー関数のような略した書き方がほしい。

```javascript:妄想.js
//これを
function*(x,i) {
    yield* [x===5, i];
});

//こうして
(x,i) *> {
	yield* [x===5, i]
};

//更にこうして
(x,i) **> [x===5, i];

//こうなる
const idx5 = arr.filterMap((x,i) **> [x===5, i]);
```


```javascript:妄想2.js
//こういうのも
function* increase(n) {
    while (true) yield n++;
});

//こう…欲張りすぎか
const increase = n **> n++;
```

なんてね

#参考

https://qiita.com/kura07/items/d1a57ea64ef5c3de8528

https://qiita.com/ttatsf/items/9cea66553fd415ecf313