OmniAICreator/Qiita-1.07M huggingface dataset:
Heroku、Flask、Python、Gitでアップロードする方法（その②）


#Heroku、Flask、Pythonについて、Progateやドットインストール、Qiita等でひととおり学習したので、まとめてみる
[（１）作業ディレクトリを作成
（２）仮想環境を設定
（３）必要なフレームワークとWEBサーバーをインストール
（４）FlaskファイルをPythonで作成
（５）Flaskファイルを実行してみる](https://qiita.com/drafts/8ed2aa6334141e6b2db5/edit?resume=true)
（６）Herokuにデプロイする
　　①Herokuにアカウント登録する
　　②HomebrewでHerokuをインストールする
　　③Gitを用意する
　　④Herokuにアプリを作成する
　　⑤ローカルアプリとHerokuアプリを紐つける
　　[⑥Herokuへのデプロイに必要なファイルを作成する
　　⑦Herokuへデプロイする](https://qiita.com/drafts/50d04013bf74ba270960/edit?resume=true)
[（７）エラー対応
　　①heroku ps:scale web=1で正常になるケース
　　②heroku ps:scale web=1で正常にならないケース
（８）ファイルを更新する](https://qiita.com/drafts/0352898c40830547015f/edit?resume=true)



#（６）Herokuにデプロイする
###①Herokuにアカウント登録する
以下のHerokuページにアクセスして、Herokuのアカウント登録をする。
https://www.heroku.com/

###②HomebrewでHerokuをインストールする
まずはHomebrewというパッケージマネージャーをインストールする。
以下のHomebrewページにアクセスして、表示されているスクリプトをターミナルに貼り付けて実行する。
https://brew.sh/index_ja

```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

その後、以下をターミナルに入力してHerokuをインストールする。

```
brew install heroku/brew/heroku
```

先ほど作成したアカウントでログインする。
<img width="575" alt="スクリーンショット 2020-02-26 20.59.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29856/fb5eca0f-c3a3-b052-6f31-5207cba2a09d.png">

###③Gitを用意する
Gitについての詳細は以下のページを参考にした。
https://backlog.com/ja/git-tutorial/
https://prog-8.com/languages/git

Gitのバージョンを確認してみる。

```
git version
```
Gitがインストールされていれば、以下の通りとなる。

```
git version 2.20.1 (Apple Git-117)
```

初期設定の確認として、以下をそれぞれ入力する。

```
git config user.name
```

```
git config user.email
```

何も反応なければ、以下の例のように名前やemailを登録する。
（登録しなければ、Gitが正常に動かない）

```
git config user.name "hogehoge"
```

```
git config user.email "example@gmial.com"
```

###④Herokuにアプリを作成する
ローカルで作成したhello.pyアプリを、Heroku（リモート）を結びつける必要がある。
まずは、以下のようにHerokuにログインする。

```
heroku login
```

以下のように表示されたら、q以外の任意の文字を入力するとHerokuページが表示される。

```
Press any key to open up the browser to login or q to exit:
```

ログインすると以下のような画面となる
<img width="575" alt="スクリーンショット 2020-02-26 20.59.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29856/aa9492f6-d6c3-a5fd-433a-a2be086bd4e1.png">

その後ターミナルで、Herokuに、例として、sample-noonnoon というアプリを作成することとし、以下のように入力する。

```
 heroku create sample-noonnoon
```

これでHerokuにアプリが作成された。Herokuページにログインし、ダッシュボードを確認すると、以下のようにsample-noonnoonが確認できる。
<img width="468" alt="スクリーンショット 2020-02-27 19.10.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29856/25cad601-0c6f-15ea-053b-911497cd7570.png">

###⑤ローカルアプリとHerokuアプリを紐つける
ローカルで作成したアプリと、Herokuで作成したアプリを紐つける。
まずは、my-project内で、Gitを初期化する。

```
git init
```
本当に初期化されたかどうか以下を入力し確認する。（.gitが表示されていればOK）

```
ls -a
```

Gitを初期化することで、ディレクトリmy-projedt内の変更履歴を管理できるようになった。
次にGitとHerokuを紐つける。

```
heroku git:remote -a sample-noonnoon
```

こうすることでローカル上のアプリとHerokuのアプリが紐つく。

```
set git remote heroku to https://git.heroku.com/sample-noonnoon.git
```


[Heroku、Flask、Python、Gitでアップロードする方法（その③）](https://qiita.com/drafts/50d04013bf74ba270960/edit?resume=true)
コードヘルス - ワールドワイドウェブブログポスト識別名

*この文書は[Code Health: IdentifierNamingPostForWorldWideWebBlog](https://testing.googleblog.com/2017/10/code-health-identifiernamingpostforworl.html)の翻訳です。*

長い識別子を調子にのって作るのは簡単だ。長い名前は可読性を改善する。しかし、*あまりに* **長い名前は可読性を下げてしまう。** GitHubなどを見ると60文字以上の変数名の例は数多く見つかる。 **ここで、58文字で俳句を詠んでみよう:**

> Name variables　　　　　　　　　名前付け
> Using these simple guidelines　　　シンプルガイドで
> Beautiful source code　　　　　　　きれいなコード　（字余り[^1]）

**名前は2つのことを守らなくてはならない：明瞭さ（それが何を表すのか）と正確さ（それが何を表さないのか）。** ここで判断の助けとなる、いくつかのガイドラインを示す：

- **変数の型宣言から明らかな単語を除外**

```java
// 悪い、型によってそれが何なのか分かる:
String nameString;
List<datetime> holidayDateList;

// よい:
String name;
List<datetime> holiday;
```

- **重要でない詳細を除外**

```java
// 悪い、過度に具体的な名前は読みにくい:
Monster finalBattleMostDangerousBossMonster;
Payments nonTypicalMonthlyPayments;

// よい、区別しなくてはならない他のモンスターや支払いがないなら:
Monster boss;
Payments payments;
```

- **周辺の文脈から明らかな単語を除外**

```java
// 悪い、文脈の繰り返し:
class AnnualHolidaySale {
  int annualSaleRebate;
  boolean promoteHolidaySale() {...}
}

// よい:
class AnnualHolidaySale {
  int rebate;
  boolean promote() {...}
}
```

- **どんな識別子にも適用できる語を除外**

思い当たりはあるだろう: data, state, amount, number, value, manager, engine, object, entity, instance, helper, util, broker, metadata, process, handle, context. これらは除こう。

これらの規則にも例外はある; 君の判断だ。長過ぎる名前は短すぎる名前よりはなおよい。しかしながら、**これらの規則に従えば、君のコードは曖昧さもなく、より読みやすいものになる。** 読者（未来の君も含めて）はきっとなんて君のコードが明快なんだと感謝するだろう！


[^1]: 編集リクエストお待ちしております
Heroku、Flask、Python、Gitでアップロードする方法（その①）

#Heroku、Flask、Pythonについて、Progateやドットインストール、Qiita等でひととおり学習したので、まとめてみる
（１）作業ディレクトリを作成
（２）仮想環境を設定
（３）必要なフレームワークとWEBサーバーをインストール
（４）FlaskファイルをPythonで作成
（５）Flaskファイルを実行してみる
[（６）Herokuにデプロイする
　　①Herokuにアカウント登録する
　　②HomebrewでHerokuをインストールする
　　③Gitを用意する
　　④Herokuにアプリを作成する
　　⑤ローカルアプリとHerokuアプリを紐つける](https://qiita.com/drafts/a7ea7c7a12c56ab8e901/edit)
　　[⑥Herokuへのデプロイに必要なファイルを作成する
　　⑦Herokuへデプロイする](https://qiita.com/drafts/50d04013bf74ba270960/edit?resume=true)
[（７）エラー対応
　　①heroku ps:scale web=1で正常になるケース
　　②heroku ps:scale web=1で正常にならないケース
（８）ファイルを更新する](https://qiita.com/drafts/0352898c40830547015f/edit?resume=true)





#（１）作業ディレクトリを作成
デスクトップ上でいつも作業してるので、今回もデスクトップ上にディレクトリを設置した。
まず、Macのターミナルを起動して、cd desktop と入力、次にmkdir myprojectと入力、これで、デスクトップ上に、ディレクトリmy-projectを作成。


#（２）仮想環境を設定
次に ターミナルで、cd myproject とし、ディレクトリmy-project内に移動。
Pythonのvenvという仮想環境を提供する機能を使ってローカルに開発環境を設定する。
*仮想環境は、python -m venv ●●●と設定。●●●は任意の名称で良いが、任意のサブディレクトリが作成され、サブディレクトリ内に仮想環境ファイルが作成される。ただし今回は、python -m venv .  としてmyproject直下に仮想環境ファイルを作成した。サブディレクトリ下に仮想環境ファイルを作成すると、後述するHerokuへのデプロイ時になぜかエラーが発生したため、直下に作成した。*

```
python3 -m venv .
```

その後、仮想環境を有効化するために以下のように入力する。

```
source bin/activate
```

ちなみに仮想環境を無効化する場合は、以下のように入力する。

```
deactivate
```


#（３）必要なフレームワークとWEBサーバーをインストール
次に、フレームワークをインストールする。

```
pip install flask
```

それと、WEBサーバーをインストールする。

```
pip install gunicorn
```


#（４）FlaskファイルをPythonで作成
有効化した仮想環境上で、以下のように入力してmy-project内に実行ファイルを作成。今回はhello.pyというファイルを作成。

```
touch hello.py
```

次にhello.pyを開き、以下のように入力する。
なお、app.run()はこのままだとapp.run(debug=false)となり、デバックモードはオフの状態。本番環境ではdebug=trueとすると危険ぽいのでデフォルトのままとした。
詳しくは以下を参考にした。
 https://www.subarunari.com/entry/2018/03/10/いまさらながら_Flask_についてまとめる_〜Debugger〜

```python:hello.py
# -*- coding: utf-8 -*-
# 日本語を使う場合にコメントアウトでも必要

# flaskなどの必要なライブラリをインポート
from flask import Flask

# 自分の名称を app という名前でインスタンス化
app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'

#app.run()はデフォルトではfalseが設定されている
if __name__ == '__main__':
    app.run()
```

#（５）Flaskファイルを実行してみる
再びターミナルで操作する。
作成したhello.pyがちゃんと動くかどうかローカル環境で試してみる。
ターミナルで以下を入力。

```
python3 hello.py flask run
```

すると、以下のように表示される。

```
* Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - 
```

ブラウザで、上記の部分の　http://127.0.0.1:5000/ にアクセスすると、以下のように表示される。<img width="158" alt="スクリーンショット 2020-02-26 22.38.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29856/b05759f2-fd74-84d6-6d58-7af30068fd9c.png">

これでローカル環境では正常に動くことが確認できた。
control + c でターミナル入力モードに戻る。


[Heroku、Flask、Python、Gitでアップロードする方法（その②）](https://qiita.com/drafts/a7ea7c7a12c56ab8e901/edit?resume=true)
文系ガチの未経験エンジニアだった僕が自力4ヶ月弱で自社開発のweb系会社で新卒内定をもらうために行ったこと

#誰に読んで欲しいか
・文系新卒未経験の方
・web系の会社で働きたい方
・フリーランスでアソビタィ！！

偉そうに書きますがご容赦ください><
右に目次があるので興味あるとこのみピックアップできます!!

#なぜ書くのか
実際に自分がエンジニアになりたいと思った時に情報が全くなかったから。
**何をしなければいけないかが全くわからなかった。**

web系で就職した人や未経験理系卒の方の情報は多少あれど文系未経験新卒でweb系エンジニアになった人の情報は全くなかったため。しかし文系卒のエンジニア志望は増えて来ていてニーズはかなりあると考えた。

#自己紹介
早稲田大学文系3年生。
長所は考えた上で実際に行動にすぐに移せること。
大学生時代は酒飲み、遊び、そして**オーストラリアとニュージーランドでワーキングホリデーを行い、ガチガチの現地の環境に身を置いて生活をした。**

####なぜエンジニアになりたいか
２年生までは「MacBook=レポートを作成できる優れモノ」だったが、
**ワーホリで初めてキャッシュレスというテクノロジーに触れて衝撃を受け、生活の質を劇的に変えることのできるITに興味を少し持つ。**
**誰かの役に立つものを作ること。喜んでもらうこと。びっくりさせることが好きだから。**
**あとは成長が積み上げ式でわかる。**営業を２年間やっていたが状況によって結果が大きく異なる。エンジニアのスキルは昔のコード見ると明らかに質が低いのがわかる。(四ヶ月目までの今の時期だけかもしれないが)

#自分が行ったこと(具体的に)
####３年生の夏
コンサル目指してサマーインターンを応募しまくるも落選落選。
→自己分析を重ねてどう考えても仕事よりノマドになって海外で自由に働きたいと強い決意。

####10月前半
・週３働いていたマーケのインターンを辞め、飲み会などを減らし、未来への投資として勉強する。
・**実際に[progate](https://prog-8.com/dashboard)という初心者向けの学習サイトで月額1000円払ってHTML,CSSを２週ほどした。**
　→一周2日あれば余裕
　→月額1000円はスクールと比較すればどう考えても格安
**・その後実際に自分の作ってみたいサイトを5つほどアウトプットしてみる**
**・その後同じものでJavaScriptを学び、難しかったので5週ほどした。**
####10月後半
**・インターンを視野にいれて重役のいる大手コンサルでの長期インターンの条件としてあげられた制作物の作成に励む。**
　・課題の内容としてはとあるチャットアプリのAPIを使ったアプリ内メッセージ検索webアプリ
　・AWSを使ったデプロイまで行うよう指示される
　・文系なのでエンジニアの友人もいなかったため小学校の時の友人、MatcherというOB訪問アプリで 出会った社会人の方、前職場のエンジニアの方に再コンタクト
　・正直かなり大変でエラー１つに数日かけることもしばしば
・Vue.jsというjavascript(js)の指定が確かあったため、**[Udemy](https://www.udemy.com/ja/)というオンライン学習用動画を販売するサイトを利用し、1000円で購入し、課題と同時並行で勉強した。**
####11月後半
課題完成し、OKが出たが人数の関係で引き伸ばされた。
→しょぼいですがもしお時間あれば見てみてください..まだまだ改良中なのでお手柔らかに..
[slackのメッセージ検索アプリ](http://52.192.253.204/index.html)
→今はec2インスタンスを停止しているので公開していません( ;∀;)

早く実務経験を積みたかったため、諦めて自分でインターンを探し始める。
**この時使ったものはMatcher,キャリアバイト,Infraというサービスたち。**
**まずエンジニアとしての働き方などベースを知るためにMatcherで20人ほど文系未経験卒のエンジニアの方中心に話を聞いた。**
####12月
その後早速実務未経験でも働けそうなインターンに応募。面接させてくれた企業は3つ。
１つ目の企業には面接中に断られる。

**ここでフィードバックをお願いして教えてもらったこと
・普通に企業の目線で考えるとエンジニアを雇う意味は２つ。
　❶即戦力で企業に利益をもたらすため
　❷就職するかもしれないため将来を見越した研修込みの投資として**
今このページを読んでいる人はおそらく❷を狙うしかない。
だから自分はそれ以降就職してもいい会社しか応募せず、面接も本気で就活のレベルで受けた。
####12月後半
ゲームメディアを運営する企業で採用された。フロントでの努力を評価されバックエンドのPHPを使ったエンジニアとして研修込みでの採用。
**同時に[レバテック](https://career.levtech.jp/beginner/)という人材サービスを利用し就活を視野に入れて自己分析をもっと極める。**
####1月
長期インターン開始。
後半からは面接も平日夜に毎日受ける。
**→フィードバックを最大限に生かしてPDCA回して面接の受けが良くなっていく。**
####2月
ついに複数の企業から内定をいただく。
現在まだ一応他の企業も見ている。

すいません。日記のような形になりました。

#まとめ
**就職までの時間を考えて何をすべきか逆算していくことが大事。**
正直自分は実務経験ないと就職できないと思ってインターンを必死に探したが、しなくてももしかしたらよかったかもしれない。
しかしインターンをして就活しながらも現在進行形で技術に触れていることをアピールできたのは大きかった所感。

**インターンを受けるにあたって大事なことは
ちゃんと就職してもいい企業で受けると互いに良くて面接ももちろん通る。**

**就活では
・しっかりエンジニアになりたい理由を固めることは大前提必要。**

**・その上でそのために実際にこんなもの作って、インターンもやっている(何かしら活動している)から私はこれからももちろんエンジニアでゴリゴリ開発していきますよ？だって書くの好きだもん！ということを伝えることが絶対に必要。**

おそらく面接官が心配していることは、
**❶エンジニアなりたいのはわかったけど続けれるのか。文系やめる人多いけど。
❷成長は早くて遅い経験有りのエンジニアを雇うよりも将来的に利益になるのか。**
ということ。
あとは文系ならでは(?)のコミュニケーション能力を面接で抜群に発揮して内定をもぎ取りましょう！！


#感想
初めてのqiita投稿でした。
ずっと前から自分が情報なくて困っていたのでこういったことを内定もらったらすぐにしたいと考えていて、それが実行できたので嬉しいです。

次回は初心者から見た簡単なIT業界やエンジニアの中の詳しい職種の特徴を発信したいと思います。
もちろんコーディングの知識もアウトプットしたいと考えてます。

**お手柔らかに、意見やコメントもらえると嬉しいです！**
ありがとうございました。
aws-vault の登録内容から AWS CLI 設定ファイルを作成する

[AWS CLI v2とaws-vaultとpecoを使ってプロファイルを選択方式にする](https://qiita.com/Ricordanza/items/fb873407b57089300748) で書いた、aws-vault の登録内容から AWS CLI 設定ファイル (~/.aws/config) を作成する shell を作ってみました。

```aws_config_maker.sh
#!/bin/sh

# Variables
readonly TARGET_HOME=$HOME/.aws
readonly TARGET_FILE=$TARGET_HOME/config
readonly REGION=ap-northeast-1
readonly OUTPUT=json

# Initialize .aws/config
if [ ! -e "$TARGET_HOME" ]; then
  echo Create a config form folder.
  mkdir -p "$TARGET_HOME"
fi

# Back up .aws/config
if [ -e "$TARGET_FILE" ]; then
  echo Back up config.
  cp -p "$TARGET_FILE" "$TARGET_FILE".`date "+%Y%m%d%H%M%S"`
fi

# Create default profile
echo Register default settings.
cat <<EOF > "$TARGET_FILE"
[default]
credential_process=/PATH/TO/credential-selector.sh
region=$REGION
output=$OUTPUT
EOF

# Added settings for each credential
for credential in ` aws-vault ls | awk 'NR>2 {if ($2 != "-") print $2}'`
do
  echo Register profile [$credential].
  cat <<EOF >> "$TARGET_FILE"

[profile $credential]
credential_process=aws-vault exec -j $credential --no-session
region=$REGION
output=$OUTPUT
EOF

done
```

これを実行すると

```
$ sh ./aws_config_maker.sh
Back up config.
Register default settings.
Register profile [prof1].
Register profile [prof2].
Register profile [prof3].
```

``` ~/.aws/config
[default]
credential_process=/PATH/TO/credential-selector.sh
region=ap-northeast-1
output=json

[profile prof1]
credential_process=aws-vault exec -j prof1 --no-session
region=ap-northeast-1
output=json

[profile prof2]
credential_process=aws-vault exec -j prof2 --no-session
region=ap-northeast-1
output=json

[profile prof3]
credential_process=aws-vault exec -j prof3 --no-session
region=ap-northeast-1
output=json
```

って感じで既にファイルがあればバックアップして aws-vault の登録内容から config を生成します。
 ~/.aws/credentials の方はなんにもしません。

作ってみたものの、かなりの数のクレデンシャルを登録しないと shell の効果を感じることは無さそうな。
新しく利用するドメインやIPアドレスが闇サイトの使いまわしでないことを確認するには

# 概要
新しくウェブサイトを立ち上げたりメールサービスを利用開始する場合に必ず必要になるのが**独自ドメイン**や**IPアドレス**です。インターネットが一般的になってから既に25年が経過して、[IPv4アドレスは枯渇](https://ja.wikipedia.org/wiki/IP%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E6%9E%AF%E6%B8%87%E5%95%8F%E9%A1%8C)していて、多くの人が取得したい人気のある独自ドメインも、誰かの使いまわしである可能性が高いです。

そのために、新しく取得した独自ドメインやIPアドレスを使ってウェブサイトやメールを使い始めたときに、過去に闇サイトやスパム送信で使われていて、セキュリティソフトからブロックされてしまう、といったリスクもあります。

セキュリティソフトで引っかかった例:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490819/4607d4d2-765a-08d6-e52c-d3f7cb131128.png)

そのため、独自ドメインやIPアドレスを取得する際に、以下の手順を踏んでレピュテーション (評判)を確認しておくことをお勧めします。レピュテーションが悪い場合、取得をやめて別のものにするか、ブロックリスト解除申請を行いましょう。

# 主要なパブリック/ベンダーブロックリストの確認

以下の主要なパブリックリスト、セキュリティベンダーのブロックリストで、対象のドメインやIPアドレスをチェックしてみましょう。もしブロックリストに載ってしまっている場合は、解除申請を出してみましょう。


|リスト名|登録情報|URL|
|:--|:--|:--|
|Barracuda|ドメイン, IPアドレス|http://www.barracudacentral.org/lookups|
|DNSBL|IPアドレス|http://dnsbllookup.com|
|McAfee|ドメイン|https://www.trustedsource.org/|
|MultiRBL|ドメイン, IPアドレス|http://multirbl.valli.org/|
|Norton|ドメイン|https://safeweb.norton.com/|
|Proofpoint|IPアドレス|https://ipcheck.proofpoint.com/|
|Sophos|IPアドレス|https://www.sophos.com/ja-jp/labs.aspx|
|Spamhaus|ドメイン, IPアドレス|https://www.spamhaus.org/lookup/|
|Symantec|IPアドレス|https://ipremoval.sms.symantec.com/|
|Trend Micro|IPアドレス|https://www.ers.trendmicro.com/reputations|

# プロモーションメールを送る場合の注意事項
メールサービスを立ち上げて、自社の顧客などにプロモーションメールを送る場合、受け取った顧客がセキュリティソフトを通して迷惑メールであるという判定をこれらのベンダーや団体に送る可能性があります。その時に、プロモーションメールを送るドメインと、自組織の従業員が同じドメインを使っている場合、自社の従業員が送る通常のメールも外部の受信者に届く前にセキュリティソフトにスパム判定されてブロックされてしまうリスクが上がってしまいます。このため、**プロモーションメールを送る送信元ドメインと、従業員が利用するメールドメインは必ず別のものを使いましょう。**

# 参考記事
ブロックリスト (ブラックリスト)の技術的なしくみについては以下に詳しいことが載っていますので、参考にしてください。

* [ブラックリスト(DNSBL)の仕組みと掲載を防ぐ方法](https://qiita.com/nakansuke/items/c014678e69fe5d801419) by @nakansuke さん
Microsoft Teamsで使えるQualityForward Botを作る

[Microsoft Teams](https://products.office.com/ja-jp/microsoft-teams/group-chat-software/)はMicrosoftの提供するチャットツールです。ビジネス界隈で利用が広がっています。他のチャットサービスと同様に、他社のサービスと簡単に連携できます。今回はMicrosoft Teamsと連携できるQualityForward用ボットを作成してみました。

## 利用するAPIについて

Microsoft Teamsのボットは5秒以内にレスポンスを返さないといけないという縛りがあります。Web APIを何度も叩いていると5秒以内にレスポンスが返せない可能性があるので、以前作成したGoogle Apps Script上で動作するWeb APIを利用します。

[QualityForwardのテスト集計を返すSlackコマンドを作ってみた - Qiita](https://qiita.com/goofmint/items/f33fea50b8280205013d)

このWeb APIであればGoogle Apps Scriptを呼び出すだけなので処理は高速になります。ただし、Google Apps Scriptのリダイレクトする仕組みではMicrosoft Teamsのボットがうまくレスポンスを受け取れませんでした。そのため、直接Google Apps Scriptを呼び出すのではなく、Node.jsサーバ（今回は[Glitch](https://glitch.com/)を利用）から呼び出す方式にしています。

## ボットについて

Microsoft Teamsのボットは簡単に作成できます。今回はWebhookを利用しています。Webhookはあらかじめ作成したボットに対してメッセージを送ると、指定したURLへPOSTメソッドを投げてくれます。レスポンスで返したJSONをボットからの返信として表示してくれます。

![Screenshot_ 2020-02-27 9.41.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/95ebe4fa-ab91-bcc0-a157-6570164c7036.png)


## コードについて

Node.js（Express利用）のコードは次のようになります。Google Apps Script上で作成しているAPIを実行し、そのレスポンスを返しているだけです。

```js
const express = require("express");
const app = express();
// POSTのボディ解析用
app.use(express.json())
app.use(express.urlencoded({ extended: true }));

// HTTPクライアントw追加
const client = require('superagent');

app.use(express.static("public"));
app.get("/", (request, response) => {
  response.sendFile(__dirname + "/views/index.html");
});

// ボットからデータを受け取る処理
app.post("/post", async (request, response) => {
  // メッセージ内にある日付部分を取得
  const date = request.body.text.replace(/^.*?<\/at>(.*)\n.*/s, "$1");
  // Google Apps ScriptのAPIを呼び出し
  const url = `https://script.google.com/macros/s/AKf...IGt/exec?text=${date}`;
  const res = await client.get(url)
  // レスポンスを返す
  response.json({
    "type": "message",
    "text": res.text
  });
});

const listener = app.listen(process.env.PORT, () => {
  console.log("Your app is listening on port " + listener.address().port);
});
```

これでボットに対して日付を送ると、その日付における処理件数を返してくれるようになりました。

![Screenshot_ 2020-02-27 10.31.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/652f50f2-1778-4202-368f-13ddfeac467e.png)


## まとめ

今回の仕組みは自前のサーバを立てることなく、サーバレスで構築しています。GlitchもGoogle Apps Scriptも安定していますので、メンテナンス不要で動作できる仕組みは便利です。Microsoft Teamsで情報共有しているプロジェクトでは、この仕組みを試してみてください。

[QualityForward](http://bit.ly/399yB70)
QualityForward APIを拡張して検索可能にする

[QualityForward](http://bit.ly/2uCLRSR)はクラウドベースのテスト管理サービスです。APIを公開していますが、絞り込み条件などは付けられず、すべてのデータが返ってきます。これでは使いづらい部分もあると思いますので、絞り込みできる機能を追加してみました。

## APIのアクセスポイント

APIのエンドポイントは下記になります。

```
https://script.google.com/macros/s/AKfycbzb7QkA-EbdynKorbaAqi3pxD8Ue-91w4jrRW1oxob5AGDXxqaN/exec
```

Google Apps ScriptではQualityForward APIを呼び出しており、その結果を条件指定に従ってフィルタリングしています。

## 利用例

例えば下記はテストケースのID=1000で絞り込む場合です。apiKeyはプロジェクト毎に発行されるAPIキーです。

```
https://script.google.com/macros/s/AKfycbzb7QkA-EbdynKorbaAqi3pxD8Ue-91w4jrRW1oxob5AGDXxqaN/exec?model=TestPhase&where=%5Bobject%20Object%5D&apiKey=AAA
```

## 利用できるオペランド

```json
{
  "カラム名": {
    "オペランド": "値"
  }
}
```

のようにして検索条件を指定できます。オペランドは下記を用意しています。

- **$ne**  
等しくない
- **$lt**  
未満
- **$lte**  
以下
- **$gt**  
より大きい
- **$gte**  
以上
- **$in**  
指定した値が含まれる
- **$nin**  
指定した値が含まれない
- **$regex**  
正規表現

イコールの場合は下記のようになります。

```
{
  "カラム系": "値"
}
```

## 実装例

下記はNode.jsで実装した場合の例です。idが3096以上、そして3100未満のデータを検索しています。

```javascript
const request = require('superagent');

(async () => {
  const url = 'https://script.google.com/macros/s/AKfycbzb7QkA-EbdynKorbaAqi3pxD8Ue-91w4jrRW1oxob5AGDXxqaN/exec';
  const apiKey = '0aa...f340';
  const params = {
    apiKey: apiKey,
    model: 'TestPhase',
    where: JSON.stringify({
      id: {'$gte': 3096, '$lt': 3100}
    })
  };
  
  const res = await request
    .get(url)
    .query(params)
    .send();
  console.log(res.body);
})();
```

これで絞り込まれたデータのみ取得できます。

## まとめ

現在はページネーションに対応していないので、大量のデータがあった時のレスポンスや処理時間は考慮されていません。QualityForwardのGoogle Apps Script用SDKを使えば簡単に実装できますので、ぜひお試しください。

[QualityForward](http://bit.ly/2uCLRSR)
無名関数とはなんぞや

##そもそも関数とは
プログラム上での処理を1つのブロックにまとめたもの。
例えば引数として受け取った数値を二乗して返す関数があれば、関数の呼び出し元には戻り値として引数を二乗した値が帰ってくるわけです！javascriptでは以下のように書きます。「nijo」が関数になるわけですね。

```js
result = nijo(2) //関数を呼び出して返り値をresultに代入

function nijo(i){
  //処理内容は中略
}
```

##無名関数
上記を踏まえた上で、無名関数について解説します。簡単に言えば名前を決めずに定義できる関数です。

```js
function(){
  //処理内容は中略  
}
```

先ほど記載した引数を二乗する関数には、関数名として「nijo」という名前がついていました。
しかし名前を決めずに関数を定義することもできるのです。

##無名関数を使うことによるメリット
setTimeout関数を例にして、無名関数を使うメリットを見ていきましょう。
ちなみにsetTimeout関数の仕様は以下の通りです。
**setTimeout(処理内容,実行タイミング)**

```js
setTimeout(function(){
  var day = new Date();
  alert("今日は" + day + "です");
}, 2000)
```
この処理をHTMLに埋め込んでブラウザで開けば、2秒後に今日の日付が表示されるわけです。
引数の処理内容の部分に無名関数を使っていますね！
処理内容として渡される関数[^1]についてはここでしか呼ばれることがありません。
なので、関数に名前をつける必要がないのです！
ちょっと回りくどくなりましたが、以下のようなメリットがあります。
・関数名を決めなくてもいい処理の場合、関数の名前を考える手間を省ける。
・他の関数と名前が被ることが絶対にないので、予期せぬ動作やエラーの対応をしなくてもいい。

##まとめ
無名関数を使いこなして効率的なコーディングを行いましょう！


[^1]: これをコールバック関数といいます。
[Python]turtleによる渦巻模様の描画

今回は、Pythonにて渦巻模様（曲線ではありませんが）を描画する方法を紹介していきたいと思います。

##タートルグラフィクス

直線や曲線を描画する時に非常に便利なツールの一つに、「タートルグラフィクス」があります。
これは子供向けの教材として開発されたものなので、非常に扱いやすく、感覚的に理解もし易く設計されています。

Pythonには標準でこのタートルグラフィクスを扱うためのturtleライブラリが用意されています。

##ソースコード

```python

import turtle

t = turtle.Turtle()

l = 200    #辺の長さの初期値
angle = 90 #直線を引き終わった後に方向転換する角度
step = 10  #辺の長さの減少値

while l > 10:     #lが10より大きい間は繰り返す
    t.forward(l)  #lだけ直進
    t.left(angle) #左に90度回転
    l -= step     #lを10だけ減らす
```

このコードを実行すると、以下のような渦巻模様が描かれます。

![bandicam 2020-02-27 21-31-21-411.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240459/5d9221c5-86a4-e663-1f13-c5549c3f765e.jpeg)


##まとめ

今回はタートルグラフィクスを用いて渦巻模様を描くコードを紹介しました。

他にもタートルグラフィクスを用いると様々な図形を描画することができます。
是非試してみてください。


[note](https://note.com/sei8inf)
プログラミング初学者のためのGithub用語解説⭐︎

## プログラミング必須タスク！Githubについて解説

- [x] 基本的な用語解説



プログラミング初学者の多くが学ぶ `Github` について解説していきます！！

分かりづらい点があればコメントください！

---------


### リポジトリ(Repository)
 VSコードなどの変更内容履歴などが格納されている場所 (貯蔵庫のようなもの)

### リモートリポジトリ(Remoterepository)

サーバー上にあるリポジトリ。ユーザー間で共有できる

### ローカルリポシトリ(Localrepository)

自分のPCにあるリポジトリ。

### ブランチ(Branch)

履歴管理を枝分かれさせたもの。各ユーザが自由に作成できる`枝`のようなもの
### コミット(Commit)

ローカルリポジトリに変更内容を保存すること。

### プル(Pull)

ローカルリポジトリの変更をリモートリポジトリに反映させること。

### フェッチ(Fetch)

リモートリポジトリの内容をローカルリポジトリに反映させること。

### コンフリクト(Conflict)

衝突。ユーザーが同じファイルの変更を反映させた時などに起こるアクシデントのようなもの。

### クローン(Clone)

リモートリポジトリをコピーしてローカルリポジトリを作成すること `git clone`

### プルリクエスト(Pullrequest)

編集リクエスト。ローカルリポジトリで変更した内容をユーザーにレビューしてもらう時に使う。



-------------------

簡単な用語ではこれぐらい！これからプログラミングを始める方は必ず覚えておいて損はない用語なのでぜひ覚えておきましょう！！




以上！
【RefreshToken】Vue.jsでAccessTokenのリフレッシュを行う共通処理【リフレッシュトークン】

## はじめに
APIリクエストの際にアクセストークンの有効期限が切れていた場合に、トークンのリフレッシュを行い再度本来のAPIリクエストを行う処理のサンプルです。

## 前提
トークンの保持はcookieで行っています。トークンの発行はflask_jwt_extendedというライブラリを使用しています。サーバーサイドの仕様によってリクエストは適宜変更してください。
axiosのバージョンは0.18.X系です。0.19.X系は共通処理に記述したwithCredentialsがうまく動作しないバグがあるみたいです。

App.vueで下記のaxios.js読み込んで使用します。


## コード
```js:axios.js
import Axios from 'axios'
import Vue from 'vue'
 
const http = Axios.create({
    // 共通定義
    baseURL: 'http://localhost:5000/api/v1',
    withCredentials: true,
    headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest"
    }
})
 
http.interceptors.response.use((response) => {
    // APIのリクエストに成功した場合はそのまま結果を返す
    return response;
}, error => {
    // エラーかつトークン切れ（401）の場合
    if (error.config && error.response && error.response.status === 401) {
        // トークンリフレッシュのエンドポイントにリクエスト
        http
            .post(
                "/token/refresh",
                {},
                {
                    withCredentials: true,
                    headers: {
                        // flask_jwt_extendedの仕様
                        // CSRF対策。サーバサイドの実装によっては不要
                        "X-CSRF-TOKEN": Vue.$cookies.get("csrf_refresh_token")
                    }
                }
            )
            .then(res => {
                if (res.status == 200) {
                    // トークンの更新に成功
                    console.log("success token refresh");
                    const config = error.config;
                    // flask_jwt_extendedの仕様
                    // CSRF対策。新しく取得したアクセストークンに対応するCSRF対策文字列を設定。
                    // Authorizationヘッダーにトークンをセットする仕様の場合はここでトークンセット。
                    // 例：config.headers["Authorization"] = "bearer" + 取得したトークン
                    config.headers["X-CSRF-TOKEN"] = Vue.$cookies.get("csrf_access_token");
                    // 本来行いたかったAPIリクエストを再実行
                    return Axios.request(error.config);
                } else {
                    // トークンの更新に失敗した場合
                    console.log("failure token refresh");
                    // 現在の画面のパスをqueryにbackuriとしてセット。
                    // ログイン時にbackuriが存在していたらその画面に戻るように実装している場合のみセット。
                    // ただログイン画面に戻るだけならqueryは不要。
                    Vue.$router.push({
                        path: "login",
                        query: { backuri: Vue.$route.path }
                    });
                }
            })
            .catch(error => {
                console.log("failure token refresh");
                // エラーの場合もログイン画面に戻る
                Vue.$router.push({
                    path: "login",
                    query: { backuri: Vue.$route.path }
                });
            });
    }
    // エラー終了時にPromiseを返す
    return Promise.reject(error);
});
export default http
```

## 追記
7/14 改良版のソースがこちらにあります。
https://qiita.com/nqyutq/items/32fbcb56218594f4a341#%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E3%83%AA%E3%83%95%E3%83%AC%E3%83%83%E3%82%B7%E3%83%A5%E6%96%B9%E6%B3%95
[Python] VScodeのデバッグコンソールでデータフレームを表示

# 課題
* データフレームをいじくり回す処理のデバッグをサクサクやりたい
* pdbデバッグもいいけど、ソースを編集するのが嫌

# 前提
VScodeが好き

# 解決方法概要（目次）
VScodeにて
1. ブレークポイントを置いて
2. デバッグモードで実行し
3. デバッグコンソールを使う
4. データフレームを表示させるために、launch.jsonに一行追記

# 解決方法詳細
## ブレークポイントを置く
行番号の左にマウスカーソルを持っていき、クリックして赤丸をつける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/c8693d1f-4ee5-7b72-1266-7e6cb87847c6.png)


## デバッグモードで実行
1. 虫っぽいマークをクリックしてデバッグ画面にいき
2. デバッグ用の実行ボタンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/24700a0a-85b3-844e-b954-d1fb11872871.png)


## デバッグコンソールを使う
デバッグコンソール画面に移動し、変数名を入れてエンターキー押したりして、色々確認する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/6b3383fd-d87f-f4fd-3bdf-07a8995c7120.png)
<font color='red'> データフレームが上手く表示されなくて困る</font>

## データフレームを表示させるために、launch.jsonに一行追記
cmd + shift + Pを押してコマンドパレットを開き、
「launch」と入力し、
launch.jsonを開くをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/7fbd36e4-8446-da14-cef6-aae9442a9ff7.png)

「"redirectOutput": true」を追記して上書き保存（直前行の最後に「,」も忘れず）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/013a0e35-1bcf-21be-b4b9-6cb72bc6929c.png)

一度デバッグを終了して再度デバッグを開始。
デバッグコンソールでprint(df)をやってみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/f80b4126-ffd0-2a4a-42f2-a285304afabe.png)
<font color='blue'>データフレームが見やすく整列して表示された</font>

※なぜこれで上手くいくのかは分かってない。ググって↓に辿り着いて真似た。
https://github.com/microsoft/ptvsd/issues/2036#issuecomment-573343490

# おまけ）デバッグコンソールで複数行の処理
sht+Enterで複数行の処理も書ける
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/a3d04c63-2c0a-1cb2-50cc-bbc92c1f647b.png)

↓
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/d49c93f5-389e-9b74-9e06-be70bd92f39e.png)


# 補足
動作環境： Mac、Python3.7

デバッグ対象としたサンプルコード：

```python
# create test data
a = 15

import pandas as pd
df = pd.DataFrame(
    columns = ['name', 'gender', 'age'], 
    data = [
        ['john', 'man', 25],
        ['yoko', 'woman', 28],
        ['kevin', 'man', 45]
    ]
)

# processing
a = a - 1
df['age'] = df['age'] - 10
```

おまけのデバッグコンソールで使ったコード：

```python
for age_hosei in [10, -10]:
    print('--------------------------')
    print('age_hosei', age_hosei)

    tmp = df.copy()
    print('tmp:\n', tmp)
    
    tmp['age'] = tmp['age'] + age_hosei
    result = tmp.groupby('gender')['age'].sum()
    print('result:\n', result)
```
npm使用時の「:  command not found」について

プロジェクトはじめるときの備忘録です。

```sh
$ cd MyProject
```

```sh
$ eslint --init
-bash: eslint: command not found
```

```sh
$ webpack -v
-bash: webpack: command not found
```

```sh
$ gulp -v
-bash: gulp: command not found
```

たまに`$ eslint --init`やれば`.eslintrc`を初期設定できるみたいな記事があって、実際やってみたら「できないじゃん！」みたいな事になり
余計に調べ始めたりする事があるんですが、調べていくと「グローバルにインストールしましょう。」っていう案内にたどり着く。

「初期化だけのためにグローバルインストールするのも面倒だな〜」と思ったりします。

プロジェクトの中の`node_modules`って、グローバルインストールしてるわけではないので、
プロジェクトのディレクトリに移動しただけでは実行できないみたいです。

ちょっとバージョンを調べたり、初期化したりする時に「あれっ？あれっ？」ってなる事ありませんかね？
グローバルにインストールしてたりしてなかったり、混乱しますよね。

それの解決法です。

##結論：ちゃんとパスを書いてあげる

```sh
$ ./node_modules/.bin/eslint --init
```

```
$ ./node_modules/.bin/webpack -v
4.41.6
```

```
$ ./node_modules/.bin/gulp -v
4.0.2
```

`.bin`の中に名前があって、npmコマンドで実行できる物に限ります。
`.bin`の中になかったら、多分グローバルインストールがオススメなのだろう、と思います。

↓ （追記）長い場合は「npx」と書いても大丈夫のようです。

```
$ npx eslint --init
$ npx webpack -v
$ npx gulp -v
```

##ちなみにプラグインのバージョンを調べたいだけだったら
`package.json`を開いた方が早いかもですが

```
$ npm list --depth=0
$ npm list --depth=0 -g
```
これで調べられます。

##参考記事
eslint --initコマンドが見つかりません
https://github.com/eslint/eslint/issues/10192
【Java】あるディレクトリの下のファイル全てに対して処理を行う

別に目新しくもなんともないのですが、なんとなく投稿してみます。

掲題のような処理を行うには、Java7で追加されてたFiles#walkFileTree()がよさそうです。

```WalkFileTreeSample.java
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.io.*;
 
public class WalkFileTreeSample {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get("/Users/daiki/go");
 
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    System.out.println(file);
                    return FileVisitResult.CONTINUE;
                }
            });
    }
}
```
SimpleFileVisitorは普通のクラスであり、関数型インタフェースじゃないのでラムダ式は使えません。
上記ではファイルごとに呼び出されるメソッドのみをオーバーライドしてますが、他にもディレクトリを訪問する前に呼び出されるメソッド、後に呼び出されるメソッドなどがあり、オーバーライドすることで処理を挟むことができます。

別の例として、Java8で追加されてたFiles#walk()を使うこともできます。こちらはStream APIを使います。上のと結果を合わせるため、ディレクトリは除外しています。ちょっと気持ち悪い？

```WalkSample.java
import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;

