OmniAICreator/Qiita-1.07M huggingface dataset:
【翻訳】RailsのフロントエンドはWebpack、Webpacker、Bundler、Asset Pipelineのどれを使えば良いの？


この記事は、Giles Bowkett氏の["Should I Dish Up My Rails Front End With Webpack, Webpacker, Bundler, or the Asset Pipeline?"](https://gilesbowkett.com/posts/2019/05/06/webpacker.html)をご本人の承諾を得て翻訳したものです。
Railsのフロントエンドの実装に使えるツールは色々ありますが、違いがよく分からないと感じていたときに参考になった記事だったので訳しました。

以下より翻訳になります。

# RailsのフロントエンドはWebpack、Webpacker、Bundler、Asset Pipelineのどれを使えば良いの？

「Webpackが具体的に何をしているのかよくわからない」
というコメントをどこかで見たことがあります。
これは多くのRailsエンジニアが疑問に思っていることですが、実は難しい話ではないのでここで整理しましょう。

## 要点まとめ

- Bundlerを使ってJavaScriptやCSS関連のライブラリをインストールするのは絶対にやめましょう。
昔は仕方なかったですが、今はもっと良い方法があります。読んでいる記事に「BundlerでBootstrapをインストール」とか書いてあったら、情報が古すぎるので気をつけてください。

- JSやCSSで一切凝ったことをしない場合、従来のasset pipeline（別名Sprockets）を使うこともできます。ただカスタマイズには不向きです。

- まずはWebpackerを使っておけばほぼ間違いないです。人気のフロントエンドライブラリやフレームワークのインストールオプションがあって便利です。

- ただしJSやCSSを使って標準的ではない実装をしたい場合は、Webpackをある程度理解しておく必要があります（特にconfig fileの編集方法）。

実際には、今日のほとんどのRailsアプリでasset pipelineとWebpackerが共存しています。
Rails 6のアプリケーションでJSファイルを `app/javascript` か `app/javascript/src` 以下に置いておけば、 Webpackerが自動的にまとめ上げてくれます。同様にCSSファイルを `app/asset/stylesheets` 以下に置けば、asset pipeline が自動で拾ってくれます。

ちなみに個人開発では、私はWebpackとWebpackerしか使いません。実はWebpackはJSだけでなくCSSも扱えるのです。ただ、この件について書くと長いので今回は割愛します。

では今回のテーマについて、もっと詳しく見ていきましょう。

## 具体的には？

RailsのバックエンドはRuby一択なので、フロントエンドは選択肢が多くて混乱しやすいかもしれません。
ですがWebpack、Webpacker、Bundler、asset pipeline（Sprockets）の関係性を理解するために必要な前提知識はこの2点だけです。

1. ブラウザが理解できる言語はJavaScript/HTML/CSSだけ。ただ実際は、開発者が他の言語で実装したものをJavaScript/HTML/CSSにコンパイルすることが多い。

2. Railsは他の言語より10年ほど先駆けてこの問題を解決した。だがその結果、Railsでデフォルトとされる方法はレガシーコードになってしまった。

### ブラウザの言語 vs 開発者の言語

ブラウザはこの3つの言語しか理解できません。

- HTML：ドキュメントの構成や内容
- CSS：色などのスタイリング
- JavaScript：ユーザのインプットに応えてHTMLやCSSを変更できるようにする

この3つの言語は今でもちょっとイケてないですが、10年前はさらにひどいものでした。
そのため、JS/HTML/CSSにコンパイルできる言語が開発されました。扱いやすい言語で書いたうえでJS/HTML/CSSにコンパイルして、その結果をブラウザに渡すという流れができあがったのです。
（Railsで実際にこれが行われたのは主にJSとCSSでした。HTMLに関してはすでにブラウザに渡すための流れが確立されていたからです。）

### Railsは問題解決の先駆者

Railsはとても早くにこの問題を解決しました。2005年には既にRubyからJavaScriptへのコンパイルが可能でした。ただ「Ruby → Javascript」のように「言語A → 言語B」にコンパイルをする際、発生する課題が2つありました。

1. コンパイルしたJavScriptやCSSをネットワーク上でどのようにウェブブラウザに送るのか？
2. パッケージのdependency管理はどうするか？

1つ目の課題は、書きやすい言語からJS/CSSにコンパイルした後、それをブラウザに送信することです。開発者としては、ロジックごとにファイルを分割してコードを書きたいものです。しかしいざネットワーク上で送るときは、1つのファイルになっているほうがパフォーマンスが良いです。

2つ目に考えなくてはならないのはパッケージのdependencyの件です。規模が大きなソフトウェアほど、小さな部品を組み合わせてできていることが多いです。部品をどこかから取得して、インストールして、最新のバージョンにアップデートしておく必要があります。

そこでWebpack、Webpacker、Bundler、Asset Pipelineの登場です。それぞれの特徴と違いを挙げます。

- __Asset pipeline（Sprockets）__：開発者が書いた細々としたJSやCSSファイルをまとめて、それぞれ1つの大きなJSファイルとCSSファイルにコンパイルしてブラウザに送信するための仕組み。Railsで先駆けて編み出された方法で、後方互換のために今も存在する。

- __Bundler__：RailsではRuby gemの管理に使うツールだが、実は長い間JavaScriptライブラリのインストールにも使われていた。理由はJavaScriptにライブラリインストール用のツールが存在しなかったから（！）。Ruby gem用に作られたツールなので、本来JSやCSSのライブラリには適さない。JS用のツールがなかった頃にBundlerを使うのは良いハックだったが、今使っても良いことはない。

- __Webpack__：JavaScriptのコンパイル用ツールで、近年になって開発されたもの。高度な使用法としてはCSSのコンパイルにも使える。

- __Webpacker__：RubyでWebpackを使うためのラッパーライブラリ。Webpackの一般的なユースケースを簡単に行えるようにしてある（JSのみ）。

# まずはWebpacker & Asset Pipelineの併用から。高度なユーザにはWebpackがおすすめ。

まとめると上記のとおりです。初心者におすすめする方法は、JavaScriptにはWebpackerを使い、CSSにはasset pipelineを使うことです。慣れているなら両方にWebpackを使うのが良いでしょう。
ec-cube4 において .env を適当に弄るとリダイレクトが発生するようになる

## 経緯
誤ってローカルの `.env` を削除してしまい、本番から持ってきたのはいいがローカルサーバーにアクセスするとリダイレクトが発生するようになり、開発が行えなくなった。

かなり初歩的な話だが、自分は解決に一日を費やしてしまったため、メモしておく

## 解決
`.env` において `ECCUBE_FORCE_SSL` が`true`になっている場合、 http のアクセスが https でのアクセスにリダイレクトされる。
例) http://localhost:8000 => https://localhost
のようにリダイレクトされるようになる。

symfony のビルトインサーバーにおいて、特に設定をしていない場合は https に対応していないため、エラーでページが表示されない。

もちろん解決するには `ECCUBE_FORCE_SSL=false` に設定すれば良い。
git checkout ブランチをした際の Your branch is ahead ofの意味

git branchで確認すると次の様なブランチがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568461/2d721be5-9f65-afa0-9c7d-59cd281aa0ff.png)


そして```git checkout task2-1```をしたところ、次の様に表示されました。


```
Your branch is ahead of 'origin/task2-1' by 1 commit.
  (use "git push" to publish your local commits)
```

意味としては、
```リモートの task2-1 ブランチに対して、ローカルのtask2-1ブランチが1コミット進んでます。
　 "git push" 使って、あなたのローカルのコミットを公開してください。```

と言われています。

初学者向けに「コミットが進んでいる」ということがどういうことかといいますと、コミットが1つ多い、と言う意味です。
（当てはまる言葉が見つからないので見つかりましたが追記します。）


したがって、指示にしたがっても良いのであれば従いましょう。



ローカルリポジトリの ```task2-1``` ブランチのコミットを
リモートリポジトリの ```master``` ブランチに push すればOKです。


```
git push origin [ローカルブランチ]:[リモートブランチ]
```
の書き方になります。

この書き方で、

[ローカルブランチ]を[リモートブランチ]に pushできます。
TouchDesignerの出力でZoomミーティング参加

## Zoomのウェブカメラ映像にTouchDesignerでフィルタかけたい！

こんなことが出来るようになります。rgb delayフィルタをかけた例。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/c2d58ba8-1a6a-7787-f048-6cd107d1a171.png)

