OmniAICreator/Qiita-1.07M huggingface dataset:
DSP.jlでspectrogramを計算する

メモ書き。
基本的にはリファレンスを見たほうが良い。
https://juliadsp.github.io/DSP.jl/stable/periodograms/

# 音の作成
CureMIDI.jlとMIDI.jlを使います。

以下のgenerateを使ってTrainingDataを生成します。
このTrainingData.soundに対してスペクトログラムを計算します。

```julia
struct TrainingData
    track::MIDI.MIDITrack
    sound::SampledSignals.SampleBuf
end


function generate(sf2_paths::Vector{String}, sample_num_per_sf2::Int)
    training_dataset = Vector{TrainingData}()
    Random.seed!(0)
    for sf2_path in sf2_paths
        for i in 1:sample_num_per_sf2
            track = create_rand_midi_track()
            sampled_buf = synth(track, TPQ, BPM, SAMPLE_RATE, sf2_path)
            training_data = TrainingData(track, sampled_buf)
            push!(training_dataset, training_data)
        end
    end
    return training_dataset
end

function create_rand_midi_track()
    track = MIDI.MIDITrack()
    notes = MIDI.Notes()
    start_pitch = 69

    max_tick = 0
    for i in 1:10
        # pitch, velocity, position, duration
        pitch = start_pitch  + 2 * i
        velocity = 70
        start_ms = 1000 * (i - 1)
        start_tick = UInt(ceil(CureMIDI.ms_to_tick(start_ms, TPQ, BPM)))
        duration_tick = UInt(ceil(CureMIDI.ms_to_tick(1000, TPQ, BPM)))

        max_tick = max(max_tick, start_tick + duration_tick)
        note = MIDI.Note(pitch, velocity, start_tick, duration_tick)
        push!(notes, note)
    end

    addnotes!(track, notes)
    return track
end
```

# spectrogramの計算
ステレオで音を生成するのでsoundにはleft、rightのchannel(2)xframe数の二次元配列で格納されている。
left

```julia

function spectrogram(sample_buf::SampledSignals.SampleBuf)
    samplerate = SampledSignals.samplerate(sample_buf)

    spectrograms = Vector()
    for ch in 1:nchannels(sample_buf)
        input = sample_buf.data[:, ch]

        # n 8部音符ごと
        tick_per_eighteen_note::UInt = ceil(TPQ / 2)
        frame_per_eighteen_node = CureMIDI.tick_to_frame(tick_per_eighteen_note, TPQ, BPM, SAMPLE_RATE)
        n::Int64 = ceil(length(input) / frame_per_eighteen_node)
        spectrogram = DSP.spectrogram(input, n, nfft=n*2, fs=samplerate)

        push!(spectrograms, spectrogram)
    end
    return spectrograms
end

spectrograms = spectrogram(training_dataset[1].sound)

p = power(spectrograms[1])
fs = freq(spectrograms[1])
ts = time(spectrograms[1])
```
# 計算結果
## power
周波数の強さ。
STFTの結果の二乗の値が入る。

## freq
周波数は量子化されている。
量子化された周波数の真ん中の値
周波数の上限はナイキスト周波数を考慮し、入力の1/2。

## time
時間も量子化されている。
量子化された時間の真ん中の値。
上の例だと単位は秒。
ビジュアルスタジオコードの便利な拡張

こんにちは。
最近、ビジュアルスタジオコードというほぼ毎日使っています。ビジュアルスタジオコードは使いやすいテキストエディタで、無料で簡単にインストールできます。

VSCもいろいろな拡張があります！

ダウンロードリンク: https://code.visualstudio.com/download

#拡張とは？
コンピュータオペレーティングシステムにおける拡張は、プログラミング言語または他のアプリの機能を強化と拡張するソフトウェアです。 拡張は、スタンドアロンアプリケーションに機能を追加します。 通常、拡張自体は自主的な機能するわけではなく、機能するための他のソフトウェアが必要です。

使いやすい拡張をいくつか紹介したいと思います。

#使い方
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326049/588ddfca-a376-4731-dd8e-43bd14ae99d9.png)

VS Code Marketplaceで最も使用されている拡張のリストが表示されます。
拡張はいつでも無効にする、有効にする、アンインストールにするの設定があります。

画面の左側をクリックするか、ショートカットコマンド (Ctrl+Shift+X) を使用して、拡張リストを開きます。

一人一人な拡張機能にはプレビューあります。そのプレビューに簡単な説明、発行元、ダウンロード数、評価があります。

## Prettier
https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

- コードフォーマッタ
- Prettierはコードの字下げを首尾一貫させます。他の人のコードを読んでいるとき、字下げの特徴がコードを読みやすく理解しやすくなられます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326049/964014ae-6507-b668-004d-7a05cc7ab2c1.png)

## GitLens
https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens

- この拡張はGitに接続することでリポジトリで何が起こっているのかとか誰が最新のコミットしたかとかを視覚化することができます。
- この拡張は各コード行の最新のコミットを表示します。
- リポジトリの管理が容易になります。
- ビジュアルスタジオコードでリポジトリの最新のコミットが自動的に見せられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326049/b4403fb5-58f3-c612-e67a-1b94a523c666.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326049/eb6bebad-80fd-258a-5a8e-bfe83d88577f.png)


## Path Intellisense
https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense

- 入力中に自動的にファイルのパスが表示されます。
- 一般的にプログラミングでファイルのパスを挿入します。
- ファイル名を自動補完することで開発時間を短縮します。

![iaHeUiDeTUZuo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326049/8caa5ae5-44b8-249a-f96a-c5f2c3272903.gif)


## Auto Close Tag
https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag

- 開始タグの閉じ括弧を入力した後、終了タグが自動的に挿入されます。