public class WalkSample {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get("/path/to/target");
        try(Stream<Path> paths = Files.walk(start)) {
            paths.filter(path -> !Files.isDirectory(path))
                .forEach(System.out::println);
        }
    }
}
```

Java6以前であれば、再帰を使って自分で書くことになりますかね。ただファイル名を出力するだけなら、特に難しいことはありませんが。

```WalkWithoutLibrarySample.java
import java.io.File;

public class WalkWithoutLibrarySample {
    public static void main(String[] args) {
        File start = new File("/path/to/target");
        walk(start);
    }

    private static void walk(File dir) {
        for(File file : dir.listFiles()) {
            if(file.isFile()) {
                System.out.println(file);
            } else {
                walk(file);
            }
        }
    }
}
```

# 参考
* https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html
* https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Path.html
* https://docs.oracle.com/javase/jp/7/api/java/nio/file/Files.html
* https://docs.oracle.com/javase/jp/6/api/java/io/File.html
WinFormsのListView.SelectedIndicesが遅い問題を回避する

仮想モードにしたListViewでも、`SelectedIndices`プロパティ(`ListView.SelectedIndexCollection`クラス)で非常に時間がかかるケースがあります。
(この記事では面倒なのでどれくらい時間がかかるかは述べません)

## 遅くならないケース

- `SelectedIndices.Count`   
   内部では`LVM_GETSELECTEDCOUNT`を使って実装されているので高速に取得できます。
- `SelectedIndices[0]`  
  1件目であれば、列挙を打ち切るため遅くはなりません。

## 全選択/全選択解除を早くする

以下のように、ループで`SelectedIndices.Add`/`Remove`でしていくと非常に時間がかかってしまいます。

```cs:NG
for (var i = 0; i < listView1.Items.Count; i++)
{
    listView1.SelectedIndices.Add(i);
}
```


[ListBoxの全項目を高速に選択する](https://qiita.com/mifumi323/items/071be4cd87998ce267f9)と同様に、P/Invokeで全選択するようにします。

こんなのを定義して、

```cs
const int LVM_FIRST = 0x1000;
const int LVM_SETITEMSTATE = LVM_FIRST + 43;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct LVITEM
{
    public int mask;
    public int iItem;
    public int iSubItem;
    public int state;
    public int stateMask;
    [MarshalAs(UnmanagedType.LPTStr)]
    public string pszText;
    public int cchTextMax;
    public int iImage;
    public IntPtr lParam;
    public int iIndent;
    public int iGroupId;
    public int cColumns;
    public IntPtr puColumns;
};
private const int LVIS_SELECTED = 2;