新型コロナの影響や、「Zoomでバ美肉」が話題になってたりで、Zoomのカメラ映像を変えるのはホットなのかなと。

ということで、TouchDesignerから出力してみた。

ウェブカメラ画像を入力して、フィルタかけて、Zoomへ送るまで、TouchDesigner上で出来るようになる。

## 試した環境

* Windows10 1909
* Spout 2.006
* TouchDesigner 2020.20020

以下は手順

## SPOUTインストール

ここからダウンロード（Donateしないとね）
https://leadedge.github.io/spout-download.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/e4fb3dbb-3e14-8388-5e7a-2fd7f522d72a.png)

現時点の最新バージョンは 2.006 update2 らしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/cec98c63-8206-a629-4371-535645b81ac1.png)

SpoutCamにチェック入っていることを忘れずに確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/dfdbc739-7ff3-ad2d-94ee-fce633540a76.png)

## ZoomでSpoutCamを選ぶ

SPOUTインストールが終わると、Zoomのカメラとして選択できるようになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/92424ae3-74f3-ec2f-3ce9-70bfb70f37db.png)

## TouchDesignerのTOPをSpoutへ送る

TouchDesignerの [Syphon Spout Out TOP](https://docs.derivative.ca/Syphon_Spout_Out_TOP) を置くだけ。パラメータ変更は不要。

好きなTOP画像を繋げたらZoom側に画像が出る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/22fc4bfa-f9e8-227b-3c20-59c17620e52e.png)

なお、自分でZoom上で見えている画像はX軸（左右）が反転しているようなので、Flip Topを挟んでおくと便利。

ということで、VideodeviceInTOP → 任意のフィルタ → SyphonSpoutOutTOP で最低限はOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69418/15f3e2c5-1dcb-ad24-3024-8fa551ad444a.png)

特に難しいことはなく、SPOUT便利！なだけ。Qiitaに書いといて、って言われたので。

以上
sshで入れない

# 原因

ssh鍵認証でログインする場合、**ホームディレクトリのパーミッションが所有ユーザ以外に書き込み権限があるとアクセスが拒否される。**

# パーミッションとは
ディレクトリとファイルに設定。
「誰に」「どのような操作」を許可するかを設定

## 誰に

| 誰に | 意味 |
|:-----|:-------|
| Owner     | (ファイルやディレクトリの)所有者 |
| Group     | ユーザが所属する |
| Other     | その他のユーザ |

## どのような操作

- r(4):読み取り
- w(2):書き込み
- x(1):実行

### ディレクトリの場合

| 種類 | どのような操作 | 使用可能なコマンド |
|:-----------------|:------------------|:------------------|
| r | ディレクトリ内のリスト表示が可能 | ls |
| w | ディレクトリ内のディレクトリまたはファイルの新規作成・削除が可能 | mkdir,touch,rm |
| x | カレントディレクトリとして移動可能 | cd |

### ファイルの場合

| 種類 | どのような操作 | 使用可能なコマンド |
|:-----------------|:------------------|:------------------|
| r | ファイルの読み取りが可能 | less,more,cp |
| w | ファイルの編集が可能 | vi |
| x | ファイルの実行が可能 |  |
【git】コマンドgit checkout の挙動を説明する（自分用）

git checkout の使い方を自身の環境を用いて説明します。


現在自分がいるブランチを確認すると、以下の様なブランチがあります。

```
git branch
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568461/15cd7685-23a1-93da-5a29-593ce1cd2965.png)


## masterからtask2-1にブランチを切り替えたい（移動したい、またはtask2-1にブランチを切りたいという表現）

下記コマンドでOK。

```
git checkout task2-1
```
fishの動きが遅い

# 本記事の内容
fish shellが重いため原因を特定して解決しました

# fishが重くなった原因
ホームディレクトリに`.git`ファイルができてしまっていた事が原因
fishのプロファイルには`command git ls-files --others --exclude-standard | wc -l | string trim`があり、Gitにおける非追跡ファイルのパス文字列の改行数を数える（？）という処理が行われているようです。

ホームディレクトリに`.git`ができてしまうと、ホームディレクトリ配下の全てのファイルを対象に、過去に一度もステージングエリアに追加していないファイルを検索します。膨大な数のため処理に時間がかかります。

つまりホームディレクトリに`.git`ファイルができてしまったことで、fish profile(`/Users/ユーザ名/.config/fish/profile.txt`にあった)の、gitの非追跡ファイルのパスの文字数カウント処理に時間がかかってしまったのが原因でした

# 原因を突き止めた過程
1. fishの起動時プロファイルを取得する
2. 明らかに時間がかかっているプロファイルを特定（目視）
3. 2.の処理の意味を調べる
4. `command git ls-files --others --exclude-standard`をホームディレクトリで実行
5. ホームディレクトリに.gitが生成されていた
6. gitを削除  
7. 解決


# 原因を突き止めるために取った行動
## 1.fishの起動時プロファイルを取得する
こちらの記事を参考にさせていただきました。
[fish shellの起動時間が遅いのでプロファイルを取得する](https://qiita.com/yagays/items/8093241b6b43b2a38d74)

```shell:以下を実行
$ fish --profile profile.txt
$ exit
$ sort -k 2 -nr profile.txt
```

## 2.明らかに時間がかかっているプロファイルを特定（目視）
```shell
:以下の処理で10秒以上かかっていた
command git ls-files --others --exclude-standard | wc -l | string trim
# 約50000
```
## 3. 2.の処理の意味を調べる
(git公式)[https://git-scm.com/docs/git-ls-files]
### ls-files
> git-ls-files - Show information about files in the index and the working tree

ワークツリーとインデックスに関する情報を表示

### ls-files --others
現在管理していないファイルを表示


### --exclude-standard
>Add the standard Git exclusions: .git/info/exclude, .gitignore in each directory, and the user’s global exclusion file.

対象のリポジトリで指定している[.gitignore, .git/info/exclude]にあるパターンに一致するファイルを表示。

## wc -c
テキストの文字列を数えたりするコマンド。-lを付けることで改行の数を数える。

## string trim
[fish](file:///usr/local/Cellar/fish/2.7.1/share/doc/fish/commands.html#string)
fishにおける、文字列操作のコマンド。trimは各文字列の前後の空白を削除するそうです。

## 4. `command git ls-files --others --exclude-standard`をホームディレクトリで実行
多くのファイルパスが表示されました

## 5.ホームディレクトリに.gitが生成されていた
`.git`を削除しました。


(参考にさせて頂いた記事)
- [VSCodeのSourceControlが５kと表示された時の対処法と今後の対策](https://qiita.com/kouki-iwahara/items/9f53f4f1fc273a954764)


## 6. gitを削除  

おしまい
ABC 157 D - Friend SuggestionsをPythonで解く！

最近のD問題は骨があって解けないことが多く、悲しみを感じる今日この頃です。
終わった後もどう解こうかもやもやしていたので、頑張って解いてみました。

今回のキーワードは、__「無向グラフ」__の__「連結成分」__。
ポイントは__「DFS」__と__「set」__！

[問題へのリンク　ABC 157 D - Friend Suggestions](https://atcoder.jp/contests/abc157/tasks/abc157_d)

#コンテスト中の回答
無向グラフであることに気づいて、すぐさまDFSで解くことに。
各頂点からDFSを使って連結成分から友達でもブロックでもない、点をカウントしていきます。
2重ループであきらかに間に合わないだろうなと思っていたら、案の定TLEでした。
コンテスト中はこれでタイムアップ。

```python
from collections import deque

N,M,K = map(int,input().split())
friend = [list(map(int,input().split())) for _ in range(M)]
block = [list(map(int,input().split())) for _ in range(K)]

g = [[False]*(N+1) for _ in range(N+1)]
for i in friend:
    g[i[0]][i[1]] = True
    g[i[1]][i[0]] = True


b = [[False]*(N+1) for _ in range(N+1)]
for i in block:
    b[i[0]][i[1]] = True
    b[i[1]][i[0]] = True
    
stack=deque()
ans = []

for i in range(1,N+1):
    cnt = 0
    visited = [0] * (N+1)
    visited[i] = 1 
    stack.append(i)
    while stack:
        n = stack.pop()
        for j in range(1,N+1):
            if g[n][j] and visited[j]==0:
                stack.append(j)
                visited[j] = 1
                if g[i][j] == False and b[i][j]==False:
                    cnt+=1
    ans.append(cnt)