![alt](https://raw.githubusercontent.com/formulahendry/vscode-auto-close-tag/master/images/usage.gif)

## Auto Rename Tag
https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag

- 開始タグの名前を変更しながら、終了タグは自動的に名前変更されます。

![alt](https://raw.githubusercontent.com/formulahendry/vscode-auto-rename-tag/master/images/usage.gif)

## Increment 
https://marketplace.visualstudio.com/items?itemName=albymor.increment-selection

- Win\Linux:  ctrl + alt + I
- Mac: cmd + alt + I
- この拡張はテストデータに関しては役に立ちます。シードデータを作成する時間を短縮するのに役立ちます。

![alt](https://raw.githubusercontent.com/albymor/Increment-Selection/master/images/demo.gif)

#以上
この拡張は初心者対に限らなくてみんなが使ってみてください。他の拡張たくさんもあります。
もっと便利なコーディングするのを助けることができるすべての拡張をインストールすることができます。他の拡張を使ってみたいで、後にみんなでシェアしたいと思います。

読んでくれてありがとうございました！

ソース: 
- https://marketplace.visualstudio.com/
- https://www.techopedia.com/definition/1828/extension
Macで作ったLaravel5.8+php:fpm-7.3+nginxの環境をCloud9上でも動かす

# 概要

DockerやPHPの勉強のために最低限のLaravelの環境を作成したので、
せっかくならクラウドで開発したいなと思い4月に東京リージョンで使用できるようになった
awsのcloud9上で動かせるようにしてみました。

| 使用技術 |
|:--|
| nginx |
| php:fpm-7.3 |
| Laravel5.8 |
| mysql5.7 |

## フォルダ構成

```
.
├── mysql
│   ├── Dockerfile
│   └── my.cnf
├── nginx
│   └── default.conf
├── php
│   └── Dockerfile
├── docker-compose.yml
└── src (laravel project)
```
サンプルコード
(github): https://github.com/enuswi/laravel_cloud9.git
＊サンプルコードが動かなくなっていたので改めてgithubに作成し直しました。(2019.07.03)

## cloud9の環境作成

awsコンソールからcloud9を選択し「create environment」で環境を構築します。
設定する項目も多くなくほぼそのままでいけると思います。
なお今回は、PlatformのみデフォルトのAmazon LinuxではなくUbuntu Serverを選択しています。
確認画面で「create」をクリックして数分で、cloud9が立ち上がると思います。

### プロジェクトのセッティング

「Welcome」というタブの隣の「＋」ボタンをクリックし
New Terminalを選択するとTerminalが立ち上がるのでそこでセッティングを進めていきます。

#### dockerコンテナの立ち上げまで

``` 
ソースコードのclone
$ git clone https://github.com/enuswi/laravel_cloud9.git

.envファイルを先に作成しておきます
$ cp -p src/.env.example src/.env

docker-composeのインストール
(cloud9には元々、dockerはインストール済みですが、docker-composeはインストールされていないのでインストールする必要があります。)
$ sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

docker-composeの権限変更
$ sudo chmod +x /usr/local/bin/docker-compose

cloneしてきたプロジェクトのルートディレクトリに移動し、dockerコンテナを作成していきます
$ cd laravel-cloud9

$ docker-compose build --no-cache

$ docker-compose up -d
```

#### コンテナ内での作業

```
コンテナが立ち上がったら、コンテナ内に入ります。
$ docker exec -it {container id} bash

(コンテナ内)
$ composer install

$ php artisan key:generate
```

「Preview > Preview Running Application」を選択すると
(おそらく)ターミナルの横にwindowが開きLaravelおなじみの画面が表示されるはずです。

# 終わりに

当初はプロジェクトの作成含め、cloud9上で全て行おうと考えていたのですが、
t2microインスタンスだとメモリが足りずプロジェクト作成時にエラーが出たり、
dockerコンテナ上でswaponコマンドが使えず、swapファイルが作れなかったりで一旦、既存プロジェクトを動かすに留めました。
この辺をもう少しちゃんと調べるとともに、cloud9のことももう少しちゃんと調べてみたいなと思います。
＊ちなみにt2small(メモリ2GB)で試しにやったらエラーも出ずプロジェクト作成ができました。

```
$ composer create-project --prefer-dist laravel/laravel src
・・・
The following exception is caused by a lack of memory or swap, or not having swap configured
Check https://getcomposer.org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details

                                                     
  [ErrorException]                                   
  proc_open(): fork failed - Cannot allocate memory  
```
Pythonでソートアルゴリズムの再発明をしてみる: シェルソート

# 概要
* Pythonでソートアルゴリズムを再発明してみるだけ
* 今回はシェルソートについて
* 参考サイトのサンプルコードをできるだけ見ずに実装する
* とりあえず昇順ソートのみの対応とする

# シェルソートの概要
* [挿入ソート](https://qiita.com/dhirabayashi/items/66d10a52ea89d3e141d0)の改良版
* 離れた要素同士で比較、挿入を行うことで「だいたいソートされた状態」にし、その後挿入ソートと同等の処理を行って「完全にソートされた状態」にするみたいな感じ
  * [コムソート](https://qiita.com/dhirabayashi/items/77ad4d659a6664b48a21)と同じアイデア
* データ数が多い場合は高速らしい
* 不安定ソート

## 基本的な手順
1. ソート対象のリストのサイズをもとにギャップ（比較する要素同士の距離）を決める（2回目以降は前回のギャップをもとにする）
2. 左端の要素を「ソート済み」とみなす（2回目以降は、左端からn+1離れた要素を「ソート済み」とする）
3. 「ソート済み」要素からギャップ分離れた要素を「未ソート」とみなす
4. ソート済み要素（の右端）と未ソート要素を比較し、大小関係が逆なら、未ソート要素をソート済み要素の中の適切な位置に挿入して「ソート済み」要素とする。大小関係が合っていれば、その位置のまま「ソート済み」要素とする
5. 比較する要素両方をギャップ分右にずらしながら手順3〜4を繰り返す
6. ギャップ分離れた要素が範囲外になるところまできたら2に戻る（最初の「ソート済み」要素が1つ右にずれる）
7. さらにまたギャップ分離れた要素が範囲外になるところまできたら1に戻る（ギャップが小さくなる）
8. ギャップが1まで減ったら、挿入ソートと同等の処理を行う。右端まで処理が終わったらソート完了

# ソース
```python
import random

def shell_sort(lst):
    # ギャップ計算のための初期値
    gap = len(lst)
    # ループ継続のフラグ
    # ギャップが1の場合の処理が終わったら処理全体を終わらせるために使う
    is_continue = True
    # ギャップを変えていくためのループ
    while gap > 0 and is_continue:
        gap = (gap - 1) // 3
        if gap == 0:
            # ギャップが1の場合の処理（挿入ソートと同じ）は絶対にやらないといけない
            # そしてそれが終われば処理全体が終わり
            gap = 1
            is_continue = False

        # 「ソート済み」要素を変えていくためのループ
        # 「ソート済み」といってもコード上ではその概念は登場せず、ループカウンタのある時点の値より前が「ソート済み」ということになる
        for i in range(0, len(lst)):
            j = i
            # 配列を走査するためのループ
            while j + gap < len(lst):
                # 大小関係が逆なので入れ替えが必要
                if lst[j] > lst[j+gap]:
                    # 挿入位置を探すためのループ
                    for k in range(i, j+gap, gap):
                        if lst[k] > lst[j+gap]:
                            lst.insert(k, lst[j+gap])
                            del lst[j+gap+1]

                j += gap
            


lst = list(range(10))
random.shuffle(lst)

print("ソート前: " + str(lst))

shell_sort(lst)

print("ソート後: " + str(lst))
```

# 参考サイト
* https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB%E3%82%BD%E3%83%BC%E3%83%88
適当なHooks入門【useState編】

##概要
Reactで開発するにあたってHooksちゅう新しい機能を使うので、公式ドキュメントで勉強したことを投稿していこうと思います。

[公式ドキュメント](https://ja.reactjs.org/docs/hooks-intro.html)

##Hooksとは？
> state などの React の機能を、クラスを書かずに使えるようになります。

つまり関数コンポーネントで従来のクラスのように、Stateの管理やらライフサイクルっぽいことができる機能みたいです。

##ステートフックをやってみる
まずはStateの管理をええ感じにしてくれる、`useState`ちゅうやつからやってみます。

~~~JSX
import React, { useState } from 'react';

// stateの初期値
const initialState = 0;

function Example() {
  // 呼び出し
  const [count, setCount] = useState(initialState);

  return (
    <div>
      <p>clicked {count} </p>
      <button onClick={() => setCount(count + 1)}>
        Click
      </button>
    </div>
  );
}
~~~

>useState は現在の stateの値と、それを更新するための関数をペアにして返します。

つまり`useState`は引数の値のGetterとSetterを生成してくれる便利関数みたいな感じなんかな？
ちなみに`useState`の引数(state)はオブジェクトでなくてもええみたいです。

~~~JSX
function Example() {
  // どっちもOK
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({name: "くいだおれ太郎"});

  // stateの更新
  const updateState = () => {
    setCount(1);
    setUser({name: "ビリケンさん"});
  }

  return (
    <div>
      <p>count: {count} </p>
      <p>user: {user.name} </p>
      <button onClick={() => updateState()}>
        UPDATE
      </button>
    </div>
  );
}
~~~

##フックのルール

>・ フックは関数のトップレベルのみで呼び出してください。ループや条件分岐やネストした関数の中でフックを呼び出さないでください。

>・ フックは React の関数コンポーネントの内部のみで呼び出してください。通常の JavaScript 関数内では呼び出さないでください（ただしフックを呼び出していい場所がもう 1 カ所だけあります — 自分のカスタムフックの中です。これについてはすぐ後で学びます）。

つまり関数コンポーネントのいっちゃん上で使ってね。と言うことみたいや

##ざっくりまとめ
1. `useState`は引数にとったstateの値と、それを更新するための関数を返す便利な関数なんやで
2. 引数にとるstateはオブジェクト出なくてもええんやで
3. 関数コンポーネントの内部のみ、しかもいっちゃん上でつこてや

##おわりに
初投稿で文章も内容も適当ですが、こんな感じで投稿を続けていこうと思います。
Selenium@Python

# Selenium@Python
- Pythonインストール
- Seleniumインストール
- Chromeのwebdriverダウンロード

#### ログインしてCookiesを取得
```python:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

# chromedriverの設定
driver = webdriver.Chrome('./chromedriver_win32/chromedriver')

# ログイン
driver.get('https://server/login/')
driver.find_element_by_id('userid').send_keys('user@example.com')
driver.find_element_by_id('password').send_keys('password')
driver.find_element_by_id('password').submit()

# Cookies取得
cookies = driver.get_cookies()
for c in cookies:
    # 特定のクッキーを抽出
    if c["name"] == "secure_token":
        print(c["value"])
        break

driver.quit()
```

#### ヘッドレスブラウザで実行
```python:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# chromedriverの設定
options = Options()
# ヘッドレス設定
options.add_argument('--headless')
driver = webdriver.Chrome('./chromedriver_win32/chromedriver', chrome_options=options)

(以下略)
```
php コマンドラインで OutOfMemoryが出るとき | memory_limit 無制限

# 要点

```php
php -d memory_limit=-1 example.php
```

をパラメータで渡す。以上。

`composer require` が重くて無理だったときに。
健忘録。
何回もググるから、タイトル修正した。


-1は無制限で下手したら止まるから、よいこのみんなは絶対にマネをしないでね


## その他

### php.iniの場所

```php
php -i | grep php.ini
```

### 設定確認

```php
php -i | grep memory_limit
```
低コストでAWSのサービスを使ってWebサイトの公開をやってみた(Route53,Lightsail)

# 概要
AWSのサーバで有名なのがEC2だが、正直個人で使うにはコストが高いと思い、
何かいい方法がないかなと思い、調べたところLightsailというサービスに辿りついた。
Lightsailは一番安いのが約500円でIPアドレスも使えるので、WEB公開にはもってこいだと思い実際に構築してみた
# 使用環境
AWS Lightsail(webサーバ　OS:ubuntu)
AWS Route53(DNSサーバ)

#手順内容
1,Lightsailにアクセスし、インスタンスを作成
　詳しくは割愛しますが、インスタンス作成時にOSの選択ができますし
　wordpressなども選択できます。
<img width="1214" alt="スクリーンショット 2019-07-02 23.18.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/898f71da-cac5-b1d3-aad4-41d5238435fe.png">

2,作成したインスタンスにIPアドレスを割り当てる
　Lightsailでは作成したインスタンスにIPアドレスを無料で割り当てることができます。
　詳しい手順は検索をかければ出てくるので調べてみてください

3,ファイウォールの設定
　インスタンスの作成が終わった状態だと、SSHの通信しか許可されていないので
　HTTPの許可もします。(ホーム> 作成したインスタンス > ネットワーキングにファイアウォールの設定があります。)
<img width="1214" alt="スクリーンショット 2019-07-02 23.26.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/7309e1ec-7c46-df40-bf58-28ae9cf58551.png">

以上でサーバの作成と設定が完了しました。
次にRoute53の設定をします。
※ドメインは取得している前提で進めます。

4,Route53にアクセスし、ホストゾーンの作成
　Route53はDNSサーバであり、ホストゾーンとはゾーンファイルのようなものです。
　bindなどとは違い、GUIで操作ができるためやりやすい印象を受けました。
　ドメイン名には取得したドメインを入力します。
<img width="471" alt="スクリーンショット 2019-07-02 23.34.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/547b30f2-0e14-c283-1044-6fc6532b5ab7.png">

5,NSレコードを確認し、取得したドメインに関連づける
　ドメインを取得した時ネームサーバを入力する項目があると思いますが、そこに先ほど作成した
　ホストゾーンのNSレコードの内容をコピペします。

　下記参照
　[はじめてのAmazon Route 53](https://qiita.com/Yuki_BB3/items/effdf1bb38263bfef82a)

6,Aレコードを追加
　レコードセット作成をクリックし、Aレコードを作成する。
　値にはLightsailのIPアドレスを入力する。
<img width="428" alt="スクリーンショット 2019-07-02 23.47.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/9cea02f7-ed70-495c-04c7-0e0b5aa0e0bc.png">

7, 動作確認
　URLを入力しちゃんとページが表示されることを確認する。
　(自分はLaravel環境の構築をしました)
<img width="1327" alt="スクリーンショット 2019-07-02 23.56.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146391/9f58cfc9-c874-850e-c371-81ce6f83417f.png">

※TTLの値が大きすぎると反映に時間がかかるため調整する。

#まとめ
料金は500~700円ほどで簡単にWeb公開ができるようになりました。(さすがクラウド！！)
実は他にもサブドメインを利用したVirtualHostの機能の実装なども
しており、時間があれば書いていきたいと思います。

以上
Firebase Authで新規登録させない要件に対応する方法

普通にウェブアプリを（BaaS使わずにRailsとかで）作る際に管理画面側のユーザー登録を直接的に行えないようになっていたりすることがあると思います（MVPで作っているものとか。とりあえず自分はあった）。

Firebase Authには新規登録を直接かつ簡便に制限する方法はないようなので、実質的に（新規登録させないのと）同じようなことを実現したい場合には下記のような感じで[Firestore security rule](https://firebase.google.com/docs/firestore/security/rules-conditions?hl=ja)で諸々のデータへのアクセスを制限する形で対応するのが良さそうです。


```
service cloud.firestore {
  match /databases/{database}/documents {
    function isAdmin() {
      return request.auth.uid != null && exists(/databases/$(database)/documents/admins/$(request.auth.uid));
    }

    match /hoges/{hogeId} {
      allow read, write: if isAdmin();
    }
  }
}
```

管理者が新たに管理者を登録するような場合には下記のようにするのが良いかと思います。

```
service cloud.firestore {
  match /databases/{database}/documents {
    function isAdmin() {
      return request.auth.uid != null && exists(/databases/$(database)/documents/admins/$(request.auth.uid));
    }

    match /hoges/{hogeId} {
      allow read, write: if isAdmin();
    }

    match /admins/{adminId} {
      allow read, write: if isAdmin();
    }
  }
}
```
レポート（機械学習）

#線形回帰モデル

###回帰問題
　　ある入力（数値）から出力（連続値）を予測する問題
　　この回帰問題を解くためのモデルとして、線形回帰モデルが存在

###線形回帰
　　入力とm次元パラメータの線形結合を出力

###線形結合
　　入力ベクトルと未知のパラメータの各要素を掛け算し足し合わせたもの
　　⇒　出力は１次元（スカラ）となる

###線形回帰モデルのパラメータ
　　特徴量が予測値に対してどのように影響を与えるかを決定
　　⇒　重みが大きければ、その特徴量は予測に大きく影響（0の場合は全く影響しない）

#データの分割／学習
###データの分割
　　モデルの学習　⇒　学習用データ
　　モデルの検証　⇒　検証用データ

　　学習データを用いてモデル検証を行った場合、一般的には誤判定が少なるなるが、
　　汎化性能を図ることができない
　　⇒　学習用データと検証用データに分割して利用

###学習
　　パラメータの推定は、主に平均二乗誤差を用いて探索する
　　（誤差が最小となるパラメータを求める）
　　⇒学習データの平均二乗誤差の最小化　＝　勾配が0となる点を求める

#ハンズオン
　　※線形単回帰分析の推論実行時にそのままのコードだとエラーとなった
　　　⇒　説明変数が１つの場合は、predictメソッドの引数は二次元配列とする必要あり
　　　　　（scikit-learnはバージョン「0.20.x」を利用）

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451282/e449824a-803e-6228-5e58-8f8d913942d3.png)

###非線形回帰モデル
　複雑な非線形構造に対して、モデリングを実施

　基底展開法
　　⇒　未知パラメータは線形回帰モデルと同様に最小二乗法や最尤法にて推定
　　　　よく使われる基底関数：多項式関数、ガウス型基底関数、スプライン関数／Bスプライン関数


#正則化法
　過学習を防ぐための手法（ただし、正則化しすぎると未学習に陥る）
　モデルの複雑さに伴って、その値が大きくなるペナルティ項（正則化項）を貸した関数の最小化問題
　⇒モデルの曲線やなめらかさを調整するため、正則化パラメータ・平滑化パラメータが存在

###ペナルティ項
　L2ノルム　⇒　Ridge推定量　：　パラメータを0に近づけるよう推定
　L1ノルム　⇒　Lasso推定量　：　いくつかのパラメータを0に推定

#モデルの選択
　正則化パラメータはクロスバリデーションで選択

　ホールドアウト法
　　学習用データ、テスト用データに分割し、予測精度や誤り率を推定　（大量データがある場合に利用）

　クロスバリエーション法（交差検証）
　　データを複数グループに分割し、学習用データとテスト用データを切り替えながら性能予測を行う
　　⇒　すべてのデータを学習とテストに利用できるため、汎化性能が良くなる

#ロジスティック回帰
　分類問題を解くための機械学習モデル（出力は確率の値）
　※シグモイド関数を対応

#最尤推定

###尤度
　あるデータを得たときに、「分布のパラメータが特定の値であることがどれほどありえそうか」を表現
　⇒　データを固定して、パラメータを変化
　ロジスティック回帰モデルにおいては、ベルヌーイ分布を用いる

###同時確率
　学習データセットが同時に得られる確率を計算
　⇒観測されたデータ（学習データ）を発生させる尤もらしい確率分布を求める
　　※尤度関数を最大化するよりも、対数尤度関数を最大化するほうが計算は楽

#勾配降下法
　反復学習によりパラメータを逐次的に更新する方法の一つ
　「パラメータ更新がされなくなる＝勾配が0」　  ⇒　　探索範囲においては最適解

#確率的勾配降下法
　データをランダムに選んでパラメータを更新
　この方法を使うと、勾配降下法でのパラメータ更新１回と同じ計算量で、パラメータをn回更新できるため効率的

###エポック
　n回の勾配が0に収束させるため、繰り返し学習を行うが、この反復回数をエポックと呼ぶ

###ミニバッチ勾配降下法
　n個のデータを複数かたまりに分けて学習
　⇒メモリ不足を解消し、計算の高速化が可能

#分類の評価

　目的に応じて、正解率・適合率・再現率・F値を使い分けて評価する

　正解率：予測に対する結果が正しいかどうか
　適合率：見逃しを許容し、誤判定を許容しない
　再現率：見逃しを許容せず、誤判定を許容する
　F値：見逃しと誤判定の最適値を適用

#ハンズオン
![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451282/5e7973a3-d435-470b-6688-597a4af2499b.png)


#主成分分析
　学習データの分散が最大になる方向への線形変換を求める手法
　「情報の量＝分散の大きさ」として、線形変換後の分散が最大となる射影軸（線形変換）を探索
　※分散値＝固有値

###主成分
　最大固有値に対応する固有ベクトルで線形変換された特徴量　⇒　第一主成分
　k番目の固有値に対応する固有ベクトルで線形変換された特徴量　⇒　第k主成分

###寄与率
　第k主成分の分散の全分散に対する割合


#K近傍法／K平均法

###近傍法
　分類問題のための機械学習手法
　⇒ 特定範囲において、k個のクラスラベルの中で最も多いラベルを割り当てる

###平均法
　教師なし学習の手法
　⇒各クラスタ中心の初期値を設定し、各データ点との距離が近いクラスタを割り当て、
　　その平均ベクトルを繰り返し計算

#サポートベクターマシン
　２クラス分類を行うための機械学習方法
　決定境界と最も近いデータ点との距離（マージン）を最大化する
　線形モデルの正負で２値分類を行

###ラグランジュ未定乗数法
　制約付き最適化問題を解くための手法
　最適解を満たす条件を「KKT条件」という

###ソフトマージンSVM
　線形分離できない場合にも対応
　サンプルを線形分離できないとき、誤差を許容し、誤差に対してペナルティを付与

###非線形分離
　線形分離できないときは、特徴空間に写像し、その空間上で線形分離する
HTML の 要素 に 背景色 を設定する ～CSS background-color: カラーコード;～

# 目的
- HTMLファイルのブロック要素の背景色を設定する方法をまとめる 

# 押さえるポイント
- 背景色を変えたい要素をCSSで指定して色を付ける。 
- background-colorプロパティを使用する。
- 色の指定にはカラーコーかRGBの値を用いて指定する。  
※カラーコード・・・色を表すRGB値を十六進法で表記した文字列の事  
※カラーコードの見本は[こちら](https://www.colordic.org/)で確認できます。

# 書き方の例(CSS)
```css
要素の名前かクラス名 {
    /* カラーコードで背景色を指定する */
    background-color: カラーコード;
    /* rgbで背景色を指定する */
    background-color: rgb(XXX, XXX, XXX);
}
```

# より具体的な例
HTMLのdiv要素である`Hello World`に赤色の背景色を設定する場合。  
～サンプルコード～
  
- 下記にHTMLファイルの内容を記載する。

   ```html
   <div>Hello World</div>
   ```

- CSSで上記のHTMLファイルのdiv要素の背景色を赤色にする設定を行う。  
- 下記にCSSファイルの内容を記載する。

   ```css
   div {
       /* 単純な色だとredなどの文字でも指定できる */
       background-color: red;
   }
   ```
「FirestoreやCloud Storage for Firebaseのセキュリティルールには気をつけよう」って話

FirestoreやCloud Storage for Firebaseのセキュリティルールをちゃんと書こうって話です（自戒）。
いくつかの前提が成立している場合に、どんな感じでドキュメントにアクセスできるかを書いていきます。

## 前提
・一部のユーザーのみがログインできるシステムを作ろうとしていたがセキュリティルールをちゃんと設定できてなかった。
・認証はFirebase AuthのEmailを使用していた。

## 脆弱なセキュリティルール

Firestoreの下記のセキュリティルールは、ログインユーザーのみが読み書きできるようにしてあります。
登録に関して制限された状態ではないため、当然画面が存在しなくとも[Firebase Auth REST API](https://firebase.google.com/docs/reference/rest/auth)を叩いてユーザー登録を行えば、誰でも認証されたユーザーとして読み書きができてしまいます。

```
service cloud.firestore {
    match /hoges/{hogeId} {
      allow read, write: if request.auth.uid != null;
    }
  }
}
```

## Firebase Auth REST APIを用いてユーザー登録する

Eメールでの認証機能が行える状態の場合には、下記の様にすれば画面が存在しなくともユーザー登録を行うことができます。

```sh
curl 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=${API_KEY}' \
-H 'Content-Type: application/json' \
--data-binary '{"email":"${EMAIL}","password":"${PASSWORD}","returnSecureToken":true}'
```

上記の様なリクエストを送信した場合には下記のようなレスポンスが得られます。

```json

{
  "kind": "identitytoolkit#VerifyCustomTokenResponse",
  "idToken": "[ID_TOKEN]",
  "refreshToken": "[REFRESH_TOKEN]",
  "expiresIn": "3600"
}
```

## Cloud Firestore REST APIを用いてドキュメントにアクセスする

上記コマンド実行時に返却されたidTokenをAuthorizationヘッダーに付与して、下記のようにリクエストを送ると、上記のセキュリティルールに従っているので当然読み込みも書き込みも行うことができます。(下記の場合だとDOCUMENT_IDにhogesを入れるとhogesが取得できます)

```
curl https://firestore.googleapis.com/v1/projects/${PROJECT_ID}/databases/(default)/documents/${DOCUMENT_ID} -H "Authorization: Bearer ${ID_TOKEN}"
```

## まとめ

上記のように仮に脆弱な設定がされている場合には、いくつかAPIを叩くだけでドキュメントにアクセスされてしまいます。

脆弱なセキュリティルールにならないように注意しましょう（自戒）。

[Firestore Local Emulator](https://cloud.google.com/sdk/gcloud/reference/beta/emulators/firestore/)を用いてテストを書きながらセキュリティルールを構築していくのがおすすめです。
Nuxtのgoogle-analyticsモジュールを使うときの落とし穴

## Nuxtのgoogle-analyticsモジュールを使うときの落とし穴
Nuxtのgoogle-analyticsを使っていても、shift + command + U で見られるページソースにはGoogle Analyticsの読み込みのコードは表示されません。

自分はこのことを知らず、Nuxtのgoogle-analyticsが正常に動作していないと思い込んでいました。

解決策を調べていたところ。JavaScriptが実行されることで、Google Analyticsが導入されることが分かりました。インスペクターを使うことで確認することができました。
つまり、正常に動作していたのです。

この記事が参考になれば幸いです。
## 終わりに
私は現在、Web3のサービスの開発をしています。詳しくはこちらの記事をご覧下さい。
        [無料でイーサリアムが当たる、Web3時代の寄付サイトを作った話](https://note.com/kenroda/n/n814347fbdcd7)
youtubeからダウンロードするものを作ろうとしたら全部コピペで出来た。

###成果物
https://github.com/sakaku193/youtube_download


###経緯
webで公開されているやつを普段使ってたんですが、
広告がうっとうしかったので自作してしまえと思って作り始めました。

###ytdl-coreを使った。
とりあえずダウンロードする仕組みを作ろうと調べました。
結構出てきますが、一番わかりやすかった記事を参考に貼ります。
[Node.jsでYoutube動画をDLして保存するytdl-coreを使ったみたメモ](https://qiita.com/n0bisuke/items/af20c95a3dc6c22e5792)

これに記載されている通り、ytdl-coreをお借りしました。
正直コピペするだけで割といい感じに動いてくれます。

###readlineで標準入力から受け取るようにした。
上記をコピペじゃソース書き換えない限り一生同じ動画がダウンロードされるので
標準入力で動くようにしました。
以下参考
[Node.jsの標準入力と](https://qiita.com/hiroqn@github/items/c927bc97780c34eda562)
[Node.js v12.5.0 Documentation](https://nodejs.org/api/readline.html#readline_readline)

###JavaScriptにはgetParamのようなものが無い？
調べた限り無さそうなのでそういった処理をすでに作ってる人のやつをパクりました。
[Javascript でURLのパラメータを取得する方法](http://www-creators.com/archives/4463)

###感想
自分で使うには良い感じです。よかったらコピペして使ってください。
作ったけれどコーディングした気分にはならなかったので色々改造したいなぁとは思います。

※使う場合はnodejsとytdl-coreが必要になります。
Emacs Command (Mac)

# Emacs Command (Mac)
<img width="200" alt="絵でわかる人工知能.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343158/731a7a87-a02b-a468-7444-457080b67127.png">
Vim をずっと使っていたが、Emacsの操作を覚えた方が良い部分がたくさんあったので、備忘録として残しておきます。
**ただし、Mail や Jupyter notebook、Qiita などに使用する目的で、ファイル作成は今まで通り Vim を用いるため、必要最低限に絞っています。**
## 基本操作
|Key|Operation|
|:-:|:-------:|
|⌃ - v|次の画面に進む|
|⌥ - v|前の画面に戻る|
|⌃ - l|カーソル位置を中央にする|

基本的な移動は以下のように行う。

                               前の行，⌃ - p
                                    :
                                    :
     前の文字，⌃ - b   ....   現在のカーソル位置   ....   次の文字，⌃ - f
                                    :
                                    :
                               次の行，⌃ - n
単語単位で移動したい場合は、^ を ⌥ に置き換えれば良い。

|Key|Operation|
|:-:|:-------:|
|^ - a|行頭に移動|
|^ - e|行末に移動|
|⌥ - a|文頭に移動|
|⌥ - e|文末に移動|
|⌥ - <|ファイル頭に移動|
|⌥ - >|ファイル末に移動|
|^ - u|数値引数|
|^ - g|Quit|

## 削除 ・ 消去 ・ 再入
|Key|Operation|
|:-:|:-------:|
|^ - d|カーソルのある文字を削除|
|⌥ - d|カーソルの後ろにある単語を消去|
|^ - k|カーソル位置から行末までを消去|
|⌥ - k|カーソル位置から文末までを消去|
|^ - y|再入 (yanking)|
|⌥ - y|再入を以前のものに変換|
|^ - x - u|取消 (undo)|
|^ - _|取消 (undo)|

※ 削除：再入できる , 消去：再入できない
エラー解決Routing Error　No route matches 

##ルーティングエラーがでた！！！！

簡易版ツイッターを作っていて、投稿画面で投稿を送った時に下記のエラーが出た。

![スクリーンショット 2019-07-02 22.13.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435710/275e9ab5-199f-dd5c-76a3-602c8d32ef2e.png)

結論から言うと、今回のエラーはツイートを投稿する時に呼び出されるルーティングの下記部分に":create,"（ツイートを新規作成するアクション）の記載がなかったために、tweetコントローラからcreateアクションを呼び出せないよと言うエラーでした。

![スクリーンショット 2019-07-02 22.38.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435710/4bf19fdf-70fb-c0ff-6461-3273b6a3e558.png)


では、どんな手順でエラーを解いていったのかをみていきましょう。

まず、エラー画面ではどんなことを言っているのだろうか。

頭の赤帯部分にRouting Errorと書いていることから、ルーティングに何らかのエラーが出ていることが考えられる。

さらにその下に
No route matches [POST] "/tweets"
と書いてあることから、"/tweets"ってところと[POST]がマッチしていないことが考えられるわけですね。

とりあえず、ターミナルで"rake routes"とうって設定されているルーティングを見てみましょう。
![スクリーンショット 2019-07-02 22.23.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435710/ab325c06-cdea-f8e4-cc8c-1ca5d66a1e26.png)
そうすると、今どんなルーティングが設定されているかがわかります。
この中から、[POST] "/tweets"の部分を探してみましょう。

............
............
............
??????????

この画像をしっかり探した人ならわかるかと思いますが、HTTPメソッドが[POST]でパスが"/tweets"のルーティングがありません。

つまり、ルーティングに今リクエストされている[POST] "/tweets"は存在しないわけです。

そこで、routes.rbを確認してみると案の定、tweetsコントローラを呼び出している

```routes.rb
resources :tweets, only: [:index, :show, :new, :destroy, :edit, :update] do
```

この記載の中に、新しくツイートを作るためのcreateアクションが記載されていないですね。

これで、原因はわかりましたのであとはその記述を追加すれば良いわけです。

```routes.rb
resources :tweets, only: [:index, :show, :new, :create, :destroy, :edit, :update] do
```

こうすることで、ツイートが正常に投稿されるようになりました。

念の為、ターミナルにもう一度rake routesをうって確認してみると
![スクリーンショット 2019-07-02 23.11.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435710/ad030402-cf22-22cb-d416-f3e632dda135.png)
このように、[POST] "/tweets"が追記されています。

これで無事エラー解決です。

終わってみると簡単なエラーですが、初めのうちはどこを見れば良いかわからないのでパニックになりますよね。笑


まとまりませんが、こんな感じで今回は終わりたいと思います。
論文紹介-#3 Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks

## 論文紹介-#3 
[落合先生のフォーマット](https://www.slideshare.net/Ochyai/1-ftma15?ref=http://lafrenze.hatenablog.com/entry/2015/08/04/120205)を元に論文をまとめ紹介していく記事です。

今回は、[Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks](https://arxiv.org/pdf/1804.06039.pdf)という論文を紹介します。
至らないところばかりなのでツッコミお待ちしてます！

## どんなもの？
MTCNN並みのリアルタイム性を保持しつつ回転不変の顔枠検出を行うProgressive Calibration Network(PCN)を提案した。

## 先行研究と比べてどこがすごい？
一般的に、回転に対して以下３つの戦略が取られる
1, データオーグメンテーション
一般的な顔枠検出器に回転顔データを入れるというもの。モデルに修正なく学習出来るが、学習にコストがかかってしまう。
2, 分割統治法
各回転角度に対して、それぞれのモデルを学習するというもの。一つのモデルは小さくて済むが、全体的に時間がかかってしまう。
3, Rotation router
ルーターネットワークにより推定したRIP角度を元に直立するようにキャリブレーションした顔画像に対して、顔枠検出を行うというもの。RIP角度の推定は難易度が高い為大規模なモデルになってしまう、かつルーターネットワークの誤検出が顔枠検出にも影響してしまう。
<img width="676" alt="スクリーンショット 2019-07-02 22.47.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218202/a8156af3-ab51-9c5e-00a4-a041149befec.png">


これらの既存の方法は、大きなＲＩＰ変動を処理するために速度と精度とはトレードオフの関係だったが、段階的なキャリブレーションプロセスを導入することで、正確で迅速な顔枠検出を可能にした。

## 技術や手法のキモはどこ？
ＰＣＮは、180度、90度、45度(最後は-45~45の間で直接RIP角度推定)とキャリブレーションプロセスを3段階に分け徐々に顔を直立にしていくことで、予測タスクが簡単になり正確で迅速なキャリブレーションを実現できる。
<img width="671" alt="スクリーンショット 2019-07-02 22.46.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218202/560dd74c-08c1-01bb-7394-5faa76bcfa8d.png">
<img width="327" alt="スクリーンショット 2019-07-02 22.58.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218202/c956df90-3721-db05-181d-70e6543b0ed2.png">


## どうやって有効だと検証した？
データオーグメンテーション、分割統治法、rotarion routerの３つの従来戦略を最先端の顔枠検出モデルに適用しFDDBデータセットとWIDER FACEデータセットで比較した。
<img width="666" alt="スクリーンショット 2019-07-02 22.58.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218202/91b3876d-7052-007b-cc17-aad09f2a9c60.png">


## 議論はある？
言及なし

## 次に読むべき論文は？
[Rotation invariant
neural network-based face detection](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.39.1819&rep=rep1&type=pdf)
Railsでスマートフォン用ページのControllerを分ける

# はじめに
レスポンシブデザインではなく、PC用のViewとスマートフォン用のViewを分けて実装する場合があるかと思います。
その際に、ActionPack Variantsを利用して、1つのControllerでデバイス毎にViewを切り替える方法が一般的かと思います。

しかし、PCページだけ先にVue.jsにリニューアルするなど、PCとスマートフォンでControllerを分けたい時などがあります。
本記事では、デバイスごとにControllerを切り替える方法をご紹介します。

# やり方
大まかな方法としては以下のようになります。
1. requestからユーザーがアクセスしているデバイスを判定する
2. routingのconstraintsを使ってPC用のControllerとスマートフォン用のControllerと振り分ける

**検証環境**

```
Ruby 2.5.5
Ruby on Rails 5.2.3
```

## 準備
PCとスマートフォンそれぞれのControllerとViewを用意しておきます。

```ruby:app/controllers/pc_page_controller.rb
class PcPageController < ApplicationController
    def index
    end
end
```

```ruby:app/controllers/smartphone_page_controller.rb
class SmartphonePageController < ApplicationController
    def index
    end
end
```

```erb:app/views/pc_page/index.html.erb
PC用のControllerから表示されました。
```

```erb:app/views/smartphone_page/index.html.erb
Smartphone用のControllerから表示されました。
```

## アクセスしているデバイスの判定
今回は`rack-user_agent`というgemをつかってデバイスを判定します。
Gemfileに`rack-user_agent`を追加しておきます。

```ruby:Gemfile
gem 'rack-user_agent'
```

以下のようにrequestが拡張されて、デバイスを判定できるようなメソッドが追加されるので、簡単に判定できます。

```ruby
class HomeController < ApplicationController
    def index
        request.from_pc?         #=> true
        request.from_smartphone? #=> false
    end
end
```

その他の機能はGithubのレポジトリを参照してください。
[k0kubun/rack-user_agent: Rack::Request extension for handling User-Agent](https://github.com/k0kubun/rack-user_agent)

## constraintsを使ってルーティングを設定
constraintsを使うとルーティングにマッチさせる条件を設定することが出来ます。
参考：[Rails のルーティング - Rails ガイド](https://railsguides.jp/routing.html#%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88%E3%82%92%E5%88%B6%E9%99%90%E3%81%99%E3%82%8B)

### 基本的な使い方
```ruby:config/routes.rb
get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ }
```
/photos/A12345のようなパスの場合`photos#show`が呼ばれますが、/photos/893はマッチしないため呼ばれません。

### 独自のConstraintを作成する
constraintsは独自のConstraintを作成することで、柔軟な判定をすることが出来ます。
matches?メソッドがtrueを返す場合、そのルーティングにマッチします。

```ruby:app/constraints/smartphone_constraint.rb
class SmartphoneConstraint
    def self.matches?(request)
        request.from_smartphone?
    end
end
```

```ruby:config/routes.rb
get '/', to: 'smartphone_page#index', constraints: SmartphoneConstraint
get '/', to: 'pc_page#index'
```

このように設定すると、routes.rbの上から順に判定されるため、
スマートフォンからのアクセスだと`smartphone_page#index`が呼ばれ、それ以外は`pc_page#index`が呼ばれることとなります。

## 確認してみる
- PCからアクセスした場合
<img width="1552" alt="スクリーンショット 2019-07-02 23.08.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402550/c9babcca-bf5d-ac79-97ff-8ccb085de68b.png">

- スマートフォンからアクセスした場合
<img width="1552" alt="スクリーンショット 2019-07-02 23.09.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402550/f00402f4-2808-dcfc-684c-ba016cc62b40.png">

ちゃんと切り替わっていますね:+1:

# おわりに
既存のRailsアプリのフロントエンドをVue.jsにリニューアルしたくて、まずはPCページだけ導入してみようと思い今回の方法を使ってみました。
基本的にはデバイス毎にViewだけを切り替えたほうが分かりやすいとは思いますが、Controllerも切り替える必要があればこんな方法はいかがでしょうか。

Github: [boronngo/rails-sp-routing](https://github.com/boronngo/rails-sp-routing)
Google App Engineのログを確認する方法

#はじめに
GAEで実行したアプリでエラーが出てしまう時にはGAEのログを確認してみましょう。
開発環境では動いたのになぜかデプロイすると動かなくなってしまったなど起こってもログを見れば原因を追求できます。

#ログの確認手順

メニューのAppEngineからサービスを開きます。

![スクリーンショット 2019-07-02 23.04.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/da5d71d1-32f9-43f5-3c66-83f6eaf890a3.png)

ツールをクリックします。

![スクリーンショット 2019-07-02 23.04.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/7ef4081a-fb7c-3f17-b367-ade039579185.png)

ログをクリックします。

![スクリーンショット 2019-07-02 23.04.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/fee00aab-c31d-ba4f-f2ea-3c796dffb30c.png)

このようなログが表示されます。

![スクリーンショット 2019-07-02 23.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/fe6abce6-b195-dd0b-50bf-221bb5f90d47.png)


#エラーログの例

デプロイ時は「requirements.txt」に必要なコンポーネントを書くことが必須のようですが、「requirements.txt」を作っていませんでした。そのためflaskが見つからずに以下のようなログが出力されました。
![スクリーンショット 2019-07-02 23.06.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/e32437d7-f88d-2502-2cae-683a40333969.png)


GAEでファイルの書き込みができない領域であるにもかかわらずファイルを書き込む処理をしようとしてしまっていました。その場合に以下のようなログが出力されました。
![スクリーンショット 2019-07-02 23.07.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439918/46a16a32-c618-2a38-2a24-03a3bf7955ec.png)

#最後に
こんなちゃんとしたログが見れることを知らず、なぜエラーが起きるのかとひたすら迷い込んでいましたがログが見れることを知ってすぐに問題が解決しましたので書き残しておきます。
BOMのせいでPHPのjson_decodeがnullになってた 

jsonファイルに問題がないように見えたけど、なぜかjson_decordでnullになったので、調べたらBOMが付きで保存されてて、悪さしてたよう

BOM付きかどうかの調べ方

参考
http://neos21.hatenablog.com/entry/2018/04/11/080000

自分はMacだったのでこちらのコマンドでできました
$ file hogehoge.json

結果
hogehoge.json: UTF-8 Unicode (with BOM) text

hogehoge.json は (with BOM)でした。

SublimeTextなら File|Save with Encoding から保存形式(BOMの有無も)選択できます。

そもそもなんでBOMが付いてたかというと、Windowsのメモ帳でjsonを開いた際についたみたいです。
メモ帳はデフォでBOM付きで保存するらしいです。
メモ帳はわかりませんが、notepad++にはBOM無し保存の設定があったのは確認しました。
インターネットに接続されていない環境下でWindows ServerにOpenSSHを入れる


# インターネットに接続されていない環境下でWindows ServerにOpenSSHを入れる

- 前提：RDPでアクセス可能である
- 公開鍵認証でログインできるようにする

## 前準備

ココを参考にする
https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH

あらかじめダウンロードしておき、Windowsに移す。
https://github.com/PowerShell/Win32-OpenSSH/releases/download/v8.0.0.0p1-Beta/OpenSSH-Win64.zip
RDPでは、ローカルでファイルをコピーして、リモートでペーストするだけで良い。簡単。

## インストール

解凍して `C:\` 直下に置く。

PowerShellを **管理者モード** で立ち上げる。
解凍したディレクトリに移動し、以下を実行。

```
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Windows\system32> cd C:\OpenSSH-Win64\

PS C:\OpenSSH-Win64> powershell.exe -ExecutionPolicy Bypass -File install-sshd.ps1
[SC] SetServiceObjectSecurity SUCCESS
[SC] ChangeServiceConfig2 SUCCESS
[SC] ChangeServiceConfig2 SUCCESS
sshd and ssh-agent services successfully installed

PS C:\OpenSSH-Win64> New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
Name                  : sshd
DisplayName           : OpenSSH Server (sshd)
Description           :
DisplayGroup          :
Group                 :
Enabled               : True
Profile               : Any
Platform              : {}
Direction             : Inbound
Action                : Allow
EdgeTraversalPolicy   : Block
LooseSourceMapping    : False
LocalOnlyMapping      : False
Owner                 :
PrimaryStatus         : OK
Status                : The rule was parsed successfully from the store. (65536)
EnforcementStatus     : NotApplicable
PolicyStoreSource     : PersistentStore
PolicyStoreSourceType : Local

PS C:\OpenSSH-Win64> net start sshd
The OpenSSH SSH Server service is starting..
The OpenSSH SSH Server service was started successfully.
```

これで設定ファイルなどが生成される

## 設定の変更

`sshd_config` の編集

```
PS C:\OpenSSH-Win64> notepad C:\ProgramData\ssh\sshd_config
```

ファイルの一番下にある以下の2行を削除する。
（このファイルが存在しない場合、公開鍵認証が途中でストップして失敗するため。）

```
Match Group administrators
       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
```

sshdを再起動して設定を反映。

```
PS C:\OpenSSH-Win64>net stop sshd
The OpenSSH SSH Server service was stopped successfully.

PS C:\OpenSSH-Win64> net start sshd
The OpenSSH SSH Server service is starting.
The OpenSSH SSH Server service was started successfully.
```

## 公開鍵認証

公開鍵をなんとかして `~/.ssh/authorized_keys` に書き込む。
普通にRDP越しのコピペが楽で良い。

このとき、ファイルのアクセス権に注意。
「プロパティ＞セキュリティ」を確認し、EVERYONEに対してアクセス権があると、鍵を読み込んでくれないため、要確認。

これで公開鍵認証によるSSHログインが可能になるはず。
うまくいかない場合は `sshd_config` に以下の設定を追加すると `C:\ProgramData\ssh\logs\sshd.log` にログが作成されるようになるので、がんばってトラブルシュートしてください。

```
SyslogFacility LOCAL0
LogLevel DEBUG3
```

## その他ハマりポイント

- デフォルト設定だとEd25519鍵が拒否される
【JavaScript】addEventListener()の主要イベントまとめ

# はじめに
今回はaddEventListener()の主要なイベント一覧をまとめます！
addEventListener()はJavaScriptの頻出中の頻出なので何が使えるかくらいは頭の片隅に置いとくと良いかと思います！:muscle:


#そもそもaddEventListenerとは
**あるイベントが実行された時に、メソッド内に書いてある一連の動作を実行するメソッドになります。**
例えば、以下のようなhtmlファイルがあるとします。

```test.html
<div id = 'btn'>ここをプッシュ！</div>
```
そして以下のjsファイルは、上記btnID内のテキストを、クリック時に変更するコードです。

```test.js
  const btn = document.getElementById('btn');　//定数btnにhtmlのbtnIDの要素を取得

  btn.addEventListener('click', ()=>{　//定数btnがクリックされた時に
    btn.textContent = "あああ"; //btnIDの要素のテキストを"あああ"に変更
  });
```

addEventListenerの基本的な書き方は以下です。
他にも書き方はありますので、気になる方はぜひ調べてみてください！

```test.js
対象要素.addEventListener(イベントの種類, function() {
  
  //イベント発火時の処理内容
  
});
```

#イベントの種類
先ほどのclickのように、他にも様々なイベントがあります。
ここではよく使用するイベントをざっくりとまとめます。

| イベントの種類      |       内容 |
|:-----------------|------------------:|
| click             | マウスボタンをクリックした時に発火 |
| mousedown           | マウスボタンを押した時に発火|
| mouseup            | マウスボタンを離したときに発火|
| mousemove |   マウスカーソルが移動した時に発火|
| submmit            |    入力フォームのsubmitボタンを押したときに発火| 
| scroll          |   画面がスクロールした時に発火| 
| submmit            |    入力フォームのsubmitボタンを押したときに発火| 
| scroll          |   画面がスクロールした時に発火|

#さいごに

上記は本当に基本的なイベントのみですが、調べるとかなりたくさん種類があります！
あらかじめこんなに用意してくれてるなんて親切すぎますね:relaxed:
【Vue】テンプレート直下のノードは1つのブロックレベル要素でないといけない

# エラー例
👇このようなテンプレートだとコンパイルエラーになります。

```vue:sample.vue
<template>
  <h1>タイトル</h1>
  <div class="content">
    <p>コンテンツ</p>
    <ul>
      <li>abc</li>
      <li>xyz</li>
    </ul>
  </div>
</template>
```

```sh:ログ
Component template should contain exactly one root element.
```
# テンプレートはどんな形式であるべきか
VueではないけどAngularJSの記事でテンプレートのroot element系エラーに関する似た内容を見つけたので参考にしました👇
[カスタムディレクティブのテンプレートで"root element"なんたらのエラーが出た場合](https://qiita.com/bump_of_kiharu/items/8a3bb35e01559d0c0528)

要するに、テンプレート直下は一つの`<div>`や`<form>`等のブロックレベル要素でないといけないんですね〜。コンポーネントという概念が少し紐解けた気がします。

```vue:sample.vue
<template>
  <div class="below-template">
    <h1>タイトル</h1>
    <div class="content">
      <p>コンテンツ</p>
      <ul>
        <li>abc</li>
        <li>xyz</li>
      </ul>
    </div>
  </div>
</template>
```

ブロックレベル要素に具体的にどんなものが存在するかはコチラのリンクを参考にしてみてください。
[ブロックレベル要素とインライン要素](http://www.htmq.com/htmlkihon/005.shtml)
今日の学習０７０２

##0702 やったこと
①RailsのMVC（https://www.pikawaka.com/rails/mvc）の記事を読む。
②わからない単語調べる。
ーインスタンス変数
③録画したビデオの復習

## 学んだこと
・インスタンス変数とは何か？
→＠＋変数名で定義され、インスタンスごとに、固有の値を保持することが可能。

・録画を見て、戻り値の理解

・MVCの流れの把握



##難しくてはまってしまったところ

・関数への理解がまだ完璧ではない。
githubでwebhookを使った自動デプロイ

#動機
ローカル環境で開発後にgithubへpushし、本番サーバへログインし、毎回gitpullをしていましたが、結構手間を感じていました。
pushしたらすぐに本番にも反映させたい！と思い、色々googleで調べているとGitHubからのWebHookでやりたいことができそうだったので、実践しました。

#設定の順序
1.gitpullするスクリプト作成
2.GitHubで設定する


#git pullするスクリプト作成
```php:webhook.php

<?php

// 設定
$LOG_FILE = dirname(__FILE__).'/hook.log';
$SECRET_KEY = '◯◯◯'; //githubで設定するSECRETと同内容(好きに決めます)

if (!function_exists('getallheaders')) 
{ 
    function getallheaders() 
    { 
           $headers = []; 
       foreach ($_SERVER as $name => $value) 
       { 
           if (substr($name, 0, 5) == 'HTTP_') 
           { 
               $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; 
           } 
       } 
       return $headers; 
    } 
} 

$header = getallheaders();
$hmac = hash_hmac('sha1', file_get_contents("php://input"), $SECRET_KEY);
if ( isset($header['X-Hub-Signature']) && $header['X-Hub-Signature'] === 'sha1='.$hmac ) {
    $payload = json_decode(file_get_contents("php://input"), true);
　　//gitpullする対象のフォルダへ移動する 
    exec('cd /var/www/html/◯◯◯ ; git pull');
    file_put_contents($LOG_FILE, date("[Y-m-d H:i:s]")." ".$_SERVER['REMOTE_ADDR']." git pulled: ".$payload['after']." ".$payload['commits'][0]['message']."\n", FILE_APPEND|LOCK_EX);
} else {
    file_put_contents($LOG_FILE, date("[Y-m-d H:i:s]")." invalid access: ".$_SERVER['REMOTE_ADDR']."\n", FILE_APPEND|LOCK_EX);
}

?>

```
作成したスクリプトは
GitHubからhttpでアクセスする必要がある為、
ドキュメントルート下に配置します。

#GitHubで設定する
GitHubで対象レポジトリ へ移動します。
Setting→Webhooksを選択します。

PayLoadURLは作成したスクリプトを指定します。
また、Secretにはスクリプトで記載した同内容を入力します。

<img width="1158" alt="Banners_and_Alerts_と_Webhook_·_http___52_15_137_52_webhook_php.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234640/f0db62bb-d96a-96c9-befa-4d6d8a38b535.png">

#最後に
ここまで作成するとpushした時点で本番環境にも変更が反映されます！
leadingAnchorとleftAnchor, trailingAnchorとrightAnchorの違い(NSLayoutAnchor)

そもそも
`leadingAnchor: 先頭
leftAnchor: 左側
trailingAnchor: 末端
rightAnchor: 右側`
という意味になる

## 英語、フランス語、スペイン語、日本語など
左から右に文章を読む言語のため、
**先頭と左（および末尾と右）を同じ意味で使える**
`leadingAnchor: 先頭 = leftAnchor: 左側`
`trailingAnchor: 末端 = rightAnchor: 右側`


## ヘブライ語、アラビア語など
右から左に文章を読む言語のため、
**先頭と左（および末尾と右）を同じ意味で使えない**
`leadingAnchor: 先頭 = rightAnchor: 右側`
`trailingAnchor: 末端 = leftAnchor: 左側`

# 結論
言語ごとの文章の読み方を考慮するため、２組用意されている
→日本人が使う分にはどっちを使っても良い

# 参考文献
・[Difference between leftAnchor and leadingAnchor?](https://stackoverflow.com/questions/32981532/difference-between-leftanchor-and-leadinganchor)
JSXの基本

# JSXとは
- JavaScriptの拡張⾔語
- 実行時に、JavaScriptにトランスコンパイル（変換）される。
- (React.createElementメソッドに変換される)

例えば、このファイルの場合

```jsx:index.js
const titleName = 'New Homepage';

const tag = (
 <div className="main">
 <h1>This web site is {titleName}</h1>
 <img src="http://~" />
 </div>
);
```

このように変換される

```jsx:index.js
const name = 'New Homepage';

const tag = React.createElement(
 'div',
 { className: 'main' },

 React.createElement(
  'h1',
  null,
  `This web site is ${titleName}`
 ),
 React.createElement(
  'img',
  { src: 'http://~' }
 ),
);
```

# 基本的なルール
##　ルート要素
```jsx:index.js

ReactDOM.render(
 <h1>React</h1>
 <a>about</a>
 <a>jsx</a>
 document.getElementById('root')
);
```
上のように、複数の要素を並べることはできない
以下のように唯一のルート要素を持つようにする

```jsx:index.js

ReactDOM.render(
 <div>
  <h1>React</h1>
  <a>about</a>
  <a>jsx</a>
 </div>,
 document.getElementById('root')
);
```

`<div>`を出力したくない場合、仮の要素として`<React.Fragment>`を使えば、出力されない

```jsx:index.js

ReactDOM.render(
 <React.Fragment>
  <h1>React</h1>
  <a>about</a>
  <a>jsx</a>
 </React.Fragment>,
 document.getElementById('root')
);
```

#### 空要素は`/>`で終える
```jsx
const tag = <img src={logo}>;
```
↓

```jsx
const tag = <img src={logo} />;
```


## 属性
`class` → `className`
`for` → `htmlFor`
`tabindex` → `tabIndex` (キャメルケースにする)

## コメント
`<!-- コメント -->` 
↓
 `{/*コメント*/}`

タグの中に書く場合
`<p // コメントです。 >`
`<p /* コメント */ >`

# JavaScript式に埋め込む
```jsx:index.js
const name = 'jon';

ReactDOM.render(
 <p>hello, {name}！</p>,
 document.getElementById('root')
);
```

演算子、関数の呼び出しも可能

```jsx:index.js
ReactDOM.render(
 <div>
  <p>{ 'hello, ' + name + '!' }</p>
  <p>Date: { new Date().toLocaleString() }</p>
  <p>2 * 330 + 6 = { 2 * 330 + 6 }</p>
 </div>,
 document.getElementById('root')
);
```

```jsx
const url = 'https://~';
ReactDOM.render(
 <a href={url}>web page</a>,
 document.getElementById('root')
);
```

#### css属性
```jsx
const style = { color: '#000', backgroundColor: '#fff' };
//キャメルケースを使うことに注意

ReactDOM.render(
 <p style={style}>WINGSプロジェクト</p>,
 document.getElementById('root')
);
```

#### タグの属性をまとめて指定
`...`演算⼦を利⽤することで、複数の属性をまとめて指定できる

```jsx
const attrs = {
 src: 'http://~',
 alt: '',
 title: 'web page'
};

ReactDOM.render(
 <img {...attrs} />,
 document.getElementById('root')
);
```

↓

```jsx
<img src="http://~" alt="" title="web page">
```

参照
https://ja.reactjs.org/docs/jsx-in-depth.html#___gatsby
Ubuntu 18.04 LTS > STM32L476 > 環境構築 (STM32CubeMX + STM32CubeIDE) + Lチカ || TrueStudioとの連携は失敗

```txt:動作環境
Ubuntu 18.04 LTS
STM32CubeMX v5.2.1
STM32CubeIDE v1.0.1
基板:STM32L476RGT6 64 PINS
```

### 概要

- STM32L476をLチカする
    - [STM32L476RGT6](https://www.stmcu.jp/stm32/stm32l4/stm32l4x6/12841/)


### セットアップ

- STM32CubeMXをインストール 
- STM32CubeIDEをインストール

### 参考

- [Lチカしてみる](https://nucleo87.webnode.jp/nucleo%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0/l%E3%83%81%E3%82%AB%E3%81%97%E3%81%A6%E3%81%BF%E3%82%88%E3%81%86%EF%BC%81/) @ Nucleo入門様

### 起動方法
(追記: 2019-07-29)

以下のようにしてSTM32CubeIDEを起動する (環境により異なるかもしれない)。

```
$ cd /opt/st/stm32cubeide_1.0.1
$ ./stm32cubeide
```

### 実施

以下におおまかな流れを示す。コードは「Nucleo入門」様の実装をそのまま使用しました。

1. STM32CubeIDEを起動する
2. STM32Projectを新規作成する
3. MCU/MPU Selectorにて「STM32L476RG」で検索 (Part Number Search)
4. MCUs/MPUs Listにて「STM32L476RG」を選択しプロジェクトを作成
    - Project Name: 任意 (例: test_L476_190702)
    - Next
    - Finishにてプロジェクト作成
5. Pinoutにて「21(PA5)」をGPIO_Outputに変更
    - あとでこのピンをLチカする
6. Src/main.cにおいて上記の参考ページを参考に2行の実装をwhile(1)内に追加する
>
```c
HAL_GPIO_TogglePin( GPIOA, GPIO_PIN_5 );
HAL_Delay( 1000 );          
```

7. ビルドする
8. Debug Asにて「STM32 MCU C/C++ Application」を選択する
9. Edit configurationにてOKを押す
10. デバッグ画面になるのでResumeを押して実行する

LD2という部分が1秒おきに点灯、消灯します。

### 実行例

緑色の点灯がLD2
![IMG_0896.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/ef4128bc-324e-33ff-1330-8bbe6d8ae9d0.jpeg)


### TrueStudio

当初、STM32CubeMXとTrueStudioを使おうとしたが、STM32CubeMXで生成したプロジェクトからTrueStudioへの連携がうまく機能しなかった。

STM32CubeMXからOpen Projectをすると、.cprojectファイルがテキストエディタで開くだけであった。

STM32CubeIDEを導入したところ、STM32CubeMXとの連携がうまくいった。
Laravel5.8 FormRequestでバリデーション失敗時にJsonResponseを返す

LaravelでAPI実装する際、バリデーション失敗したときにJSONレスポンスを返したいときのtips

## 環境

- PHP: 7.3.6
- Laravel 5.8.22

## FormRequest とは

コントローラに書いたバリデーションの処理をバリデーション専用のクラスとして切り出したものになります。
バリデーションを分けることにより、コントローラにはバリデーション済みのリクエストが来るのでよりシンプルに記述できるメリットがあります。

## バリデーションエラー時

デフォルトでは、`getRedirectUrl()`メソッドが実行されてリダイレクトされ、HTMLレスポンスが返ってきます。
APIを実装するときにHTMLでレスポンスが返ってくると困るのでjsonで返ってくるように設定します。

- [getRedirectUrl](https://laravel.com/api/5.8/Illuminate/Foundation/Http/FormRequest.html#method_getRedirectUrl)
- [FormRequest.php#L140](https://github.com/laravel/framework/blob/5.8/src/Illuminate/Foundation/Http/FormRequest.php#L140)

## ApiRequest

新しく `app/Http/Requests/ApiRequest.php` を作成します。

```php
<?php

namespace App\Http\Requests;

use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;

abstract class ApiRequest extends FormRequest
{
    /**
     * Handle a failed validation attempt.
     *
     * @param Validator $validator
     * @return void
     * @throw HttpResponseException
     */
    protected function failedValidation(Validator $validator): void
    {
        $data = [
            'data' => [],
            'status' => 'error',
            'summary' => 'Failed validation.',
            'errors' => $validator->errors()->toArray(),
        ];

        throw new HttpResponseException(response()->json($data, 422));
    }
}
```

バリデーション失敗時の挙動を変えるには `failedValidation()` をオーバーライドします。
全てのFormRequestクラスに実装するのは手間なので、 `ApiRequest` という抽象クラスを用意します。

- [failedValidation](https://laravel.com/api/5.8/Illuminate/Foundation/Http/FormRequest.html#method_failedValidation)
- [FormRequest.php#L128](https://github.com/laravel/framework/blob/5.8/src/Illuminate/Foundation/Http/FormRequest.php#L128)

## 例: SignUpRequest

`ApiRequest` を継承して実装していけばokです。

```php
<?php

namespace App\Http\Requests;

class SignUpRequest extends ApiRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => ['required'],
        ];
    }
}
```

## 参考記事

- [【Laravel5】FormRequestのバリデーション結果をJSON APIで返す](https://qiita.com/junsan50/items/ec7f810decd3b82d3d76)
【初心者向け】CentOS7でFTPサーバー構築

#はじめに
今やっている案件でFTPを使用してファイルをGETするスクリプトが必要となり
検証環境でFTPサーバを立てる必要があったのでつくりました。
備忘のためもあるので、お手軽にFTPサーバ作りたいという人の参考になればと思います。

#環境
今回は以下環境で作成します。
OS:CentOS7

#構築
##FTP接続用ユーザ作成
まずはFTPで接続するためのユーザを作成する。

```
# useradd ftp-user
# passwd ftp-user # パスワード設定
```

##FTP用のディレクトリ作成
FTPでは通常chrootといって、上位のディレクトリに接続できないような設定を入れることが通常です。
なので、まずはFTPで接続したときにデフォルトでたどり着くディレクトリを作成しておきます。(今回は/var/www/ftp_dirとします。)
作成したら権限変更もしておきましょう。

```
# mkdir -p /var/www/ftp_dir
# chown ftp-user /var/www/ftp_dir
# chmod 755 /var/www/ftp_dir
```

##FTPのインストール

```
# yum -y install vsftpd
```

##FTPの設定
```bash:/etc/vsftpd/vsftpd.conf
# 以下を追記します。
～
# userlist_fileで指定指定したユーザを有効にする（接続できる）
userlist_enable=YES

# アクセスファイル（/etc/hosts.allow, /etc/hosts.deny)をアクセス制御に利用しない
tcp_wrappers=NO


# パッシブモードの接続を許可する
pasv_enable=YES

# FTPサーバのIPアドレスを指定（パッシブモードで接続時）
pasv_address=[FTPサーバーのIP]
※上記のIPは固定となります。

# ポート指定 
pasv_min_port=60001
pasv_max_port=60010
※ポートはハイポートであれば特に指定はありません

# ドットから始まるファイルを含めた一覧を返す
force_dot_files=YES

# ファイル（またはディレクトリ）のタイムスタンプの表示をローカルタイムで表示する
use_localtime=YES

#ユーザー毎の設定ファイルの場所を指定する。
user_config_dir=/etc/vsftpd/user_conf

```


```/etc/vsftpd/vsftpd.conf
# 以下のコメントを外します。

chroot_local_user=NO

#chrootを有効化する。
chroot_list_enable=YES

#chroot対象のユーザーリストファイルの場所を指定する。
chroot_list_file=/etc/vsftpd/chroot_list
```

##FTP接続用ユーザの登録
```bash:/etc/vsftpd/chroot_list
ftp-user
```

```
# mkdir /etc/vsftpd/user_conf
# vi /etc/vsftpd/user_conf/ftp-user
```

以下を設定することでftp-userで接続したときのchrootは「/var/www/ftp_dir」となる。

```bash:/etc/vsftpd/user_conf/ftp-user
local_root=/var/www/ftp_dir
```
##サービス起動

```
# systemctl start vsftpd
```

#接続確認
Windowsの端末からコマンドプロンプトを起動し、接続確認を行います。

```
>ftp [FTPサーバーのIPアドレス]
[FTPサーバーのIPアドレス]に接続しました。
220 (vsFTPd x.0.x)
200 Always in UTF8 mode.
ユーザー ([FTPサーバーのIPアドレス]:(none)):    ←FTP接続ユーザ名
331 Please specify the password.　←パスワードを入力
パスワード:

230 Login successful.　←これがでていれば接続OK
```


試しにdirを打ってみると、chrootに設定したフォルダ配下のファイルが確認できる。

#参考記事
[FTPサーバの設定手順](https://qiita.com/yoshiokaCB/items/313fa290832a0afa4544)
[centos7のvsftpdでchroot環境を作る](https://qiita.com/mugi-tea/items/b6ee5d9127409667bc7c)