public static void SelectAll(ListView control, bool select)
{
    var lvItem = new LVITEM
    {
        stateMask = LVIS_SELECTED,
        state = select ? LVIS_SELECTED : 0
    };
    SendMessage(new HandleRef(control, control.Handle), LVM_SETITEMSTATE, new UIntPtr(unchecked((uint)-1)), ref lvItem);
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(HandleRef hWnd, int msg, UIntPtr wParam, ref LVITEM lvi);
```

こう使います。

```cs
    // 全選択
    SelectAll(listview1, true);

    // 選択解除
    SelectAll(listview1, false);
```

## 選択行の取得を早くする

`SelectedIndices`に対して列挙すると、[内部では1件目から全部舐めてリストを作る](https://referencesource.microsoft.com/system.windows.forms/R/73dccafa6aea1b00.html)ことになるので、非常に遅くなります。
(`foreach`で回さなくても、`CopyTo`メソッドなども含む)
大きなインデックスでインデックスアクセスしても[同様にその位置まで1件進める](https://referencesource.microsoft.com/system.windows.forms/R/4eaad2f957bb6100.html)のに時間がかかります。

```cs:NG
foreach (int i in listView1.SelectedIndices)
{
    // iを使用した処理
}
```

そのため、`VirtualItemsSelectionRangeChanged`と`ItemSelectionChanged`イベントで選択行を保持しておき、選択行が必要なときはそれを参照するようにします。

```cs
private bool[] _selected = new bool[/*listView1.VirtualListSize分のサイズ*/];
private void listView1_VirtualItemsSelectionRangeChanged(object sender, ListViewVirtualItemsSelectionRangeChangedEventArgs e)
{
    for (var i = e.StartIndex;  i <= e.EndIndex; i++)
    {
        _selected[i] = e.IsSelected;
    }
}
private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    _selected[e.ItemIndex] = e.IsSelected;
}
```

他の方法として、

- C#を諦めてC++/CLIで同等の処理を実装する
- 別途ネイティブDLLを作成してそれをP/Invokeで呼び出す

などもあります。
背景画像のFade-in表示

CSSで背景画像をフェードイン／フェードアウトで画像を切り替えるサンプルを掲載しています。
当ページでは、フェードイン／フェードアウトの速度や動き（遠近/近遠）や表示切り替えの速度などを自由に指定しながら実際のデザインを確認することが出来ます。

生成された「html」「css」などのソースコードは、そのままコピー＆ペーストで利用することが出来ます。

いろいろフェードインを試してみよう
表示時間と変化の大きさ
表示時間	
切り替え時間	
 
背景画像の動き
なし	小→大	大→小
時計回り	横軸回転	縦軸回転
フェード
切り替え
使用したhtml
<div class="box">
  <div class="bgImg src1"></div>
  <div class="bgImg src2"></div>
  <div class="bgImg src3"></div>
  <div class="bgImg src4"></div>
  <div class="boxString">フェード<br>切り替え</div>
</div>
生成したCSS
<style type="text/css">
/* --- 全体のBOX定義 ---------------------------------------- */
.box {
  position   : relative;
  max-width  : 400px;
  height     : 250px;
  margin     : auto;
  overflow   : hidden;
}

/* --- 背景の指定 ------------------------------------------- */
.box .bgImg {
  position   : absolute;
  top        : 0;
  left       : 0;
  bottom     : 0;
  right      : 0;
  opacity    : 0;
  animation  : bgAnime 20s infinite;   /* 4画像 × 各5s = 20s */
}

/* --- 段差で背景画像のアニメーションを実行 ----------------- */
.box .src1 {
  background-image : url(./spring.jpg);   /* 背景の画像を指定 */
}
.box .src2 {
  background-image : url(./summer.jpg);   /* 背景の画像を指定 */
  animation-delay  : 5s;
}
.box .src3 {
  background-image : url(./autumn.jpg);   /* 背景の画像を指定 */
  animation-delay  : 10s;
}
.box .src4 {
  background-image : url(./winter.jpg);   /* 背景の画像を指定 */
  animation-delay  : 15s;
}

@keyframes bgAnime {
   0% { opacity: 0; }
   5% { opacity: 1; }
  25% { opacity: 1; }
  30% { opacity: 0; }
 100% { opacity: 0; }
}

/* --- 前面の文字定義（サンプルのため変更してください） ----- */
.box .boxString{
  position   : absolute;
  display    : inline-block;
  padding    : 20px;
  background : rgba(0, 127, 255, 0.7);          /* 半透明の青 */
  color      : #fff;
  top        : 50%;                               /* 中央寄せ */
  left       : 50%;
  transform  : translate(-50%,-50%);
  z-index    : 11;
}
</style>



https://webparts.cman.jp/box/bgfade/のまんまコピペ
今更ながらWPFに置き換えてみる（4）


多分WPFの開発はXAMLでがっちり記述するのが大前提なんだろーな、と思ってはいるんですが、なんかスカスカ感があってついデザイナ中心で作ってしまいがちです。皆さんはどうなんでしょうか。

#実装予定機能・・・
ということで、
・画面構成はほぼ今のFORMSのVBアプリと同様。
・時報機能あり。時間になるとこんな感じのNOTIFYが表示される

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590512/002b8867-dbb3-f216-0534-2b9216d89f85.png)

・タスクバー内のNotificationも今と同様のバリエーション
・NotifyIconクリックでメイン画面をShowする際に、Screenのエッジにスナップ

新たにこんなことができるかを確認しつつ可能であれば実装
・NotifyIconをデスクトップにドラッグすることでNotifyIconを消去しアプリのタスク専用の小Windowを表示。
・同様に逆に小WindowをタスクバーにドラッグすることでNotifyIcon化
（これイベントトラップ細工しないと難しそう）
・メインのウインドウのShow／Hideの際のトランジションをなんか工夫

見た目重視。
プロセスのアイコン化ってそれほど特殊な考え方じゃないハズ。UNIXとかそうなんですよねよく知らないけど。
NeXTの適当にまねて作ったこんな感じのとか↓。
![コメント 2020-02-27 210952^2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/590512/8b8ab624-81c4-98de-1e1e-d95ccd2701be.png)

Macで最初に手に入れたThinkCって言語環境についてたリファレンスアプリがいい感じにやってたのを思い出して、あれを目標に。
IE10/11のplaceholderについてのメモ

## 前段
　Google Apps Script でちょっとしたシステムを作った。スプレッドシートに設問内容を書いておいて、それをロードして `HtmlService` を介して入力フォームを表示するもので、完成して運用もしていた。全体を ES5 で書いて IE でも動くようにしたつもりだったが、なにやら不具合報告をいただいたので、原因は結局よくわからなかったが、調査した結果をメモしておく。

## IE のプレースホルダはやや特殊
　HTML5 で導入された `placeholder` 属性が IE で微妙な挙動をすることはよく知られている。たとえばフォーカス時の動作が微妙に異なっており、Chrome や Firefox などの一般的なモダンブラウザでは、テキストエリア選択中でも値が空であればプレースホルダが表示される。IE10/11 ではフォーカスを外すまで表示されない（IE9 以下ではそもそも実装されていない）。

参考：[placeholder のブラウザごとの違い＆その対処法など](https://qiita.com/myzkyy/items/54c19077f95f0fc1a19a)

## DOM で生成したときのバグ
　IE10/11 の `placeholder` については、html にベタ書きするか、DOM から動的に生成するかによっても異なる挙動をするようだ。たとえば以下のようにして `placeholder` と `値` が両方とも入った `textarea` を DOM から生成してみる。

```html
<script>
  window.onload = function(){
    var ta = document.createElement("textarea");
    ta.setAttribute("placeholder", "placeholder");
    ta.textContent = "value";
    document.getElementById("placeForInsert").appendChild(ta);
  }