print(*ans)
```

#コンテスト後の回答
inを使う場合は、ListよりSetのほうが早いと聞いて、使ってみることにしました。
いろいろ試行錯誤しながらTLEと戦った結果、以下でACをいただきました。

linkに連結成分をためていき、連結成分を求める。
連結成分の探索が終わったら、
連結成分の各点に関して引くべき友達関係とブロック関係の数を引くことで求めています。

```python
from collections import deque

N,M,K = map(int,input().split())
friend = [list(map(int,input().split())) for _ in range(M)]
block = [list(map(int,input().split())) for _ in range(K)]

f = [set() for _ in range(N+1)]
b = [set() for _ in range(N+1)]

for i,j in friend:
    f[i].add(j)
    f[j].add(i)
for i,j in block:
    b[i].add(j)
    b[j].add(i)

stack=deque()
ans = [0]*(N+1)
visited = [0]*(N+1)

for i in range(1,N+1):
    if visited[i]:
        continue
    link = {i}
    visited[i] = 1 
    stack.append(i)
    while stack:
        n = stack.pop()
        for j in f[n]:
            if visited[j]==0:
                stack.append(j)
                visited[j] = 1
                link.add(j)
    for i in link:
        ans[i] = len(link) - len(link & f[i]) - len(link & b[i]) - 1
print(*ans[1:])
```
　
##工夫ポイント
探索済みを調べるのが明らかに無駄なので、探索済みは探索しないように追加しました。

```python
if visited[i]:
    continue
```
　
ある点に対してすべての頂点が友達関係であるかを確認していたところを、
その点に対して友達関係にあるものだけから探索するように変更しました。

```python
for j in range(1,N+1):
# ↓↓↓
for j in f[n]:
```
　
連結成分のサイズから、友達関係またはブロック関係の数を引くのに一工夫しました。

```python
ans[i] = len(link) - len(link & f[i]) - len(link & b[i]) - 1
```
　
つまり、どういうことかというと…
積集合を使うことで、連結成分の中から友達関係・ブロック関係の数を求めています。

```python
f = [set for _ in range(10)]
b = [set for _ in range(10)]

#連結成分
link = {1,2,4,9,10}
#1と4・10が友達関係
f[1] = {4,10}
#1は5・6・9をブロックしている
b[1] = {5,6,9}