</script>
　 ↓ ↓ ↓
<span id="placeForInsert">
  <textarea placeholder="placeholder">value</textarea>
</span>
```

　これを IE10/11 で実行すると、見た目は Chrome などと同様に `"value"` が入力されたテキストエリアが表示されるが、テキストエリアをクリックすると、この `"value"` は消失し、テキストエリアは空欄となる。さらにフォーカスを外すと、真のプレースホルダ `"placeholder"` が表示される。非常に不可解な挙動。


　どうやら **IE10/11 では、このように DOM から生成した `textarea` は値の規定値がなぜかプレースホルダとして解釈される** らしい。わかりやすいように `style` でプレースホルダを赤色にして対照してみよう。次の画像は比較用に作成したページを IE10/11 でロードした直後の画面であるが、上段は html にベタ書きで書いた `input` と `textarea` で、下段は DOM から生成した同じものである。いずれもプレースホルダに `"placeholder"`、値に `"value"` を設定している。IE10/11 で表示したときだけ、ロードした段階でひとつだけプレースホルダ扱いになっている、つまり、`textarea` を DOM から生成したときのみ、規定値がプレースホルダとして解釈されていることがわかる。

![IE10/11 ではロード時に値がプレースホルダ扱いされる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526487/0333f806-323f-950b-748e-426d4ba0838b.png)

## 開発者ツール…
　IE11 の開発者ツールの DOM Explorer でソースをチェックしてみる。テキストエリアをクリックする前にソースを開いて確認するときちんと意図通りのタグをしているが、クリックして上述の現象が起きたあとに確認すると、規定値がすげ変わっている。IE の DOM Explorer はそもそもソースをチェックするものではないので、まあ観測した時点で確定するということで。

```html
<!-- when opening before click -->
<textarea placeholder="placeholder">value</textarea>