print(link&f[1], link&b[1])
print(len(link&f[1]), len(link&b[1]))
```

```python
{10, 4} {9}
2 1
```
はてなブログ iPhoneで撮影した写真を添付しようとしたら「アップロードに失敗しました」「未対応のファイル形式です」となった話

# 目的

- はてなブログに画像を載せようとした時に「アップロードに失敗しました」「未対応のファイル形式です」となった話をまとめる  
![スクリーンショット 2020-03-03 16.26.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8332dd82-ae7a-416e-e69a-ca12cdafc138.png)

# 原因

- iPhoneの画像拡張子である.heicがはてなブログに対応していないためである。

# 解決方法

- 画像の拡張子を変更する。
    - 下記リンク先にアクセスし拡張子を変更する。
        - [HEIC変換フリーソフト](https://www.apowersoft.jp/heic-to-jpg)
Java6アプリからSSHJを使用してSSH接続する

## やりたいこと
Java6でコンパイルした自作アプリから別マシンに、[SSHJ](https://github.com/hierynomus/sshj)ライブラリを使用して、SSH接続することです。
以前は[JSch](http://www.jcraft.com/jsch/)ライブラリを使用して実現しようとしたのですが、[こちらの記事](https://qiita.com/sanoa/items/86c9e9efb5358601b25b)に書いたように上手くいかなかないので、JSchライブラリからSSHJライブラリに変更して実現しようとしています。

## 環境
### 接続先
- Ubuntu 16.04 LTS
- OpenSSH 7.2p2

### クライアント
- Windows 8.1
- 自作アプリ（Java6で開発）
- [SSHJ](https://github.com/hierynomus/sshj)

## サンプルコード
SSH接続時にパスワード認証を行うサンプルはたくさん見つかると思いますが、鍵認証（パスフレーズあり）を行うサンプルが少なかったように思えたので、備忘録をかねて、接続に成功したサンプルコードを記載しておきます。

単にSSH接続するだけではツマらないので、SSH接続後にファイルのダウンロードを行うようなコードにしてみました。

オール大文字の英字は、外部から指定してもらうなり、コード内で定義するなりしておく必要がある部分です。
あと、例外処理はだいぶサボってます。

```java:Sample.java
        SSHClient ssh = new SSHClient();
        ssh.addHostKeyVerifier(new PromiscuousVerifier());
        try {
            ssh.connect(HOST, PORT);
            ssh.authPublickey(USER, ssh.loadKeys(PRIVATEKEYFILEPATH, PASSPHRASE));
            ssh.newSCPFileTransfer().download(SRCDIR, new FileSystemFile(DSTDIR));
            ssh.disconnect();
            ssh.close();
        } catch (UserAuthException e) {
            e.printStackTrace();
        } catch (TransportException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
```


## 鍵認証時の注意点
SSHJで使用可能な秘密鍵ファイルは、PEM形式のファイルのようです。

秘密鍵ファイル・公開鍵ファイルは`ssh-keygen`コマンドを使用して作成すると思いますが、使用する`ssh-keygen`コマンドのバージョンによっては、デフォルトで以下の形式の秘密鍵ファイルを生成します。
SSHJライブラリでこの秘密鍵ファイルを使用すると認証に失敗します。

```
-----BEGIN RSA PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
：
：
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----
```

SSHJライブラリで認証するためには、以下のような形式の秘密鍵ファイルを使用します。

```
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
：
：
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----
```

上のようなPEM形式の秘密鍵ファイルを生成するためには、`ssh-keygen`コマンドのオプションとして、`-m PEM`を指定します。

-- 以上 --
GitHub ActionsでPHPUnitを実行する

# GitHub Actionsとは

> GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。

[GitHub Actions](https://github.co.jp/features/actions)

# 事前準備

まずはcomposerでPHPUnitをインストールし、テストを実装しておく。
そしてcomposer.jsonにscriptsを追加しておく。

```json
    "scripts": {
        "test": "vendor/bin/phpunit"
    }
```

# ワークフローを作成する

ワークフローはリポジトリにYAMLファイルで作成する。

.github/workflows/{name}.yml

GitHubで作成するなら、リポジトリのActionsタブを開く。
テンプレートから選んでもいいし、「Set up a workflow yourself」から自分で作成してもいい。
ここではPHPのテンプレートをベースに作成していく。

.github/workflows/test.yml

```yml
name: Test

on: [push]

jobs:
  test:
    name: Test

    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Validate composer.json and composer.lock
      run: composer validate

    - name: Install dependencies
      run: composer install --prefer-dist --no-progress --no-suggest

    - name: Run test suite
      run: composer run-script test

```

リポジトリにPushするとワークフローが実行される。

# 複数のPHPバージョンでテストする

## PHPのセットアップ

PHPのバージョンを切り替える必要があるのだが、残念ながら公式のPHP Setup Actionが無いので他を探すことになる。

[shivammathur/setup-php](https://github.com/shivammathur/setup-php)

```yml
    steps:
    - name: Setup PHP 7.2
      uses: shivammathur/setup-php@v2
      with:
        php-version: '7.2'
```

## PHP7.2と7.3でテストを実行する

strategyで実行環境のバリエーションを定義する。
[GitHub Actionsのワークフロー構文 - GitHub ヘルプ](https://help.github.com/ja/actions/reference/workflow-syntax-for-github-actions)

```yml
    strategy:
      matrix:
        php-version: ['7.2', '7.3']

    steps:
    - name: Setup PHP ${{ matrix.php-version }}
      uses: shivammathur/setup-php@v2
      with:
        php-version: ${{ matrix.php-version }}
```

# 最終的なワークフロー

```yml
name: Test

on: [push]

jobs:
  test:
    name: Test

    runs-on: ubuntu-latest

    strategy:
      matrix:
        php-version: ['7.2', '7.3']

    steps:
    - name: Setup PHP ${{ matrix.php-version }}
      uses: shivammathur/setup-php@v2
      with:
        php-version: ${{ matrix.php-version }}

    - name: Checkout
      uses: actions/checkout@v2

    - name: Validate composer.json and composer.lock
      run: composer validate

    - name: Install dependencies
      run: composer install --prefer-dist --no-progress --no-suggest

    - name: Run test suite
      run: composer run-script test

```

# 参考サイト
[GitHubActionsでPHP7.4環境のPHPUnitを実行する - istyle Tech Blog](https://techblog.istyle.co.jp/archives/4143)
Python2.7の辞書型の要素取得方法

#Python2.7の辞書型の要素を取得する方法

辞書についてよくわかってないのでメモ代わりです。
2.7なのは、私がPythonを利用するメイン環境がMotionBuilderだからです。

##in
###key in dictionary
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print("key1" in dictionary)

#True
```
指定のkeyの項目が存在するかどうかがboolで返ります。
###key in dictionary.values()
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print ("key1" in dictionary.values())
print ("value1" in dictionary.values())

#False
#True
```
こちらは指定のValueが存在するかどうかがboolで返ります。
##スライスとget()による取得
スライスとは[]での取得の事
###dictionary[key]
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print (dictionary["key1"])

#value1
```
###dictionary.get(key)
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print (dictionary.get("key1"))

#value1
```
どちらもvalueが取得できることには差がないが、対象がなかった時のふるまいが異なる。
スライスの場合はエラー、get()の場合はNoneが返ってくる

```py

print (dictionary.get("key4"))

#None
```

指定のkeyが存在しない場合の戻り値をあらかじめ準備も可能

```py
print (dictionary.get("key4","no value"))

#no value
```

##keys()
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print(dictionary.keys())

#['key3', 'key2', 'key1']
```
値はリストで取得される。なんで末尾から取得されるのか少し調べてみたがわからない。
知ってる人がいたら教えてほしい。

##value()
```py
dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print(dictionary.values())

#['value3', 'value2', 'value1']
```
こちらも値はリストで取得される

##items()
```py

dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
print(dictionary.items())

[('key3', 'value3'), ('key2', 'value2'), ('key1', 'value1')]
```
keyとvalueを同時に取得できる。リスト内の各要素はタプルになります。

##iteritems()
```py

dictionary = {"key1":"value1","key2":"value2","key3":"value3"}
for k, v in dictionary.iteritems():
    print k,v

#key3 value3
#key2 value2
#key1 value1
```
こちらもkeyとvalueを同時に取得できる。
for文で回す場合はこちらを利用するようだ。
python3系のitems()はデフォルトでこちらを返すようです。

##まとめ
辞書型は苦手だったが、こうしてまとめてみたことで自分自身でも少し整理できた気がする。
値が末尾から取得される件を改めて調べたい。

##参考にしたページ
https://qiita.com/jinshi/items/9f0f7ae716bce77a1b8a
https://www.headboost.jp/python-dict-keys-tips/
accepts_nested_attributes_forとfields_forを使ってデータを保存で苦戦した（has_one とbelongs_to 使用）

環境　rails 5.2.3 ,ruby 2.5.1

# 問題点
taskの内容は保存されるのにdaytimeのデータは全く保存されない！
## model task
class Task < ApplicationRecord
  has_one :daytime
  accepts_nested_attributes_for :daytime
end

## model daytime
class Daytime < ApplicationRecord
  belongs_to :task ,optional: true
end


# 改善点 tasks_controller.rb
- before
def new
@task = Task.new
@task.daytimes.new
end

- after
def new
    @task = Task.new
    @task.build_daytime
  end

@task.daytimes.newだと
undefined method `new' for nil:NilClassとエラーが出ていたので
@task.build_daytimeにしたら出なくなった

また、ストロングパラメータも
daytime_attributes:とする！
daytimes_attributes:ではない！

# 改善点２　views/tasks/new.html.haml
= f.fields_for :daytime do |i|
とする！

= f.fields_for :daytimes do |i|
ではない！

# まとめ
has_oneはあまりつかったことがなく、いつもhas_many ばかり使っていたため
複数形、単数系の違いでものすごくエラーにハマった！
has_oneの場合は単数系であることを意識していこう！
にしてもどうして
@task.daytimes.newの表記ではダメなのかは分からなかった。
ちなみにnew→buildにしてもdaytimes→daytimeにしてもこの表記はだめだった。。。
残差と誤差の違いって何？メモ

機械学習を勉強している際に、残差と誤差の違いがよくわからなくなったので自分なりの解釈をまとめておきます。

# 残差と誤差は別のもの
自分は残差と誤差は別のものを指していると解釈しています。
自分なりの解釈は、
**残差の定義**は、
```(残差) = (観測した値) - (予測した値)```
**誤差の定義**は、
```(誤差) = (観測した値) - (真の値)```

であり、予測した値と真の値は異なるので、残差と誤差は異なると思います。

だから、機械学習でよく出てくる
$$ y^{\(i)} - yhat^{\(i)} $$
においては$yhat^{\(i)}$は予測した値を指しているので、残差であると思います。(yの真上に^ってどうやって書く？）
これを誤差と表現されている場合が多くあったので、自分のなかで納得がいかず、ここに書いています。

例えば、平均二乗誤差の定義式など。

よく似ているのですが、使い分けた方がいいのではないかと思いメモってみました。

正直あまり自信がないので、自分なりに理解が深まったら書き足すかと思います。
詳しい人がいらっしゃりましたら、訂正してほしいです。
無料のApp Serviceマネージド証明書を発行する

# はじめに

無料のApp Serviceマネージド証明書を実際に発行して実装してみたのでその手順をメモする。この機能はカスタムドメインに対して無料の証明書を発行し、その証明書はApp Serviceで管理され、さらに自動で更新されるというもので、いつも悩ましい証明書の運用から解放される可能性のあるサービスである。現在この機能はプレビュー公開されている。

公式ドキュメントの手順は下記の通り。
https://docs.microsoft.com/ja-jp/azure/app-service/configure-ssl-certificate#create-a-free-certificate-preview

# 制限事項など

- ワイルドカード証明書はサポートされない
- ネイキッド ドメインはサポートされない
- エクスポート不可
- Aレコードはサポートされない (CNAMEを使う必要がある)

事前にDNSレコードを設定しておく必要がある。以下のURLのチュートリアルはAzure DNSでの設定方法だが、こちらを参考に設定すると良い。
https://docs.microsoft.com/ja-jp/azure/dns/dns-web-sites-custom-domain
App Services は、TXTレコードを、カスタム ドメインの所有者であることを検証するために構成時に参照するので、TXTレコードも忘れずに設定しておくことが重要。

# 手順のメモ

**プレビュー機能なので今後GAに向けて設定画面や手順等は変わっていく可能性がある事に注意**

## カスタムドメインをApp Serviceに適用

Azure PortalのApp Serviceの画面から[Custom domains]メニューを選択し[Add custom domain]を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/0835f431-33db-20b8-81e9-6f8c7c24e5c4.png)

Custom domain欄にカスタムドメインのCNAMEレコードを入力し[Validate]を押して有効性をチェックする。チェック項目が両方ともグリーンのステータスになったら[Add custom domain]をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/0b8fb5ed-c5f1-9c5d-dc7e-dc9393551ff6.png)

設定に成功すると以下のような画面になる。カスタムドメインはアサインされているが、SSL証明書が設定されていない状態になっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/d59d8787-d15d-7835-5325-d8e51ef785af.png)

## App Serviceマネージド証明書の作成
App Serviceにカスタムドメインをアサイン完了したら、マネージド証明書を作成する。[TLS/SSL setting]メニューに入り、[Private Key Certificates (.pfx)]を選択し、[+ Create App Service Managed Certificate]を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/84c4d95a-4f28-be01-4706-0c5a62e60219.png)

設定済みのCNAMEレコードを選択できるようになっているので、選択して[Create]を押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/0409ce3b-eb14-73a2-4b40-119255835b4b.png)

証明書の発行が完了すると以下のような表示になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/3c5158c8-95d3-976e-b6be-fc15ab647184.png)

このように証明書がApp Serviceで管理される。これは楽かもしれない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/42c32b30-0fd3-3452-ccd0-d533aaf76988.png)


## 証明書の適用
証明書が発行されたので、これをカスタムドメインに適用する。[Custom domains]メニューに入り、ドメインのステータス欄のカスタムドメインの行が"Not Secure"となっていることを確認し、右の[Add binding]をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/5d1e345e-7329-4077-ded0-27fba0d04111.png)

カスタムドメインと発行した証明書をドロップダウンから選択し、SNIベースかIPベースかを選択する。ここではSNIを選択。設定完了したら下部の[Add Binding]を押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/f18a8a8a-2b92-d9f5-0faf-6582f89e59eb.png)

SSL STATEがSecureに変わっていればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/beb20658-fd37-45d9-b933-0940c10f5951.png)

ブラウザからアクセスして確認。証明書は[DigiCert](https://www.digicert.com/)から発行されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144789/45e3a69a-b02f-f90d-b616-d2161e68876f.png)
スマフォ写真で３Dスキャンができる→３Dプリントまでの手順

#概要
- スマフォで立体の撮影をして，３Dモデルをつくる手順を詳しく説明した[YouTubeの動画](https://www.youtube.com/watch?v=1bJfzCBTBKQ)
- ソフトはAutoDesk社のReCap PRO
- 学生と教職員は無料で使えるとのこと
- 「[元ネタの動画](https://www.youtube.com/watch?v=1bJfzCBTBKQ)では，ロッククライミング教室の掃除機の先端をつくる」というプロジェクトを題材につくられています．

![スクリーンショット 2020-03-04 20.08.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237044/d3da1e41-1c38-e7f7-345a-df5a77013543.png)
[動画サイト](https://www.youtube.com/watch?v=1bJfzCBTBKQ)からのスナップショット


#元ネタ
- [YouTube動画：WKWK-3D：ReCap Photoで写真から3Dモデルを作る!：13分23秒](https://www.youtube.com/watch?v=1bJfzCBTBKQ)
- [AutoDesk社：ReCap Proの公式ページ](https://www.autodesk.co.jp/products/recap/overview)

# YouTube 簡単マイコン教室

- [簡単マイコン教室
　micro:bitの使い方をYouTubeで配信中](https://qiita.com/tmdoi/items/61be444e1879912aa2a5)
政府統計(e-Stat)の API と elastic stack を試した



# はじめに
kibana lens を試すために適当なデータを探していたら、政府統計(e-Stat) に行き着きました。
実際に Kibana lens で可視化するための道のりをまとめました。

## 動作環境
- Mac OS X 10.15.3
- Java 11.0.5
- Elasticsearch 7.6.0
- Kibana 7.6.0
- Logstash 7.6.0

## e-Stat API

## ユーザー登録 と アプリケーションIDの取得

私は[こちら](
https://qiita.com/kushikota/items/eed1d2b26e710fdc60f8#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E7%99%BB%E9%8C%B2-%E3%81%A8-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3id%E3%81%AE%E5%8F%96%E5%BE%97) を参考にアプリケーションIDを取得しました。

### データの選択と該当する REST API のリンク取得

今回は以下の統計データを選択しました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172174/0ae4026f-c7e5-c0b8-2f2d-a80605ac81c1.png)



上のアドレスの画面上の、APIボタンをクリックすると統計に対応するURLが表示されます。
今回はこちら
http://api.e-stat.go.jp/rest/2.1/app/getStatsData?appId=&lang=J&statsDataId=0003281493&metaGetFlg=Y&cntGetFlg=N&sectionHeaderFlg=1

- 今回は Kibana で可視化したいので、データは JSON で受け取るため、url に `/app/json/getStatsDatas?` を追加します。
- このappID=　のあとの部分にアプリケーションIDを設定します。


## Kibana で可視化するためのデータ抽出

```
# appid=の後の部分はご自身で取得します。
curl --location --request GET 'http://api.e-stat.go.jp/rest/2.1/app/json/getStatsData?appId=&lang=J&statsDataId=0003287753&metaGetFlg=Y&cntGetFlg=N&sectionHeaderFlg=1' > traffic.json
cat traffic.json | jq -c  .GET_STATS_DATA.STATISTICAL_DATA.DATA_INF.VALUE[] > data.json


```

## logstash でデータの加工


```
# cat estat.traffic.0003281493.conf
input {
  file {
      codec => "json"
      path => "/path/to/data.json"
      start_position => "beginning"
      sincedb_path => "/dev/null"
  }
}

filter {
    translate {
      field => "@tab"
      destination => "code"
      dictionary => {
          "1010" => "発生件数"
          "1060" => "負傷者数"
          "1270" => "死者数"
          "1360" => "人口10万人当たり死者数"
        }
    }

    mutate {
      remove_field => ["[@tab]"]
      rename => { "$" => "count" }
      gsub => ["@time", "(\d{4})(\d{6})", "\1-01-01T00:00:00.000Z"]
      convert => {"count" => "integer"}
      add_field => { "year" => "%{@time}" }
    }
}

output {
	elasticsearch {
           hosts => "localhost:9200"
           index => traffic

	}
}
```

```
$ cat run.sh 
rm -rf data
conf="/Users/surfer/elastic/labs/logstash/logstash.config/estat.traffic.0003281493.conf"
./bin/logstash -f $conf
$ 
```

## Kibana の画面

- index pattern (traffic*) 作成後に Lens を使用した際のサンプル画像
- 統計がある 1947 年から現在までの交通事故年間死亡者数のグラフを描画しました。
- 1968 年が歯抜けの理由は分からず。(ドキュメントは存在するのだが)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172174/bada840c-c770-a8bc-e69b-7514dd2dc60b.png)
CakePHPのCounterCache



## [CounterCache](https://book.cakephp.org/4/ja/orm/behaviors/counter-cache.html)とは

例えば

* 記事の情報をもつArticleテーブル
* 記事に対するコメントをもつCommentテーブル

がある場合、1つの記事に対するコメント数を自動で集計してくれる。


CounterCacheはbelongsToのモデルに使える。親テーブル側に`comment_count`のカラムを用意しておき、子テーブルのモデルにCounterCacheを追加する。

```php
class CommentsTable extends Table
{
    public function initialize(array $config)
    {
        $this->addBehavior('CounterCache', [
            'Articles' => ['comment_count']
        ]);
    }
}
```

## カウントする条件を指定する

削除フラグのデータをカウントから除外したい場合。

```php
$this->addBehavior('CounterCache', [
        'Articles' => [
            'comment_count' => [
                'conditions' => ['Comments.is_deleted' => false]
            ]
        ]
    ]);
```

## 複数のCounterCacheを持つ

記事に対するReactionテーブルが合った場合、それぞれのリアクションをカウントする。

```Reactions.php
$this->addBehavior('CounterCache', [
        'Articles' => [
            'like_count' => [
                'conditions' => ['Reactions.type' => 1]
            ],
            'read_count' => [
                'conditions' => ['Reactions.type' => 2]
            ],
        ]
    ]);
```


## 参考
* [[CakePHP]counterCacheがすごく便利なのでメモ](https://happyquality.com/2010/03/31/1127.htm)
* [【CakePHP3】CounterCacheを使ってhasManyで紐づくレコードの件数をキャッシュする](http://bashalog.c-brains.jp/16/03/29-143437.php)
AWS 認定ソリューションアーキテクトに合格した

# 受験動機 

- ありがちですがAWS summitに参加した時に「有資格者ラウンジ」というスペースがあるのを知って、資格取得すればあそこに入ってドヤ顔出来る！と思った
- 転職後自社サービス基盤としてAWSをそれなりに触っているので、以前よりはAWSサービスに対して知識が増えている事と、サービス連携での視野を広げておきたかった
- エンジニア的なキャリアプランとして、今後状況がどう転んでも資格取得は有利になると思った
- 年収アップの裏付けになるといいな([稼げるIT資格はどれ？](https://itjinzai-lab.jp/article/detail/1553))

# 使用した教材について（個人レビュー風）

1. 合格対策 AWS認定ソリューションアーキテクト - アソシエイト
    - 昔MSP会社に在籍してた時(2016)に購入。旧試験をターゲットとしているので扱っているサービスもやや少なく、サンプル問題の内容も易しめ。AWS入門用としてならいいかも
2. 徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書
    - クラウドプラクティショナーの受験で使用、SAA用としては深度が足りない感じ
3. この1冊で合格! AWS認定ソリューションアーキテクト - アソシエイト テキスト&問題集
    - 現時点(2019/10)でたぶん最新の参考書、試験範囲はほぼ網羅してる。サンプル問題は本番よりはやや簡単かも？言い回しが分かりづらいような問題が少ない
4. AWS WEB問題集で学習しよう
    - プラチナプランで利用。最初の方はわりと簡単だけど（旧問題基準？）途中から難易度が上がって、本番とほぼ同等レベル
    - 求められる構成内容に対してどのサービスを選択するか？ではなくて、複数の実現手法がある中でコスト優先/性能優先/運用優先でどれが適切か？を問われるパターンが多い。これは本番試験も同じ
    - 試験テキストとして言葉の言い回しや解釈などをきちんと読み取って判断しないと正答出来ない問題が多く、かなり実用的
    - ぱっと見て分からなくても、1と2の内容は明らかに違うから正解は3と4のどちらかになるはず…という消去法での答え方(試験テクニック)を身につけるにはとても有効
    - 正解回答の解説ページに紐づくサービスに対するドキュメントへのリンクがたいてい貼ってあるので、間違えた場合はすぐに参照できる


# 受験までの流れ
2019/05~

- ひとまず"徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書"を買う
- クラウドプラクティショナーというSAAより難易度の低い入門資格がある事を知る
- 6月のAWS summitまで一か月でSAA合格はちょっと敷居が高そうだったので、最初のステップとしてクラウドプラクティショナー合格を目指す
- 通勤時間でひたすら教科書を読む、業務で実際に触っているところ(EC2,VPC,RDSあたり)は大丈夫そう
- オンラインの模擬試験をやってみる。試験システムは本番と同じなので、操作を把握するためにも一度は受けた方が良い
- 5月末に受験、無事合格

2019/06~

- AWS summit参加。有資格者ラウンジは無事利用できたけど、クラウドプラクティショナーだとちょっと恥ずかしかった(w
- やっぱり最低でもSAA合格しないとAWSエンジニアは名乗れない

2019/07~

- しばらくだらけていた
- とある技術イベント参加で、やっぱり勉強しないと！と触発される
- 勢いでAWS WEB問題集を申し込む

2019/08~

- 通勤時間でたまにポチポチとWEB問題集をやるが、自分が使った事のないサービスに対する理解が浅い
- 問題数のボリュームがあるので、たまに少しやるぐらいだと中々先に進まない
- 本で読む方が自分は知識定着しやすいなーと思ったので、"この1冊で合格! AWS認定ソリューションアーキテクト - アソシエイト テキスト&問題集"を買う
- まずはこの教科書の範囲をしっかり抑える事にする

2019/09~

- ひたすら教科書を読んで概要を頭に入れる
- 中旬ぐらいでだいぶ理解できた（と思った）ので、WEB問題集を再開
- 自分は問題を早読みして誤回答する癖があるので（リージョンとAZを勘違いとか）本番試験では気を付けないといけない

2019/10~

- WEB問題集を二周ぐらいやってだいぶ自信が出てきたので、SAAを申し込む
- 通勤時間はずっとWEB問題集をやって、間違えたところはすぐに教科書で把握する
- 試験前日はWEB問題集を再度全通ししておく

# 受験当日

- CBTS歌舞伎座テストセンターで受験、タイミングよかったのか同日の受験者は少なめ
- 時間きっちりではなく、手続きが完了すればすぐに試験開始できた
- うっかりミス防止のために、問題は必ず二度読みしてから進めるようにした
- 持ち時間130分のうち、即答出来ない問題にフラグを立てつつ65問まで終えたところで残時間60分。そこからフラグ問題を再検討して、見直しx2を終えたところで残時間10分、ここで終了ボタンを押す

# 受験結果

- 無事合格(スコア881)
- 見直しが結構重要だった気がする…
- 見たことない内容でも消去法である程度絞り込める事が多い、後は運で！

# 要点

- 過去問題集的なものがないので難しい…と言われるけど、勉強の段階で似たような問題を読み解いておけば自然と応用が効くし、実力もつく
- 自分の勉強スタイル（続けやすく効率の良いやり方）を見つける
- 試験テクニックを習得しておく（消去法から解答を導くやり方はとても有効）


# 今後の受験予定

- 勉強する習慣（感覚）が薄れないうちに次はSysOpsを取りたい
- できればアソシエイトは全部クリアして三冠を目指したい
- プロフェッショナルは別にいいかな
Helm インストール

# 概要
 - k8sテスト環境構築<br>
<b>Helm インストール</b>

# 構築目次
 - [全体目次] (https://qiita.com/prodigy413/items/89fcb8cfb71e80f541d8)

# 環境
 - Rancher: v2.6.3
 - kubernetes(Client): v1.22.4
 - kubernetes(Server): v1.22.4
 - Helm: v3.7.2

# インストール
 - Helm Quickstart Page<br>
https://helm.sh/docs/intro/quickstart/

### Helm ダウンロード
 - 作業場所: ClientPC<br>
 - Helm Release Page<br>
https://github.com/helm/helm/releases

~~~
$ wget https://get.helm.sh/helm-v3.7.2-linux-amd64.tar.gz
$ tar xvf helm-v3.7.2-linux-amd64.tar.gz
$ sudo mv linux-amd64/helm /usr/local/bin/
$ helm version --short
v3.7.2+g663a896
~~~

### Repoの追加とアップデート

~~~
$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm repo update
$ helm search repo
NAME                                CHART VERSION	APP VERSION        DESCRIPTION                                       
stable/acs-engine-autoscaler        2.2.2        	2.1.1              DEPRECATED Scales worker nodes within agent pools 
stable/aerospike                    0.3.5        	v4.5.0.5           DEPRECATED A Helm chart for Aerospike in Kubern...
...............
~~~

### 動作確認
 - Helm Commands<br>
https://helm.sh/docs/helm/

~~~
### mysqlインストール
$ helm install bitnami/mysql --generate-name
NAME: mysql-1640762552
LAST DEPLOYED: Wed Dec 29 16:22:36 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mysql
CHART VERSION: 8.8.18
APP VERSION: 8.0.27

** Please be patient while the chart is being deployed **

..........
..........
..........

### 確認
$ helm list -n default
NAME            	NAMESPACE	REVISION	UPDATED                                	STATUS  	CHART       	APP VERSION
mysql-1640762552	default  	1       	2021-12-29 16:22:36.264348607 +0900 JST	deployed	mysql-8.8.18	8.0.27     

### 削除
helm uninstall mysql-1640762552 -n default
~~~
PythonのLogging

## ロガーの設定手順

1. アプリケーション全体で使う名前をつける.
2. ハンドラを設定.
3. （フィルタの設定）.
4. フォーマッタの設定.

### ハンドラ

* ログをどこに出力するか（コンソール？ファイル？）を決める.
* よく使うのは `StreamHandler` : コンソール、 `FileHandler` : ファイル
* 組み合わせて設定することも可能.

### フォーマッタ

* ログの表示形式を決める.

#### 時刻のフォーマットについて

日本時間に設定したい場合は、 `Formatter.converter` 属性をいじる必要がある。  
`Formatter.converter`は `time.struct_time` を返す callableなので、そのような適当な関数を定義しておく.

```python

def customTime(*args):
    return datetime.now(timezone('Asia/Tokyo')).timetuple()
```

※ `.timetuple()` で得られる struct_timeにタイムゾーン情報を含める方法ご存知でしたら、コメントいただきたいです。


## 設定例

```main.py

import logging
from pytz import timezone
from datetime import datetime

# loggerに命名する. この名前で呼び出すことで他のモジュールにも以下の設定が引き継がれる.
logger = logging.getLogger("example")
logger.setLevel(logging.DEBUG)
# コンソールに出力するハンドラの設定
sh = logging.StreamHandler()
sh.setLevel(logging.DEBUG)
def customTime(*args):
    return datetime.now(timezone('Asia/Tokyo')).timetuple()
formatter = logging.Formatter(
    fmt='%(levelname)s : %(asctime)s : %(message)s',
    datefmt="%Y-%m-%d %H:%M:%S %z"
)
formatter.converter = customTime
sh.setFormatter(formatter)
# ファイルに出力するハンドラの設定
fh = logging.FileHandler("logs/example.log")
fh.setLevel(logging.DEBUG)
def customTime(*args):
    return datetime.now(timezone('Asia/Tokyo')).timetuple()
formatter = logging.Formatter(
    fmt='%(levelname)s : %(asctime)s : %(message)s',
    datefmt="%Y-%m-%d %H:%M:%S %z"
)
formatter.converter = customTime
fh.setFormatter(formatter)
logger.addHandler(sh)
logger.addHandler(fh)
```
AutoMLTranslationAPIを使用して作ったモデルで自動翻訳

#はじめに
前回投稿したGoogle AutoMLの学習をしたモデルを使用して、ファイルの中身を翻訳して指定してファイルに出力してくれるようにしてみました！
たくさんの文章を翻訳して検証したり、翻訳時間を計測する仕組みを作るのも楽になりました:sunny:


#開発環境
・Windows10
・VScode
・Python3

#実装
**AutoMLのAPIを使用するのにサービスアカウントを取得する必要があります。**

```python:

from google.cloud import automl_v1beta1 as automl
import os

#model_id → AutoMLtranslationで作成したモデルのID
model_id =  '...'
#file_path → 翻訳したい文章のかいてあるファイルのファイルパス
file_path = '...'
#サービスアカウントの作成時にダウンロードしたサービスアカウントキーファイルのパスを設定
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "..."

automl_client = automl.AutoMlClient()
prediction_client = automl.PredictionServiceClient()

#file_pathに指定したファイルを開く
with open(file_path, "rb") as content_file:
    #ファイルの中身を１行ずつ読んで処理
    content = content_file.readline()
    while content:
        content.decode("utf-8")
        payload = {"text_snippet": {"content": content}}
        params = {}
        response = prediction_client.predict(model_id, payload, params)
        translated_content = response.payload[0].translation.translated_content
        #'...'に翻訳結果を入力したいファイルを指定。なければ作成されます。
        with open('...', mode='a',newline="",encoding='utf-8')as f:
            #指定したファイルを開いて翻訳結果を書き込む
            f.write(u"{}".format(translated_content.content))
        content = content_file.readline()
content.decode("utf-8")
```
##詰まったとこ
google.cloudがimportできないことがあって少し詰まったので解決方法をかいておきます
VScodeのsettingファイルに記入してあるpythonのpathと実際のpathが違うことによるエラーだったようです。

```terminal
which python
```
このコマンドを実行した後に出てくるpathがpythonのある場所なのでそのpathを./vscode/setting.jsonにあるpython.pythonPathに設定したところ、エラーが出なくなりました！

```./vscode/setting.json
{
  "python.pythonPath": "..."
}
```
#終わりに
APIにすぐしてくれるってとっても便利だなぁと感じました！
しばらくAutoMLのデータばっかり作っていたのでPythonを触るいい機会でした:blush:
APIにリクエストを投げるところと結果の取得のところはほとんどサンプルコードの引用ですが、
ファイルの内容を一文ずつ処理したりとか、翻訳結果を一つずつファイルに出力するところは自分で考えてコードを書いたので、楽しかったです:sunny:
シンプルなコードですが参考にしていただけると嬉しいです！

#参考文献
[外部モジュールをimportすると、「Unable to import 'module-name' pylint(import error)」と表示される](https://qiita.com/dynamonda/items/5a8129cd6e9cc139d94a)
[AutoML Translation ドキュメント](https://cloud.google.com/translate/automl/docs)
TypeORMのsimple-arrayでnumber配列を扱う | Use number array as actual value of TypeORM's "simple-array" typed entity columns

TypeORMはTypeScriptで使えるORM。

すごくニッチなニーズな気もするが、`simple-array`というTypeORM特有の型をコード中で扱う際に、numberの配列として扱いたい。

# simple-array
[doc](https://github.com/typeorm/typeorm/blob/master/docs/entities.md#simple-array-column-type)

配列をDBに手軽にストアしたいなら便利。ただしDocに明記されていない問題が一つ。

<strong>string要素以外格納できない。</strong>

より厳密に言うと、string以外の要素を含んだ配列を渡すと、DBから値を取得する際にstringとして返ってくる。

ex) [1, 2, 3] -> DB -> ['1', '2', '3']

```typescript

@Column({ type: 'simple-array' })
public elements: number[]
```

のように定義していても上記のような動作をするので、<strong>普通に嫌だ</strong>。

https://github.com/typeorm/typeorm/issues/1092
https://github.com/typeorm/typeorm/issues/2267

この辺を見ても分かるが、TypeORM側としてはライブラリサポートする気はなさそう。

# どうするか
これで解決できた。

```typescript

@Column({ type: 'simple-array', nullable: true })
public elements: number[]

/**
 * @NOTE: simple-array type automatically save column as string.
 * numifyElements will fix string elements to number after each time entity is loaded.
*/
@AfterLoad()
numifyElements() {
  if (this.elements&& this.elements.length) {
    this.elements.forEach((e, i, self) => self[i] = parseInt(String(e), 10))
  }
}
```

`@AfterLoad`デコレータがついた関数を定義しておくと、Entityがロードされる度に所定の処理を行うことができる。
`simple-array`は少なくとも配列の状態でロードしてくれるので、その要素を自分が求める型に変換する。

カラム定義は`number[]`にしておかないとコード内で型について怒られる。
ただし、そうすることによって`numifyElements`内の`parseInt(String(id), 10)`でわざわざidをStringで囲う必要が出てくる。

なぜなら既に`number`と定義されているものをparseIntに渡すので、tsに怒られてしまうから。

そもそもnumber[]と定義しているにも関わらず、なぜstring[]を入れることができているのかについてはよくわからない。

そのうちちゃんと調べよう。
みずほ銀行の為替のヒストリカルデータダウンロード→エクセルに集計まで　ほぼ1クリックで

#自動化したい
当方、経理職として戦っているのですが、毎日のドルレートを活用して海外送金取引等の仕訳に利用しているわけなのですが(´･ω･`)毎日毎日、みずほ銀行からヒストリカルデータのCSVをダウンロードしてエクセルに張り付けているのであります。そこでこんなルーチンワークこそ自動化の出番だということで作ったのが今回のコーディングです。