<!-- when opening after click -->
<textarea placeholder="placeholder">placeholder</textarea>
```

## 対策
　原理がよくわからないが、プレースホルダは乱用するとフォームの可用性を損なうものでもあるので、IE 対応をするならプレースホルダの使用を控えるしかなさそう。

## 補足
　検証はローカルで行なったが、現象自体は本番環境（GAS）で確認されたものなので、特に影響はないだろう。
[flutter] sdkmanagerの入れ方

# はじめに
flutter docterで`Android license status unknown`と怒られてたので対処しました。

```terminal
[✓] Flutter (Channel stable, v1.12.13+hotfix.8, on Linux, locale ja_JP.UTF-8)
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
    ✗ Android license status unknown.
      Try re-installing or updating your Android SDK Manager.
      See https://developer.android.com/studio/#downloads or visit
      https://flutter.dev/setup/#android-setup for detailed instructions.
[✓] Android Studio (version 3.6)
[✓] VS Code (version 1.42.1)
```

# 開発環境
Linux

# 方法
Android Studioの Appearance & Behavior >> System Settings >> Android SDK >> SDK Tools タブ >> Android SDK Toolsにチェック >> Apply >> インストール

# 確認
すべてyを入力すると`All SDK package licenses accepted`と表示されて無事完了

```
❯ flutter doctor --android-licenses

Accept? (y/N): y
...
...
Accept? (y/N): y
All SDK package licenses accepted
```

```
❯ flutter doctor                                                  2020/02/27 20:42:08
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.12.13+hotfix.8, on Linux, locale
    ja_JP.UTF-8)

[✓] Android toolchain - develop for Android devices (Android SDK version
    29.0.3)
[✓] Android Studio (version 3.6)
[✓] VS Code (version 1.42.1)
[✓] Connected device (1 available)
```
標準入力で連続した数値を受け取る方法(paiza用)

#Rubyで文字列ではなく連続した数値を入力したい
paizaの問題を解くときに標準入力で数値を受け取りたいが、paizaの入力方法ではgets.to_iだけでは不十分だったのでメモとして残します。

#NG例
入力は以下である場合
1 2 3

```ruby:example.rb
number = gets.chomp.split.map(&.to_i)
puts number
```
map(&.to_i)のように'&'の後に'.'をつける方法がネットによく出ているが、paizaの環境ではsyntaxエラーがでて動かない。

#成功例

```ruby:example.rb
number = gets.chomp.split.map(&:to_i)
puts number
```

'.'を':'に変えると動いた。
【PHP】CSVファイルを読み込む方法


#はじめに
アプリケーションを作成しているとcsvファイルを読み込み、書き込みする場面が多くあると思います。
今回はcsvファイルを読み込み、書き込みする方法を解説します。

##今回使用するcsvファイル　祝日のデータ
内閣府が出している祝日のファイルを使って解説します。

中身は下記のような形式です。

```:syukujitu.csv
1955/1/1,元日
1955/1/15,成人の日
1955/3/21,春分の日
　　//　：
　　//　：
2021/10/11,スポーツの日
2021/11/3,文化の日
2021/11/23,勤労感謝の日
```

#csvファイルの読み込み
csvファイルを読み込む方法の大まかな流れは下記の通りです。

1. csvファイルを開く
2. csvファイルを読み込む
3. csvファイルを閉じる

##1. csvファイルを開く
まず、csvファイルを読み込むためにファイルを開きます。
使用する関数は`fopen`です。
この関数の処理に成功するとファイルポインタリソースが返ってきます。
ファイルポインタリソースとはファイルの読み書きに必要な情報（ファイル名、ファイル操作権限、csvファイルの何行目から読むのかなど）です

```php:sample.php
<?php
$filePath = "sample.csv";
$handle = fopen($filePath, "r");
```

`$handle`の意味は`$filePath`のファイルを`r`モードで開くということです
###モードについて
ファイルを開くにも色々な種類があります。
書き込み専用で開く、読み込み専用で開く、書き込みと読み込みできるように開くなどです
4種類について説明します。これだけで基本的な操作はできます

`r`：読み込みのみで開きます。処理の開始場所はファイルの先頭です。
`r+`：読み込み／書き出し用に開きます。 処理の開始場所はファイルの先頭です。
`a`：書き出し用のみで開きます。処理の開始場所はファイルの終端です。 ファイルが存在しない場合には、作成を試みます。
`a+`：読み込み／書き出し用で開きます。 処理の開始場所はファイルの終端です。 ファイルが存在しない場合には、作成を試みます。 

##2. csvファイルを読み込む
1で開いた結果を使ってcsvファイルの読み込みを行います
`fgetcsv `関数を使用します。
下記のコードでcsvファイルを読み込みます。
$handleの情報を元に`,`区切りのcsvファイルを1000項目まで読み込みます
1000は0にすると無制限に読み込んでくれます。しかし、処理が遅くなるようなので、設定しているのが無難です

```php:sample.php
<?php
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
	echo $data[0];
	echo $data[1] . "<br />\n";
}
```

##3. csvファイルを閉じる
読み込みが終わったら、ファイルを閉じます。
使用する関数は`fclose`です

```php:sample.php
<?php
fclose($handle);
```

##文字コード
csvファイルはShift_JISで書かれていることが多いので、プログラムで扱うためには文字コードをUTF-8に変換する必要があることが多いです。
文字コードの変換方法は下記の通りです。

```php:sample.php
<?php
//Shift_JISからutf-8に変換
$data[1] = mb_convert_encoding($data[1],"utf-8","sjis");
```

以上です！！！
**ここまで読んでいただきありがとうございました！！
疑問、気になるところがございましたら、質問、コメントよろしくお願いします！！！**
create-react-appのnpm startで開くブラウザを変更する

他にも記事がある気がしますが、自分がQiitaで調べた中では見つけられなかったのでメモします。

## したいこと
create-react-appで用意した環境で、npm startで自動的に開くブラウザを変更したい。
MacだとデフォルトではChromeが優先して開くみたいで困る。

## したこと

プロジェクト直下に`.env.local`ファイルを作り以下のように記述[^1]。
例）FireFox

```
BROWSER="firefox"
```
これで自分の環境では上手く行きました。

ただ、ブラウザ名はOSによって指定の仕方が違うみたいです[^2]。
例）Chromeの場合
MacOS : `google chrome` 
Windos: `chrome`
Linux : `google-chrome`

[^1]: https://create-react-app.dev/docs/advanced-configuration/
[^2]: https://github.com/sindresorhus/open#app
MySQL UNIQUE KEYの変更

## 概要
MySQLのUNIQUE KEYの変更がすんなりいかないということだけは記憶に残っていましたが、方法が記憶に残っていなかったので、備忘録的に記載します。

## 前提
以下のようなTableを想定します。

```
mysql> SHOW CREATE TABLE articles;

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4

mysql> ALTER TABLE articles ADD COLUMN experiment_sign varchar(64) AFTER tab_id;

Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0
```

このarticles Tableにexperiment_sign Columnを追加したとします。

```
mysql> SHOW CREATE TABLE articles

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4
```
experiment_sign Columnがvarchar(64)で追加されているのが確認できると思います。
そして、UNIQUE KEYがtab_idのみになっていますが、これにexperiment_signを追加して、下記のような変更を行いたいと仮定します。

```
UNIQUE KEY `idx_tab_id (`tab_id`)
```
↓

```
UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)
```

さあ、このときどうするかというのが、今回のお話です。

## 失敗例
何も考えず、とりあえずUNIQUE KEY追加すればええやろと、安易に考えると、下記のようなALTER文を書いてしまいます。私です。

```
ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`)
```

こうすると、どうなるかというと

```
CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id` (`tab_id`)
  UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4
```

とまあ、UNIQUE KEYが二つつきますね。はい。
これでは、わけわからんことになりますね。見た目も、無駄に誤解を招きます。


## 正解例
ちゃんと既存UNIQUE KEYを潰しましょう。

```
mysql> ALTER TABLE articles ADD UNIQUE `idx_tab_id_experiment_sign (`tab_id`, `experiment_sign`);
mysql> ALTER TABLE articles DELETE INDEX `idx_tab_id;
```