#以下コード
```python:qiita.rb
from selenium import webdriver
import time
import pathlib
import csv
import datetime
import openpyxl


def download_csv(save_dir):
    profile=webdriver.FirefoxProfile()
    profile.set_preference("browser.donwload.folderlist",2)
    profile.set_preference("browser.download.manager.sjpwmwjemStarting",False)
    profile.set_preference("browser.download.dir", save_dir)
    profile.set_preference("browser.helperApps.neverASK.savedTodisk","text/csv")

    driver=webdriver.Firefox(firefox_profile=profile)
    driver.get("https://www.mizuhobank.co.jp/market/historical.html")
    time.sleep(1)
    driver.find_element_by_css_selector("#mainCol > article > div > div.section > div.inner > table > tbody > tr:nth-child(2) > th > span > a").click()


def open_csv_to_excel():
    path=pathlib.Path(r"C:\Users\name\Downloads")
    for pass_obj in path.iterdir():
        
        if pass_obj.match("quote.csv"):
            
            with open(pass_obj)as x:
                reader=csv.reader(x)
                
                for row in reader:
                    if row[0]==today:
                        print(row[1])
                        wb=openpyxl.load_workbook(r"C:\Users\name\Documents\excel_python\data\レート表.xlsx")
                        sh=wb.active
                        
                        for dt_row in range(2,sh.max_row+1): #(2行から最終行まで繰り返し　変数dt_row)
                            　
                            for dt_column in range(2,sh.max_column+1):　#(2列から最終列まで繰り返し　変数dt_column)
                                
                                if sh.cell(dt_row,dt_column).value != None: #datetime.datetime形式を文字列(str型)に変換
                                    date=sh.cell(dt_row,dt_column).value    
                                    date_sec="{}/{}/{}".format(date.year,date.month,date.day) 
                                    
                                    if date_sec==today:　　　　
                                        sh.cell(dt_row,dt_column+1).value=row[1]                            

                                        wb.save(r"C:\Users\name\Documents\excel_python\data\レート表.xlsx")
                               
                   
now=datetime.datetime.today()
today="{}/{}/{}".format(now.year,now.month,now.day)
download_csv(r"C:\Users\name\Downloads\為替レート表")
time.sleep(8)
open_csv_to_excel()

```
内容としてはseleniumを活用してみずほ銀行のヒストリカルデータをダウンロード、その間8秒間はスリープをかけて保存しきるのを待ってから、続いてエクセルに集計するという段取りです。最新の日付を見やすいように所定の文字列型に直すのに一工夫いりました。