こうすると、理想の形ができあがります。

```
CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `tab_id` int(10) unsigned NOT NULL,
  `experiment_sign` varchar(64) NOT NULL,
  `articles` mediumtext NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_tab_id_experiment_sign` (`tab_id`, `experiment_sign`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4
```

## 終わり

ちゃんと、理想の形になっているか確認しましょう。以上です。
ああ、もちろん、UNIQUE KEYはNOT NULL制約をつけましょう。
面接メールで「第三希望まで予定出してください」をなくしてストレスフリーにする

:::note warn
2023年現在ですと、Googleカレンダーの予約機能を使ったり、スケジュール調整サービスなどもあるので、以下のように頑張らなくていいと思います。
使えるサービスは使いましょう。
:::

人事にウケたんで作り方を公開します。

## 1. 公開用のGoogleカレンダーを作る
[Googleカレンダーの設定](https://calendar.google.com/calendar/embedhelper)から埋め込みコードを持ってきて自分のサイトなどに貼ります。
自分のサイトを作る方法は略しますが、ホスティングサービス等を使うと簡単です。

## 2. カレンダーに対応するようGoogleフォームを作成する
[Googleフォーム](https://docs.google.com/forms/)
Googleカレンダーの設定項目をそのまま移植していくイメージ。
「新しい回答についてのメール通知を受け取る」ようにすると連絡を受ける事ができます。

後は手作業でカレンダーに反映させましょう。

## 楽したい！
ここからはコードを書く必要があります。

### 3. 回答を解析してGoogleカレンダーに反映する
ここでGASを使うことになります。
トリガーを設定してGmailAPIなりSpreadSheetAPIなりを使ってデータを受け取ります。おすすめは後者。
フォームの内容をGoogleカレンダーの諸項目に当てはめてGoogleCalendarAPIを叩くと順次反映されます。

### 自動的に登録されるのは困る
GoogleCalendarの登録APIではなくまずは取得してダブっていたら登録せず、別途通知をするようにしたらいいです。
SlackやLINEなどに送るようにすると気付きやすいでしょう。

### サンプル
手前味噌で恐縮ですが、[こんな感じ](https://shimajima-eiji.github.io/resume/archive/recruit)にできます。
試しにGoogleカレンダーに祝日とconnpassのイベントスケジュールを連携してみたイメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122800/4a13fcbb-b7d8-1c0d-4c03-01fa115a6e3f.png)

需要があれば、ハンズオンやろうかな。
▶️要らなくなりましたね。サービス使いましょう。
bin/rails db:migrateしようとしたらMysql2::Error: Table '*****' doesn't existが出る

##経緯

``` 
bin/rails g model image
```
と

``` 
bin/rials g model product
```
によって作られたマイグレーションファイル
***20200227092210_create_images.rb***と
***20200227092647_create_products.rb***を,

```ruby:20200227092210_create_images.rb
（中略）
      t.references :product, foreign_key: true
（中略）
```
と書いて, bin/rails db:migrate を実行

##起きたエラ〜

```
(中略)
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Mysql2::Error: Table 'プロジェクト名_development.products' doesn't exist: SHOW FULL FIELDS FROM `products`
(中略）
```

というエラーが発生した。

##解決策
どうやら、
***referencesされるマイグレーションファイル***
の方が先に***migrate***されるべきなようで、
***
20200227092647_create_products.rbを、
　→2020022709 ***2209***_create_products.rb
　　(20200227092210_create_images.rb)
にすると、images.rbよりも早くmigrateされた！！

###参照したwebページ
teratail:[rails db:migrateができません（おそらくmysqlのカラムがマッチしていない）](https://teratail.com/questions/89334)
JavaScript Hoisting メモ

#Hoistingとは
JavaScriptは変数宣言は必要がありません、自動的に変数名を設定されます。

```JavaScript:example1.js

var a = 12;
console.log(a);//12

a = 12;
console.log(a);//12

```
example2は同じ結果になります。こられはhoistingということです。

```JavaScript:example2.js

var a;
a = 12;
console.log(a);//12

a = 12;
console.log(a)//12
var a;

```

example3は無名と名つけ関数のHoistingの事例です。

```example3.js
function a(x,y){
  return x+y;
}
console.log(a(x,y));//5

var b = function (x,y){
  return x+y;
}

console.log(b(2,3));//5


c = function (x,y){
  return b+c;
}

console.log(c(2,3));//5
var c;

```
ヤマハのルータとのL2TP接続を複数ユーザに対応する

去年、「[Windows10とヤマハのルータ間でL2TP接続をする](https://qiita.com/kazuhiro1982/items/772dd854da83d4b5ff28)」という記事を書きましたが、今回は複数ユーザが同時に接続出来るようにしたいという要件に対応したので、そのメモです。

## 環境
### ルータ
機種: RTX810
ファーム: Rev.11.01.33

## やったこと

### tunnel追加

前回はtunnel3にL2TP設定をしました。
同時接続を実現するにはユーザごとにtunnelを確保する必要があるので同じ設定を追加していきます。
ここでは2ユーザ分追加して全部で3ユーザが同時接続できるようにします。

```

### TUNNEL 4 ###

tunnel select 4
 tunnel encapsulation l2tp
 ipsec tunnel 4
  ipsec sa policy 4 4 esp aes-cbc sha-hmac
  ipsec ike keepalive log 4 off
  ipsec ike keepalive use 4 off
  ipsec ike local address 4 192.168.0.1
  ipsec ike nat-traversal 4 on
  ipsec ike pre-shared-key 4 text ${shared-key}
  ipsec ike remote address 4 any
 l2tp tunnel auth off 
 l2tp keepalive use on
 l2tp syslog on
 ip tunnel tcp mss limit auto
 tunnel enable 4

### TUNNEL 5 ###

tunnel select 5
 tunnel encapsulation l2tp
 ipsec tunnel 5
  ipsec sa policy 5 5 esp aes-cbc sha-hmac
  ipsec ike keepalive log 5 off
  ipsec ike keepalive use 5 off
  ipsec ike local address 5 192.168.0.1
  ipsec ike nat-traversal 5 on
  ipsec ike pre-shared-key 5 text ${shared-key}
  ipsec ike remote address 5 any
 l2tp tunnel auth off 
 l2tp keepalive use on
 l2tp syslog on
 ip tunnel tcp mss limit auto
 tunnel enable 5
```

tunnel_idやpolicy_idを更新した以外は内容はすべて一緒です。
事前共有キーもpp anonymousに設定した同じものをすべてのtunnelに指定する必要があります。

### IPSecのトランスポートモード設定

作成したポリシーのトランスポートモードを追加します。

```
ipsec transport 4 4 udp 1701
ipsec transport 5 5 udp 1701
```

### pp設定

```
### PP anonymous ###

pp select anonymous
 pp bind tunnel3 tunnel4 tunnel5
 pp auth username ${userA} ${passwordA}
 pp auth username ${userB} ${passwordB}
 pp auth username ${userC} ${passwordC}
```

pp anonymousに追加した各tunnelをbindします。
ユーザを必要数分追加します。

### 完了です

これで作成した各ユーザが同時に接続できるようになりました。
トランスポートモードの追加とかbindの追加とかを忘れがちなので備忘録です。

なお、ユーザをどれほど増やせるかについては、各機種でいくつまでトンネルを作れるかの数字と一致します。
詳細は公式のドキュメントを参照。

参考：[トンネルの数(VPNの対地数)に制限はありますか？](http://www.rtpro.yamaha.co.jp/RT/FAQ/IPsec/faq_2_d.html)

今回使用している機器はRTX810なので最大トンネル数は6です。