#職場で試してみて
まぁ、環境構築に時間が結構取られてしまいました。firefoxの準備とブラウザ操作のドライバですね。これを別のPCに入れ込むというのを考えると改めてエクセルのVBAの環境構築不要な面は魅力的だなと感じる次第です。あと、職場のPCは性能が悪いので処理に30秒くらいかかりますね。(;´Д｀)何でわざわざダウンロードしてエクセルに集計するかというと帳票(エビデンス)として残すためであります。つまりのこの操作で帳票づくりもやってしまおうという魂胆です。
[React]Hooks を使って window.innerWidth からブレイクポイントを計算する

最近 React の勉強をしているんですが、Hooks の使い方と Custom Hook の作り方を身につけるためにいろいろサンプルを作っていくことにしたので、それを上げていきます。

今回はブラウザの横幅からブレイクポイントを設定するサンプルです。

デモは [こちら](https://curiosity-driven.firebaseapp.com/tools/use-breakpoint-hook)。
※ ブラウザの横幅を変えると表示が変わります。

```
react: 16.13.0
react-router-dom: 5.1.2
lodash: 4.17.15
typescript: 3.7.5
```

## useBreakpoint
```javascript
import { useEffect, useState } from 'react';
import lodash from 'lodash';

// ブレイクポイントを定義。Material UI を使っているので、それに合わせて作成
// https://material-ui.com/customization/breakpoints/
const getBreakpoint = (width: number): string => {
  if (width < 600) {
    return 'xs';
  }
  if (width < 960) {
    return 'sm';
  }
  if (width < 1280) {
    return 'md';
  }
  if (width < 1920) {
    return 'lg';
  }

  return 'xl';
};

const useBreakpoint = () => {
  const [innerWidth, setInnerWidth] = useState(window.innerWidth);
  // ブレイクポイントを設定
  const [breakpoint, setBreakpoint] = useState(
    getBreakpoint(window.innerWidth),
  );

  useEffect(() => {
    // resize の度に処理しないように throttle で間引く
    const calcInnerWidth = lodash.throttle(() => {
      setBreakpoint(getBreakpoint(window.innerWidth));
      setInnerWidth(window.innerWidth);
    }, 200);

    window.addEventListener('resize', calcInnerWidth);

    // アンマウント時にクリーンアップ
    return () => window.removeEventListener('resize', calcInnerWidth);
  }, []);

  return { innerWidth, breakpoint };
};

export default useBreakpoint;
```

## useBreakpoint を使ったコンポーネント
```jsx
import React, { FC } from 'react';
import useBreakpoint from 'hooks/use-breakpoint';

const UseBreakpointHook: FC = () => {
  const { innerWidth, breakpoint } = useBreakpoint();

  return (
    <>
      <div>width: {innerWidth}</div>
      <div>breakpoint: {breakpoint}</div>
    </>
  );
};

export default UseBreakpointHook;
```

これでブラウザの横幅を動かすと breakpoint と width が適宜変わっていく Custom Hook を実装できました。
Laravelでライブラリを入れる際にバージョン違いで怒られた話

# 困ったこと
* 下記のようにlaravel/uiというライブラリインストール時に、laravel/frameworkのバージョン違いでインストール失敗  

<img width="1167" alt="スクリーンショット 2020-03-04 19.10.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/161180/104e7154-c271-23f2-aa67-65bfd146cc0f.png">

# 問題の背景
* プロジェクト作成時はLaravel6で開発をはじめたが、開発中にLaravel7がリリースされていた。  
※2020年3月3日にLaravel7がリリースされていた

# 解消方法
* 自身の利用しているlaravel/frameworkへ対応しているライブラリのバージョンを指定してインストールする  
```$ composer require laravel/ui:1.2```

# 参照文献
* [composer requireがInstallation failedするけどどうしてもrequireしたいんだ](https://qiita.com/engineerYodaka/items/46cf6419825732c14528)
* [Laravel7リリースノート](https://laravel-news.com/laravel7)
本日の精進：動的計画法(DP)

D問題のDはDPのD!!!

[Educational DP Contest](https://atcoder.jp/contests/dp)
A問題:Frog 1
スタートからゴールまで、高さのある足場を辿っていく。このとき、
・足場iからjへ行くには足場iとjの高さの差分の絶対値だけコストがかかる。
・足場iからはi+1,i+2に移動できる。
という条件下でコストを最小化せよ、という問題。
「スタート→ゴールのコストを最小化する」=「足場i:i=0,...,n-1　へ到達するコストを最小化する」と言い換えられる。

典型的なDPの問題であり、以下の遷移式でdp[i]:0=1,...,n-1を計算する。
dp[i] = min(　dp[i-2]+cost(i,i-2),　　dp[i-1]+cost(i,i-1)　)
なお、初期状態として、
・dp[0]=0(最初から足場0に居るので、コスト0)
・dp[1]=cost(0,1)(足場1には足場0からしか移動できないので確定)
を与えましょう。

```AC.cpp
#include <bits/stdc++.h> //library
#define rep(i, n) for(int i = 0; i < (int)(n); i++) //for-loop (from 0)

using namespace std;
using ll = long long; //long long
ll M = 1000000000 + 7;

int main(void) {
  int n; cin >> n;
  vector<int> h(n);
  rep(i,n) cin >> h[i];
  
  vector<int> dp(n);
  dp[0] = 0; dp[1] = abs(h[0]-h[1]);
  for (int i = 2; i < n; i++){
    dp[i] = min(dp[i-2]+abs(h[i-2]-h[i]),dp[i-1]+abs(h[i-1]-h[i]));
  }
  
  cout << dp[n-1] << endl;
  return 0;
}

```

B問題:Frog 2
上記の問題の発展形。先ほどは移動の種類が「i+1,i+2」の2種類だったが、本問題では「i+j:j=1,...,K」となっている。Kは100以下の自然数。
Kの制約が緩いため、Kに関して全パターン計算し、最小のものを選べばよい。

```AC.cpp
#include <bits/stdc++.h> //library
#define rep(i, n) for(int i = 0; i < (int)(n); i++) //for-loop (from 0)

using namespace std;
using ll = long long; //long long
ll M = 1000000000 + 7;


int main(void) {
  int n,k; cin >> n >> k;
  vector<int> h(n);
  rep(i,n) cin >> h[i];
  
  vector<int> dp(n);
  dp[0] = 0; dp[1] = abs(h[0]-h[1]);
  for (int i = 2; i < n; i++){
    dp[i] = M;
    for (int j = 1; j <= k; j++){
      if (i-j < 0) break;
      dp[i] = min(dp[i],dp[i-j]+abs(h[i-j]-h[i]));
    }
  }
  cout << dp[n-1] << endl;
  return 0;
}

```

dpの初期化に注意。上記ACコードでは10^9+7で初期化している。しかし実際には、最小コストがこれを超えるような入力に対して正常に動作しないため、あまり良いコードではない。
なので、dp配列をlonglong型で宣言し、初期値としてINF=10^50とかを与えるべき。こうすれば、よほどでない限り上記のバグは発生しない。
USキーボードのCAPSLockを無効化するfor win10

https://winaero.com/blog/disable-caps-lock-key-windows-10/

この記事に沿ってやります。残念ながらJISキーボードと違ってレジストリを書き換えずにやる方法は無さそう。自己責任でお願いいたします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106327/474863d1-620a-f0c5-7618-5daa734645d8.png)


レジストリエディタを開きます

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout

と階層を掘り進めます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106327/a52c623f-f124-a22b-0a4d-29a586a3b219.png)

右クリック、新規でバイナリ値を選択します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106327/f0550cf2-1658-da65-6006-278228573a13.png)

こんな感じの値を入れます

```
00,00,00,00,00,00,00,00,02,00,00,00,00,00,3a,00,00,00,00,00
```

3Aは3をおしてからAで入れれます

後は再起動

## その他の方法

物理的に外します

![IMG_20200304_192318.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106327/dce8a015-95aa-8df3-6f5a-0a2e9b738cdd.jpeg)
今度はTechAcademy!

Tech Expert挫折してはや...５ヶ月？！:sweat:
だいぶ間があいてしまいましたが仕切り直して:laughing:
今度はTechAcademyで挑戦します！！
しかもセットセールしていたので調子に乗って３コース申し込んでしまいました:upside_down:
<ul>
<li>PHP/Laravelコース
<li>フロントエンドコース
<li>Webデザインコース
</ul>

響きはいいけど、大変そw:see_no_evil::hear_no_evil::speak_no_evil:（まだ実感なし:zipper_mouth:）

実はすでに開始日を遅らせており2/25スタート予定だったが...はたまた4月初めに変更:sweat:
3月中旬にアメリカに戻る為、実家の荷物も断捨離&箱詰め作業をせねば。。。何より今現在、ぎっくり腰中。。。
空いた時間で少しずつ予習していくつもりが...一週間なんてあっという間:frowning2:
(こういう時間の流れ方こわぃ...:confounded:)

が、今回はやり通さなければ:fire:（払い戻し不可:scream:）

そして今、アウトプットツールを整理中。。。（Qiita/Twitter/はてなブログ）
Tech Expertを挫折してからどれも触っていなかったため、使い方含め、若干振り出しに戻る:tired_face: