OmniAICreator/Qiita-1.07M huggingface dataset:
HTTP Strict Transport Security ヘッダー メモ


## HTTP Strict Transport Security とは

* ウェブサイトがブラウザにHTTP の代わりに HTTPS を用いて通信を行うよう指示するためのレスポンスヘッダー

![hsts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/0782e984-bf44-406f-60fe-92a50999f976.png)


## HSTSを設定する理由

* **中間者攻撃の防止**
  * 暗号化されていないHTTPによるアクセスを誘い、暗号化しない状態で通信させることで、Cookieや、フォーム送信した情報を傍受・改竄される危険がある。

![mima.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/1ba5e50d-014a-09db-4521-6e56ba3c7ef8.png)




* **HSTSを設定することで...**
  * 一度正規サイトにアクセスした場合、同一ドメインにアクセスする際にはHTTPでのアクセスを試みることはなく、HTTPSでアクセスを行うようになる。

## ディレクティブ

```
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
```



- `max-age`※必須
  - 対象サイトに HTTPS だけで接続することをブラウザーが記憶する有効期間
  - 秒数指定。31536000=1年間
  - 有効期限が経過すると、自動的にHTTPS を使用するのではなく、通常通りに HTTPでサイトアクセスを試みる。
  - Strict-Transport-Security ヘッダーがブラウザへ送信されるたびに、そのウェブサイトに対する有効期限が更新される。
  - HSTSを無効にしたい場合は0を指定する。
- `includeSubDomains` ※任意
  - すべてのサブドメインに適用される。
- `preload` ※任意
  - pre-loaded list(Googleが管理)に事前追加(登録申請必要)することで、初回リクエストからHSTSを動作させる。
  - ペイレベルドメインのみ設定可能※サブドメイン単位では登録不可。
  - [申請要件](https://hstspreload.org/#submission-requirements)



## 参考情報

* [Strict-Transport-Security](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Strict-Transport-Security)
* [RFC 6797 - HTTP Strict Transport Security (HSTS) 日本語訳](https://tex2e.github.io/rfc-translater/html/rfc6797.html)
Ubuntu20 でユーザー追加､sudo設定､SSHのrootログイン禁止までの手順

外部公開サーバーでは、sudo設定、SSHのrootログイン禁止は
セキュリティ上、必須な設定だと思われます。
恥ずかしながら、実は設定していなかった為、作業手順のまとめ

## ■ユーザー追加

##### ①新規ユーザー追加

~~~
# adduser -c "Guest Add User" -d /home/userA -s /bin/bash userA
~~~

useradd -c "[コメント]" -d [ホームディレクトリ] -s [シェル] [ユーザー名]
 


##### ②ユーザーパスワード設定

~~~
# passwd userA
~~~
passwd [ユーザー名]


## ■sudo設定

対象ユーザーを､「wheel」グループに追加
~~~
# usermod -G wheel userA
~~~
usermod -G wheel [ユーザー名]


##### ｢wheel｣グループのユーザが｢sudo｣を利用できるように設定を変更

~~~
# visudo
~~~

##### ↓以下の部分がコメントアウトされていなければ、コメントアウト

~~~
## Allows people in group wheel to run all commands
# %wheel ALL=(ALL) ALL
~~~

~~~
%wheel ALL=(ALL) ALL
~~~


## ■SSHのrootログインを禁止

設定ファイルの変更

~~~
# vi /etc/ssh/sshd_config
~~~

↓私の環境では、38行目

~~~
LoginGraceTime 15s
#PermitRootLogin yes
#StrictModes yes
~~~

↓以下のように設定変更

~~~
PermitRootLogin no
~~~

設定ファイルの保存後､構文チェック

~~~
# sshd -t
~~~
エラーにならないことを確認

##### sshdの設定反映　※ !!以後ログオフした時点から、rootでログインできなくなるので注意!!

~~~
# systemctl restart sshd.service
~~~


##### ※sshdログインユーザーは、以下のファイルで設定

##### ①/etc/pam.d/sshd　の設定
```/etc/pam.d/sshd
account    required    pam_access.so  ## 11行目のコメントを外します
```

##### ②/etc/security/access.conf　の設定
```/etc/security/access.conf
+ : user1 : 168.192.
+ : user2 : 172.0.0.
+ : user3 : ALL    ## 全てのIP
```
※↑但し、私の環境では、外部からの接続時、ファイアーウォールのIP＝1つの処理になって
　　しまう事が判明、このIP制御は機能しませんでした。
　　(どの外部IPから来ても、＝ファイアーウォールのIPと処理されていました。)

#### ■WinSCPの追加設定
↓いざという時にWinSCPでの重要ファイルの操作が不便な為、WinSCPでも特定ユーザーで
root権限でのファイル閲覧編集を可能にする事に
[WinSCPで特定ユーザーでもroot権限でファイル閲覧編集を可能にする方法](https://qiita.com/0to9/items/08e8becfff3f8c4e3629)

----

##### 参考にさせていただいたページ
https://qiita.com/Zen-GoTo-May/items/3aab7b6c76e8a857e717
https://manual.sakura.ad.jp/vps/support/security/sudo.html
https://webkaru.net/linux/ssh-prohibit-root-login/
https://cloudpack.media/1101
Elastic Cloud の filters の使い方

参考ページ
[Search API Filters](https://www.elastic.co/guide/en/app-search/current/filters.html)

サンプルの
national-parks-demo
を使います。

## Curl ##

```bash:filter_curl.sh
HOST="https://mar29-project.ent.asia-northeast1.gcp.cloud.es.io"
SEARCH_KEY="search-******"
NAME_ENGINE="national-parks-demo"
#
#
curl -X POST ${HOST}'/api/as/v1/engines/'$NAME_ENGINE'/search' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer '${SEARCH_KEY} \
-d '{
  "query": "",
  "filters" : {
"states": [ "Texas", "Minnesota" ]
  }
}'
```

```bash:go_curl.sh
./filter_curl.sh > out01.json
jq .results[].states out01.json
```

実行結果

```text
$ ./go_curl.sh 
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2646  100  2573  100    73   5105    144 --:--:-- --:--:-- --:--:--  5302
{
  "raw": [
    "Texas"
  ]
}
{
  "raw": [
    "Minnesota"
  ]
}
{
  "raw": [
    "Texas"
  ]
}
```

## Httpie ##

```bash:filter_http.sh
HOST="https://mar29-project.ent.asia-northeast1.gcp.cloud.es.io"
SEARCH_KEY="search-******"
NAME_ENGINE="national-parks-demo"
#
#
http ${HOST}'/api/as/v1/engines/'$NAME_ENGINE'/search' \
	'Authorization: Bearer '${SEARCH_KEY} < query01.json
```

```json:query01.json
{
  "query": "",
  "filters" : { "states": [ "Texas", "Minnesota" ] }
}
```

```bash:go_http.sh
./filter_http.sh > out02.json
jq .results[].states out02.json
```

実行結果

```text
$ ./go_http.sh 
{
  "raw": [
    "Texas"
  ]
}
{
  "raw": [
    "Minnesota"
  ]
}
{
  "raw": [
    "Texas"
  ]
}
```
FlutterでWebRTC（flutter-webrtc-demoを動かす）

<h2>経緯</h2>
業務で初Flutter＋初WebRTCということでまずは調査開始。
flutter-webrtcというOSSを利用しようと思い、github眺めていたらdemo版も用意されていたので動かしてみようと思い立ったのが経緯となる。
<h2>WebRTCってなに</h2>
WebRTCって聞いた事ない・・・。って状態だったのでそこから調査すると、
<b>「Web Real-Time Communication」</b>の略称とのこと。
APIを経由して、ウェブブラウザやモバイルアプリでリアルタイム通信を実現する技術だそうです。
細かい説明は省略しますが、結局、ワードを知らないだけでエンジニアには馴染み深いものでした。
Teams、Zoomとかの裏側で動いてるテレビ通話を可能にしてくれるものって感じ（適当）

これに加え、<b>WebRTC SFU</b>というものも存在します。
WebRTC SFU (Selective Forwarding Unit ) はP2Pでの通信とは異なり、音声や映像を「サーバー経由」で配信する技術の一つ。

結局、上記２つの何が違うんだよって感じ。
簡単にいうと、WebRTC（P2P）とWebRTC SFU（SFU）との違いは、データ通信を行っているのがサーバー経由(SFU)か直接端末同士(P2P)なのかってことだそうです。
なので、SFUであれば受信側の人数が増えても配信者自身の回線や端末に高い負荷がかかることはなく、一度に多くの視聴者へ音声や映像をリアルタイムに配信することができるメリットがある。

<h2>【本題】flutter-webrtc-demoが動くまで</h2>
<h6>環境</h6>
Mac(intel)

![スクリーンショット 2022-04-01 14.39.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552825/6af66bae-1685-8b9a-0e46-0b78e8792e0e.png)

<h4>言語</h4>flutter
<h4>エディタ</h4>VSCode
<h4>使用OSS</h4>flutter-webrtc（demo,server）

https://github.com/flutter-webrtc/flutter-webrtc-demo

https://github.com/flutter-webrtc/flutter-webrtc-server

<h2>環境構築</h2>
Flutterの環境構築は既に終わっている前提とします。
終わっていない方は以下を参照し、構築してください（Windowsの方は他の方のサイトを参照ください）。

https://qiita.com/syny/items/6cee4fe2ba1c8b5db998

OSSのソースをクローン（flutter-webrtc-demo、flutter-webrtc-server両方）

クローンの仕方がわからない方は以下を参照。

https://docs.microsoft.com/ja-jp/azure/developer/javascript/how-to/with-visual-studio-code/clone-github-repository?tabs=create-repo-command-palette%2Cinitialize-repo-activity-bar%2Ccreate-branch-command-palette%2Ccommit-changes-command-palette%2Cpush-command-palette

<h2>flutter-webrtc-server</h2>
ターミナルを開いてコマンドの実施

:::note info
  `` <path> ``の箇所は適宜合わせてください
:::

```
cd <path> flutter-webrtc-server
brew update
brew install mkcert
mkcert -key-file configs/certs/key.pem -cert-file configs/certs/cert.pem  localhost 127.0.0.1 ::1 0.0.0.0
brew install golang
go run cmd/server/main.go
```

上記、実施後エラーなく実行完了したら、準備完了

<h2>flutter-webrtc-demo</h2>
ターミナルを開いてコマンドの実施

:::note info
  `` <path> ``の箇所は適宜合わせてください
:::

```
cd <path> flutter-webrtc-demo
flutter packages get
flutter run
```

上記で、アプリが起動。
<h2>動作確認</h2>
ブラウザから以下にアクセス。

https://0.0.0.0:8086 

アクセス後、「P2PCall Sample」を押下して「localhost」と入力後、「CONNECT」押下
![スクリーンショット 2022-04-01 15.23.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552825/7a84b4fa-0fe4-3eb3-827d-430d5d54711b.png)

次に「demo」の方に戻り、「P2PCall Sample」を押下して「localhost」と入力後、「CONNECT」押下
![スクリーンショット 2022-04-01 15.31.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552825/f566d7c5-aca9-3fe6-6ad0-c7643c7585bd.png)

赤枠のビデオマークを押下

![スクリーンショット 2022-04-01 15.34.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552825/ea26037c-79a7-fb46-26f2-f6b59e3fa2e3.png)

通話できた！

![スクリーンショット 2022-04-01 13.57.38 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552825/97358f56-feda-359b-ce68-255825b4f563.png)
grepするときに任意のフォルダを除外する

（自身の備忘録用です）

grep時、除外したいフォルダがある場合、`--exclude-dir`を付与します

```
grep -r [検索文字列]　--exclude-dir=[除外フォルダ]　./

例）
grep -r [検索文字列]　--exclude-dir=node_modules　./
grep -r [検索文字列]　--exclude-dir={node_modules,tests}　./ 
　→複数指定したい場合は括弧で囲む
```
BOM付きCSVファイルを読み込むときの注意点

## 環境
node.js v10.19.0
Ubuntu 20.4

## はじめに
UTF-8のCSVファイルには、BOM付きのCSVファイルととBOMなしのCSVファイルの２種類が存在しています。CSVファイルを読み込んで中身を取得する場合、BOM付きのときは、BOMを削除しないとデータが正しく取得されないことになります。（BOMが付いたままでデータを取得すると、処理に誤りが生じることになります）

CSVファイルを読み込んだら、まずは、BOMが付いているかどうか判断して、付いていれば、データを取得する前に削除する必要があります。

BOMが付いているかどうかを判断する方法は、CSVファイルをダブルクリックして、Excelで開いて、文字化けが起こればBOMが付いていない。文字化けが起こらなければ、BOMが付いていると判断することができます。

CSVファイルをエディタで開いて中身を確認しても、BOMは改行コードのように、目で見えるものではないため、エディタでは直接確認することはできません。

## コードの解説
test_bom.csvをBOM付きで作成します。test_bom.csvをエディタで開くと、このようなデータが作られます。１行目の先頭（長浜の左側）にはBOMが付いています。しかし、エディタで見る分にはBOMが付いているかを判断することはできません。

```test_bom.csv
長浜,みかん,500
米原,バナナ,1000
彦根,りんご,3000
```

test_bom.csvを作成した後、test_bom.csvを読み込んで、各行の１カラム目だけを取得します。つまり、「長浜、米原、彦根」のデータだけを取得します。

test_bom("")を実行して、test_bom.csvを読み込んだとき、１行目に「長浜」が存在しているがマッチしませんでした。「（BOM付き）長浜」と「長浜」の文字列とを比較していたことが原因です。このようにBOMが付いたままになっていると、１行目の処理で誤りが発生する原因となります。

次に、test_bom("BOM")を実行します。２回目にtest_bom.csvを読み込みんだときは、先頭行のBOMを削除しています。２回目の処理では、「（BOMなし）長浜」と「長浜」の文字列をと比較しているため、正しくマッチされました。

## コード
```sample.js
function test_bom(flg) {

    var fs = require('fs');
    var fd;
    var file = __dirname + "/test_bom" + ".csv";
    var ary = [];

    ary.push("\ufeff" + "長浜,みかん,500");
    ary.push("米原,バナナ,1000");
    ary.push("彦根,りんご,3000");

    fs.writeFileSync(file, "");

    fd = fs.openSync(file, "w");                    
    fs.writeSync(fd, ary.join("\n"), 0);            
    fs.closeSync(fd);

    var text = fs.readFileSync(file, 'utf8');                 
    var lines = text.toString().split('\n');                  

    // BOMが付いていれば削除
    if (flg == "bom") {
        lines[0] = (lines[0].includes("\ufeff")) ? lines[0].replace("\ufeff", "") : lines[0];
    }

    for (let idx in lines) {

        let ary_match = ["長浜", "米原", "彦根"];
        let data = lines[idx].split(",")[0];

        if (ary_match.includes(data)) {
            console.log(data);
        } else {
            console.log("マッチしない");
        }
    }
}


test_bom("");
console.log("---------");
test_bom("bom");
```

## 結果
```
$node sample.js
マッチしない
米原
彦根
---------
長浜
米原
彦根
```
C＃を使用してExcelで結合されたセルを取得する方法

人々がExcelのテーブルを作るとき、いつもいくつかのセルを結合したり結合を解除したりする必要があります。セルをマージ解除したいと、1つずつ検索してキャンセルしなければならないからかなり面倒です。ゆえに、次はExcelで結合されたセルを識別する簡単な方法を紹介します。これを使って結合されたセルを識別した後また結合を解除したり、書式設定などの他の操作を実行することもできます。

より良く理解させるため、こちらはふたつの結語したエリアを含むドキュメントを例として、[Spire.XLS](https://www.e-iceblue.com/Download/download-excel-for-net-now.html)というコンポーネントを使用して、詳しく説明します。
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/d2041592-ddd3-fd36-f687-0a6ef5db7c8a.png)


## コードを使用する
ステップ1：Workbookオブジェクトをインスタンス化し、Excelドキュメントを読み込みます。
```
Workbook workbook = new Workbook();
workbook.LoadFromFile("test.xlsx");
```
ステップ2：Excelドキュメントの最初のシートを取得します。
```
Worksheet sheet = workbook.Worksheets[0];
```
ステップ3：マージされた領域を取得し、結果をCellRange配列に保存します。
```
CellRange[] range = sheet.MergedCells;
```
ステップ4：配列をトラバースして、マージされた領域のセルをマージ解除します。
```
foreach (CellRange cell in range)
{
    cell.UnMerge();
}
```
ステップ５：ドキュメントを保存します。
```
workbook.SaveToFile("result.xlsx");
```
操作した画像は以下のように：
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/464e93fc-aabd-dbe8-bccf-98b25f9550b2.png)

## 完全なるコード一覧：

```
using Spire.Xls;

namespace Detect_Merged_Cells
{
    class Program
    {
        static void Main(string[] args)
        {
            Workbook workbook = new Workbook();
            workbook.LoadFromFile("test.xlsx");
            Worksheet sheet = workbook.Worksheets[0];
            CellRange[] range = sheet.MergedCells;
            foreach (CellRange cell in range)
            {
                cell.UnMerge();
            }
            workbook.SaveToFile("result.xlsx");
        }
    }
}
```

今回のExcelで結合されたセルを取得する方法は以上でした、最後まで読んでいただきありがとうございました。
fish shellでnvmが使えなくなったときにしたこと

# homebrewでinstallしたnvmが使えなくなった

## 状況
- zshからfish shellに変えた
- fish shellでbrewコマンドを使えるように設定
- zsh使用時にインストールしたnvmはこの時点で使用できていた
- ある日突然nvmが使えなくなった

## 環境
M1 MacBook Air

## 調査
- `nvm`はfish shellに対応していない
- fish shellに対応したサードパーティ製のものが存在する
- zshで使用していた`nvm`を使用するには[base](https://github.com/edc/bass)を使用する必要があるらしい
    - [base](https://github.com/edc/bass)はshellでbashコマンドを使えるようにするユーティリティ
## やったこと
調査結果からすでに使用している`nvm`を使用するためにbassを導入することにした。
- 以下を実行してbassを導入する
```
git clone https://github.com/edc/bass.git
cd bass
make install
```

- `~/.config/fish/config.fish`の末尾に以下を追加する
```
function nvm
   bass source (brew --prefix nvm)/nvm.sh --no-use ';' nvm $argv
end

set -x NVM_DIR ~/.nvm
nvm use default --silent
```
- ターミナルを再起動するか、以下を実行して設定を反映させる
```
source ~/.config/fish/config.fish
```

## 最後に
なんで、途中まで`nvm`が使えていたのかは謎のまま
まあ、使えるようになったんでOKということでｗ

## 参考
- [nvm on mac for fish users](https://medium.com/@joshuacrass/nvm-on-mac-for-fish-users-e00af124c540)
file_field_tag と submit_tag を横並びまたは縦並びに調整する

個人的な作業メモ。`erb`ではなく[haml](https://github.com/haml/haml-rails)を使っており、[.col-sn](https://getbootstrap.jp/docs/4.2/layout/grid/)やボタンのスタイルは、Railsの標準Gemfileとして設定されている`Bootstrap`。

## 横並び

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82601/34f00040-b720-2fdd-23fc-5e1edcabcf06.png)

```haml
= form_tag import_items_path, multipart: true do
  = file_field_tag :file, accept: "text/csv", style: "display: inline;"
  = submit_tag "一括編集", class: "btn btn-success"
```


## 縦並び

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82601/cc6e9bda-7a8f-da95-0c2b-6eb4101a7286.png)

```haml
= form_tag import_items_path, multipart: true do
  = file_field_tag :file, accept: "text/csv"
  = submit_tag "一括編集", class: "btn btn-success", style: "margin-top:20px"
```
Microsoft Teams メンションの「さん付け問題」に終止符を打つ！「僕が実践する最強のメンションさん付けポリシー」

Microsoft Teams を利用する上でメンション機能の利用が欠かせませんが、この機能の利用にはたびたび物議を醸しています。論争の焦点は、メンションに敬称を付けるのか否かです。

この「メンションさん付け問題」は長らく渾沌としてきましたが、この度、この問題に終止符を打つべく「僕が実践する最強のメンションさん付けポリシー」を記しておこうと思います。

# 文章の先頭や末尾のメンションに「さん付け」不要！文中のメンションは「さん付け」可能！

投稿の先頭や末尾は、よくメンションが現れる代表的な場所です。この場所で用いられるメンションは、通知先を指定するために機能的に用いられるだけであり、さん付けは不要です。メールアドレスにさん付けしないのと同じ理由です。

一方で、文章に組み込まれる形で、文中にメンションが現れることがあります。このような場合は、敬称を付けても良いでしょう。文章としても、敬称が付いていた方が自然に感じられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167110/41aeaad6-c933-b13d-1985-7f884eb4cd53.png)

# さいごに

「僕が実践する最強のメンションさん付けポリシー」でした。悩んだときは参考にしてください。

※ 個人的には付いていても付いていなくてもどちらでも全く気にしないのですが…
※ 異論お待ちしております
Getform.ioの使い方をまとめてみた

## Getformとは

公式サイト:[Getform](https://getform.io/)

「Getform」は、WebサイトやWebアプリに簡単にフォーム送信を実装できるサービス。
サーバーの設定とか難しいバックエンドの作成も不要。
本番環境にデプロイする必要もなく、localhost環境で動作を確認できる。

##### ① Getformのアカウントを作る

リンクをクリックすると、下記のページが開き、
「Generate endpoint for free」をクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/380890e0-67e9-24c0-3107-3dba7dec8852.png)

##### ②以下の3つを入力する

『名前』『メールアドレス』『パスワード』

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/57c5b43f-6509-7fec-0260-8dd53281d462.png)

入力したら「Sign up」をクリックする。

##### ③「Verify email」をクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/3c7a94ec-0d4f-0923-f660-0d39deaa26ac.png)

## エンドポイントを作る

##### ① アカウントを作ったら、今度はエンドポイントを作成する

※ Getformの無料プランでは、作れるエンドポイントは1つのみ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/1849b39b-633d-3989-4ce9-143a72a6bf82.png)

##### ② 下記の画面で、「+ Create...」をクリックする

以下の2つを決める。
『エンドポイントの名前』『タイムゾーン』

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/3b8f515f-13cb-176c-341f-e7cb6d1d0bd2.png)

##### ③ 入力したら「Create」をクリックする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/4516e919-a624-1546-5f9f-d27395e899ae.png)

## お問い合わせを送信してみる

サイトは実際のネット環境に公開する必要はなく、
テスト環境（localhost環境）で動作確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/a1fbe9e4-a5a5-512e-0170-f613835ac4b7.png)

『お名前』『メールアドレス』『お問い合わせ内容』

上記を入力して「送信する」をクリックする。すると、Getformのページが表示されて「Thank You!」というメッセージが出る。「Back to Previous Page」をクリックすれば、元のお問い合わせページに戻る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/6aa9a584-5cdd-d898-a0b6-8db49a0cb4c0.png)


## 参考サイト
[【フォーム送信を実装】Getform.ioの使い方を解説【HTMLのみでOK】](https://ralacode.com/blog/post/create-contact-form-with-getform-io-in-react/#:~:text=%E3%80%8CGetform%E3%80%8D%E3%81%AF%E3%80%81Web%E3%82%B5%E3%82%A4%E3%83%88,%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%A7%E3%81%99%E3%80%82&text=%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%A8%E3%81%8B%E9%9B%A3%E3%81%97%E3%81%84%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%81%AE%E4%BD%9C%E6%88%90%E3%82%82%E4%B8%8D%E8%A6%81%E3%80%82)
DTO(Data Transfer Object)

# DTOとは
- オブジェクト指向プログラミングで用いられる**デザインパターン**の一つ
- 関連するデータをまとめ、データの格納・読み出しのためのメソッドを定義したオブジェクト
- データの**受け渡し専用**クラス
- プログラム間でデータを効率良く、互いに利用しやすい形式で渡す手段

# 詳細
DTOにはビジネスロジックを含まない。
データを管理するためのフィールド変数と、
フィールド変数に対応したゲッター・セッターが定義されるのが一般的。

# 例

```php
<?php

class HogeDto
{
  private string $id;
  private HogeValueObject $value;

  public function __construct(array $parameter)
  {
    $this->id = $parameter['id'];
    $this->value = $parameter['value'];
  }

  public function id(): string
  {
    return $this->id;
  }

  public function hogeValue(): HogeValueObject
  {
    return $this->value;
  }
}
```

# 参考
[DTO（Data Transfer Object）とは - IT用語辞典 e-Words](https://e-words.jp/w/DTO.html#:~:text=DTO%E3%81%A8%E3%81%AF%E3%80%81%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91,%E6%B8%A1%E3%81%99%E9%9A%9B%E3%81%AB%E7%94%A8%E3%81%84%E3%82%89%E3%82%8C%E3%82%8B%E3%80%82)
[DTO作るとき、パラメーター多すぎ問題どうしよう。【PHP】](https://zenn.dev/tyamahori/scraps/e476852498942a)
VPCドメインのOpenSearch dashboardにアクセスする方法


# 初めに：OpenSearchのドメイン

OpenSearchを構築するときは
- パブリックドメイン
- VPCドメイン

のどちらかを選択して構築します。
パブリックドメインで構築するとインターネットに接続されたあらゆるデバイスからアクセスできるような形でデプロイが行われますが、VPCドメインですと指定したVPCの中でOpenSearchがデプロイされますので、OpenSearchにアクセスするときはプロキシを用意するなど一手間が必要になります。
セキュリティを考慮したプロダクション環境ではVPCドメインで構築されることもよく発生すると思います。

# 今回実施すること

OpenSearchにはOpenSearch dashboardというブラウザからアクセスするGUIが提供されています。
VPCドメインでデプロイされたOpenSearch dashboardにアクセスするときは以下のような方法が考えられます。

- VPC内のEC2インスタンスにプロキシを立ててPort Forwardingする
- Client VPNでVPCにアクセス
- VPCにWorkspacesを立てる
- VPC内のWindowsのEC2インスタンスにFleet Managerでアクセスする

ここでは「VPC内のWindowsのEC2インスタンスにFleet Managerでアクセスする」という方法でOpenSearch dashboardに接続してみたいと思います。

# Fleet Managerとは？

Fleet ManagerはAWS Systems Managerの一機能になります。
AWS Systems Manager コンソールからマネージドノードに対してさまざまなタスクを実行できます。
Fleet Managerではリモートデスクトッププロトコル(RDP)を使ってブラウザからWindowsインスタンスに接続できますので、ここではこの機能を使ってWindowsインスタンスに接続し、インスタンス上のブラウザからOpenSearch dashboardにアクセスしてみます。
以下のような構成になります。

![opensearch private.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/d7580cc6-bd95-98da-a545-9b5b67cc78fc.png)

# OpenSearchの構築

構築のポイントをかいつまんで解説します。

- ネットワークについてはVPCアクセスを指定します。今回は2サブネット構成で構築を行います。
- セキュリティグループは自己参照インバウンドルールが設定されており、後述のEC2インスタンスに対しても同じセキュリティグループを設定します。

![opensearch-dashboard-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/b86ef4ad-77ab-57eb-3fb0-41a65568dcc6.png)

- きめ細かなアクセスコントロールは検証の簡易化のため有効化しません。きめ細かなアクセスコントロールはOpenSearch dashboardのログイン検証にも使用されるため本来は有効化すべきです。

![opensearch-dashboard-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/35b49186-cb58-23e6-76c5-a4962a2ba02b.png)

- ドメインアクセスポリシーは使用しません。ドメインアクセスポリシーを有効化するとIAMベースのアクセス制御を行えるようになりますが、認証を通すためにCognitoとの連携が必要になったりするため、今回は検証の簡易化のためドメインアクセスポリシーは使用しません。

![opensearch-dashboard-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/8ddb23b0-9469-6a32-bd9b-92c8100bfef6.png)

# EC2インスタンスの構築

EC2インスタンスの構築を行います。
Fleet ManagerでインスタンスにアクセスするにはインスタンスにSSM Agentが導入されている必要があります。
Windows Server 2019のAMIであればSSM Agentがプリインストールされているため、Windows Server 2019のAMIを使用してインスタンスを構築します。
そのほかの構築時の注意点は以下です。

- AmazonSSMManagedInstanceCoreポリシーを含むインスタンスプロファイルをEC2インスタンスにアタッチする
- セキュリティグループはOpenSearchに割り当てたものと同じものを割り当てる
- セキュリティグループでポート3389は開く必要はない

# OpenSearch dashboardにアクセスしてみる

OpenSearchとEC2インスタンスの構築が終わったので、Fleet Managerを使用してOpenSearch dashboardにブラウザからアクセスできるか確認してみます。

- Fleet Managerに移動し構築したノードIDを選択して「リモートデスクトップ(RDP)との接続」を選択します

![opensearch-dashboard-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/377be27d-acbb-cb5f-57d5-3bdd24e7d527.png)

- 構築時に指定したキーペアを指定してインスタンスにリモートデスクトップ接続します

![opensearch-dashboard-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/3a832445-7fb0-2954-5ffb-04e5d96f584e.png)

- リモートデスクトップ接続できるので、ブラウザを開きOpenSearch dashboardのURLを指定すると以下のようなOpenSearch dashboardの画面が確認できます

![opensearch-dashboard-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276167/53ccd9bd-b130-07cd-4f57-04adf28dbc62.png)

# 終わりに

VPCドメインに構築されたOpenSearch dashboardにFleet Managerで接続する方法を確認してみました。
Fleet ManagerであればRDPポート(3389)を開く必要もないので、よりセキュアにVPCに構築されたOpenSearchにアクセスできることが確認できたと思います。
M5StackをI2Cのスレーブにする

はい、いつもお世話になっております。
今回は素朴な疑問からつまずいたことの備忘録です。

タイトルの通り、「M5StackをI2Cのスレーブ通信をしてみる」というものです。
I2C通信についてはここで触れるほどではないので記載しません。

# M5StackをI2Cのスレーブするだって？！
　そもそもなんでそんな事するの？っていう事になりますが、ほら、あるじゃないですか、なんとなくI2C通信するコード書いたものの、それがちゃんとデータ送信できているかセンドバックみたいに確認したいとき。
例え話とかじゃなくて、実際そういう事になってやってみようとした結果です。

# 結論から言います
　M5Stack（ESP32シリーズコアのマイコン）をArduinoベースでコード書く場合、I2Cスレーブに使うのは標準ライブラリ（M5Stack.hなど）ではサポートされていないので、基本的に非対応。
（一応非公式の ESP32のWireSlave.hというものはあるが・・・）
　[ESP32のスレーブライブラリ](https://github.com/gutierrezps/ESP32_I2C_Slave "ESP32のスレーブライブラリ")
です。

では、高額なI2C通信変換ボードを買うしかないかというと、そうでもありません。
（最近はそれほど高額でもないですが）
ESP32が非対応なだけで、Arduinoは対応しているので、普通にArduino UNOとか使えば、公式のサンプルコードまであります。
　スケッチ例＞（Arduino uno用とか）Wire＞slave_receiver
試みると

```cpp
#include <Wire.h> // I2Cのライブラリ
#define I2C_ADDRESS 0x02 // 今回はスレーブアドレスを0x02にしました

void requestEvent(){
// 自分のIDが呼ばれたときに実行する内容
}
void receiveEvent(int howMany){
// 自分のIDの通信を読み込んだ時に実行する内容
// 引数はコード上使わなくてもINTで宣言しておかないとエラーが出るマイコンもあります（UNOは出ない）
 while(1 < Wire.available()){ // 受信していたらTRUE
 char c= Wire.read(); // 1byte分受信　複数あるならその回数繰り返し
 Serial.print(c); // とりあえず受信した内容を確認したいからPCのシリアル通信へ送信
 }
}

void setup(){
 ...関係ないところは省略...
 Wire.begin(I2C_ADDRESS); // このボードのスレーブIDを決める
 Wire.onReceive(receiveEvent); // 自分のID向けの通信を受信したときに呼ぶ関数

void loop(){
 ...関係ないところは省略...
 // 割り込み処理にしたからここは記載なし

```
と、これだけ。
M5StackのLCDに表示させようと悪戦苦闘したものの、結局諦めてArduino unoつかったらこれで終わりでした。
M5Stackのクラスでできるか試してみたものの、そもそもスレーブアドレス指定なんてないですねぇ
　[M5Stack I2Cクラス](https://github.com/m5stack/m5-docs/blob/master/docs/ja/api/commutil.md)
（もしかすると他に手段があるのかな？）

# 結論は書いたものの
アンオフィシャルとは言うものの、ライブラリがあるっちゃあるので、たぶんできると思います。
そのうち試してみて、何か注意点などまとめて記載する予定
（ライブラリをインストールして、サンプルコード書いてみたけど上手く動かなかった。。。）
ライブラリのインストールは、Arduino IDEのツール＞ライブラリ管理で
ESP32 I2C Slave
を検索すれば、「ESP32 I2C Slave」が出てくるので。

それではまた。
Mahalo
スマホのアドレスバーを考慮していい感じにする

要素をある位置で固定したい時、脳死で```top: calc(100vh - 配置したい位置px);```などを指定。
しかし、デスクトップではうまいこと表示されるものの
スマホで上スクロールした際、出現したsafariのアドレスバーにより消えてしまう事態があり、少し悩んだので備忘。

#### cssで100vhを指定すると、アドレスバーの高さは考慮されない


```css
     .float-nav {
       position: sticky;
       top: calc(100vh - 100px);
     }
```

#### アドレスバーを考慮した高さを取得するには```window.Height```を使用する。

```js
      const windowHeight = window.innerHeight;
      const floatNav = document.getElementById('固定したい要素');

      floatNav.style.top = windowHeight - 100 + 'px';

```
論文まとめ：PFLD: A Practical Facial Landmark Detector

# はじめに
CVPR2019より以下の論文
[1] X. Guo, et. al. "PFLD: A Practical Facial Landmark Detector" CVPR2019
のまとめ

- 公式コード
https://github.com/guoqiangqi/PFLD
その他、有志のpytorch版など多くのコードが存在する

- まとめサイト
akinoriosamura氏のまとめ
https://qiita.com/akinoriosamura/items/657d2ca8cc4163ef13ed

全体像はakinoriosamura氏に譲るとして、ここでは本手法の核心であるloss部分のみまとめ

# 概要
- １枚の画像から顔のランドマークを検出する系のモデル
- 従来の精度、特にin-the-wildな環境での精度を上回りつつ、スマホ等でも運用可能な推論速度を達成した
- in-the-wildへの対応としては顔の正面に対する角度が大きいものをより大きなlossとする、一部が隠れたものや特殊な表情のものにより大きなペナルティを与える、等を行っている
- 推論速度への対応としては、back-boneのネットワークにmobileNet-v2を使用している


# loss
lossを工夫することで角度、occlusion、表情の変化に対応している。

まず、一般的なlossを以下とする。

```math
\mathcal{L} := \frac{1}{M} \sum^M_{m=1} \sum^N_{n=1} \gamma_n \| {\rm d}^m_n \| \tag{1}
```

ここで
$M$: data数
$N$: ランドマーク数
$\gamma\_n$: ランドマークごとの掛け率
$\| {\rm d}\_n^m \|$: 推論値とtargetとの距離。具体的にはL1やL2。

<font color="red"> γに添字 m がないのはなぜ？ </font> 意味からすると、この値はデータごとに変わるはずだが。

これを本手法では以下のようにする。

```math
\mathcal{L} := \frac{1}{M} \sum^M_{m=1} \sum^N_{n=1} \left( \sum^C_{c=1} \omega^c_n \sum^K_{k=1} \left( 1 - \cos \theta^k_n \right) \right) \| {\rm d}^m_n \|_2^2 \tag{2}
```

γが括弧内に置き換わった。

<font color="red"> 同様に括弧内に添字 m がないのはなぜ？既に右上に添字があるからゴチャゴチャになるのを避けたか？ </font>


まず

```math
\sum^K_{k=1} \left( 1 - \cos \theta^k_n \right)
```

の部分だが、これは顔の正面に対するヨー、ピッチ、ロールの３つの角度。よってこれら角度が大きいほどlossへの掛け率が大きくなり、より重点的に学習される。

次に

```math
\sum^C_{c=1} \omega^c_n
```

部分。dataを人の属性、顔の角度、表情、オクルージョンで分類し、data数が少ないカテゴリーほどより大きな重みとなるよう、カテゴリーの数の逆数としている。

# ネットワーク構造と学習の方法

ネットワーク構造は以下。

![スクリーンショット 2022-04-01 14.01.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/77f3c4f3-5091-d535-a2eb-d68b75b7a42f.png)

下側がランドマークを推定する部分。上側の補助的なブランチは学習時のみ使用し、角度を推定する。

back-boneはmobilenet-v2を用いることで高速化を実現している。

# 実験と結果

## 推論速度
まず気になる推論速度から。

(A)はモバイルcpu、(G)はGPU(GTX1080Ti)、(C)パソコンCPUとして、

![スクリーンショット 2022-04-01 14.04.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/7e0ca639-3f38-e074-3b8f-2a191f439dec.png)

となる。より小型のPFLD 0.25Xではモバイルでも7m second。

## 精度

AFLW-full datasetにおける他モデルとの比較は以下。normalized mean errorで見てるので、低い方が精度が良い。

![スクリーンショット 2022-04-01 14.08.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116706/900c9bc9-82dc-2d88-272f-8fd81ca2dd31.png)

1Xだと当時のSOTA。0.25XでもSOTAに近い。
リモートデスクトップ経由でキーボード配列がおかしくなるのを直す

RDP先のコンピュータで英語配列を使いたいのですが、設定>時刻と言語をたどってハードウェアキーボードのの設定を英語配列にしてPC再起動しても、日本語配列になってしまう事象が発生しました。

ネットで調べると、レジストリで```HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\00000411```を編集する方法がでてくるのですが、別のレジストリを編集することで直すことができたのでメモとして残します。

参考にさせていただいたのは[こちら](https://qiita.com/n_bando/items/6679494e5a931425641f)です。

デバイスマネージャーでリモートデスクトップキーボードデバイスというものがあったので、こちらのデバイスインスタンスパスを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620712/cf7e5414-cf75-8c05-bbd5-65c55a50fe7c.png)

レジストリで```HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum```配下のデバイスインスタンスパスと同様のフォルダをたどり(今回の場合、文字列にSession2が含まれているのが目印です)、DWORD値(32bit)で以下パラメータを作成します。

```OverrideKeyboardType``` ```2```
```OverrideKeyboardSubType``` ```7```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620712/827a95eb-c55c-6522-565b-5479d05c1cb8.png)

PCを再起動してRDPし直すと、ちゃんと英語になっていました。
あまりレジストリはさわりたくないのですが、何度設定からキーボード配列を切り替えてPC再起動しても直らずもやもやしていたのですっきりしました。
(```...Keyboard Layouts\00000411```のレジストリの方の対応は知らなかったので試していないのですが、そちらでも直った可能性はあります)
5回目　ネットワークとは

# ネットワークとは


```ruby:qiita.rb
puts 'コード書くときに使えるコードブロック'
```
![ネットワーク1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2606732/5f3f7d32-599b-0479-3cc0-a2f18c83d3a3.png)

第一分岐点（ネットワークを形成するために必要なもの）
第二分岐点（具体例）

#### 初めに
ネットワークについて理解するためにまずは、情報通信が複雑になる理由について明らかにしたいと思う。

ネットワークを例える場合はによく、郵便配達に例えられる。手紙を宛先へ届けるために中継地点であったり、住所が必要になるといった風にイメージがつく。
また、情報分野でいうとラジオや電話、テレビなどが近いと感じた。しかし、これらとは違うものであり、その違いというのは

#### 電話回線
電話にも色々な方法はあるが、基本的なものでは「回線通信」といって一本の回線を一つの通話が独占することになる。極端な例でいえば糸電話を（電信柱、そして地中ケーブルを経由して、電話局）などを経由して遠くまでつながるようになったイメージ。

![ネットワーク２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2606732/fb2652e6-7c48-29ad-4fc4-5fb1ff3ac500.png)

#### データ通信
一方で、データ通信では、「パケット交換方式」という方法が用いられており、データを細かなサイズに分割して、送信してます。そのためデータを送信していないときは、回線を占有しないため一つの回線を複数のデータで共有することができます。車で目的地まで物を届けるようなイメージです。
そのため情報を運ぶための経路は複数かんがえられ、効率的なルートを見つけるためにルータや中継器が必要になります。

#### 主な中継器
・ルーターは二つ以上のネットワーク通しを繋ぐために必要
・スイッチは特定の宛先のみにデータを送るために必要
・ハブは受け取ったデータを接続しているすべてのデバイスに発信する


#### まとめ
今回はネットワークに必要なものを解説した。その中で、データ通信ではパケット通信という方法が用いられておりメリットとしては回線を独占しないということが分かった。、これにより機器やルールが必要になりネットワークを理解するためには多くのことを学ばないといけないことが確認できた。
次回はプロトコルについて解説する。
ベクトル化について


# ベクトル化とは：

- ベクトル化とは、コンピュータのプログラムにおいて、繰り返し処理で配列（ベクトル）の要素をひとつひとつ計算しているような部分を、ベクトル計算機で高速に演算できるよう変形すること。
- ここではbag of wordsという手法で、文字列から数字に変換している。

# TF-IDFによる重み付け：

- ベクトル化の手法として、tf-idf化の方法を用います。

## 1. 必要なモジュール、ライブラリのインポート：

```python
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
```

## 2. 実際に使えるように関数化：

```python
# 形態素解析した文字列をベクトル化する
def vectorization(messages_list):
    docs = np.array(messages_list)
    count = CountVectorizer()
    bags = count.fit_transform(docs)
    features = count.get_feature_names()
    tfidf = TfidfTransformer(use_idf=True, norm='l2', smooth_idf=True)
    np.set_printoptions(precision=2)
    tf_idf = tfidf.fit_transform(bags)
    data = tf_idf.toarray() 
    return data
```

# 参考記事：

- [自然言語処理の基礎である形態素解析からbowによるベクトル化、TF-IDFによる重み付けまで解説](https://note.com/shimakaze_soft/n/nf02b0f8ab0f6)
形態素解析について


# 形態素解析とは：

- 形態素解析とは入力されたコーパスを「名詞」・「動詞」のように**品詞レベルに分類すること**
- 日本語を機械学習する際には、形態素解析が必須となります。

![GIG_deeplearning_5-03.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0c21a833-72c9-4125-9b95-c4bf67523897/GIG_deeplearning_5-03.png)

# MeCabの導入方法：

- 日本語の形態素解析ライブラリで一番メジャーなMeCabを使用します。
- 環境：Colab

## 1.MeCabと辞書のインストール：

```docker
# 形態素分析ライブラリーMeCab と 辞書(mecab-ipadic-NEologd)のインストール 
!apt-get -q -y install sudo file mecab libmecab-dev mecab-ipadic-utf8 git curl python-mecab > /dev/null
!git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git > /dev/null 
!echo yes | mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n > /dev/null 2>&1
!pip install mecab-python3 > /dev/null

# シンボリックリンクによるエラー回避
!ln -s /etc/mecabrc /usr/local/etc/mecabrc
```

## 2.動作確認：

```python
import MeCab

sample_txt = "シックスワンは企画とマーケティングの専門家による、新規事業の立ち上げ支援を行います。"
path = "-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd"
m = MeCab.Tagger(path)
print("Mecab ipadic NEologd:\n",m.parse(sample_txt))
```

- 以下のような実行結果が表示されればOK

```docker
Mecab ipadic NEologd:
シックス	名詞,一般,*,*,*,*,シックス,シックス,シックス
ワン	名詞,一般,*,*,*,*,ワン,ワン,ワン
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
企画	名詞,サ変接続,*,*,*,*,企画,キカク,キカク
と	助詞,並立助詞,*,*,*,*,と,ト,ト
マーケティング	名詞,一般,*,*,*,*,マーケティング,マーケティング,マーケティング
の	助詞,連体化,*,*,*,*,の,ノ,ノ
専門家	名詞,固有名詞,一般,*,*,*,専門家,センモンカ,センモンカ
による	助詞,格助詞,連語,*,*,*,による,ニヨル,ニヨル
、	記号,読点,*,*,*,*,、,、,、
新規	名詞,一般,*,*,*,*,新規,シンキ,シンキ
事業	名詞,一般,*,*,*,*,事業,ジギョウ,ジギョー
の	助詞,連体化,*,*,*,*,の,ノ,ノ
立ち	名詞,一般,*,*,*,*,立ち,タチ,タチ
上げ	名詞,一般,*,*,*,*,上げ,アゲ,アゲ
支援	名詞,サ変接続,*,*,*,*,支援,シエン,シエン
を	助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
行い	動詞,自立,*,*,五段・ワ行促音便,連用形,行う,オコナイ,オコナイ
ます	助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。	記号,句点,*,*,*,*,。,。,。
EOS
```

## 3.実際に使えるように関数化：

```python
def split_text(text):
    tagger = MeCab.Tagger()
    words = []
    for c in tagger.parse(text).splitlines()[:-1]:
        surface, feature = c.split('\t')
        pos = feature.split(',')[0]
        words.append(surface)
    return ' '.join(words)
```

### 3.1コードの補足：

- 4行目のfor文ですが、形態素解析した結果の最後に EOS があるので一個手前までで行う。
- surfaceに単語、featureに解析結果を代入。
- 特定の品詞のみ取り出したい場合は、上記のようにif文で制御してください。

```python
# 単一の品詞のみ取り出したい場合
if pos == '名詞':
    words.append(surface)
# 複数取り出したい場合
if pos in ['名詞', '形容詞', '動詞']:
```

# 参考記事：

- [自然言語処理における前処理の種類とその威力](https://qiita.com/Hironsan/items/2466fe0f344115aff177#%E3%82%B9%E3%83%88%E3%83%83%E3%83%97%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AE%E9%99%A4%E5%8E%BB)
- [【Python】Mecabで形態素解析をする](https://qiita.com/DisneyAladdin/items/eb72a47f6543efe2b951)
2022年4月 AIOps 定点観測

# 概要
AIOps の動向や学習のため、Google で AIOps を検索した結果を記録している記事です。

# 2022年4月1日の検索結果 (リスティング広告)

![20220401_AIOps_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345504/b35d600a-1e3a-a5fd-9f8c-1125e78dcb06.png)


* IBM
* Splunk
* NEC
* AI・業務自動化展

リスティング広告だと、トップが IBM でした。Splunk、NEC が続いています。4番目は展示会ですが、企業以外の広告表示は初めてのパターンです。
先月表示された2社 (ServiceNow、LogicMonitor) が、今月は表示されていません。

先月の結果はこちら --> [2022年3月 AIOps 定点観測](https://qiita.com/mkodera/items/e232f11c0024c938079a)

# 2022年4月1日の検索結果 (広告以外)

![20220401_AIOps_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345504/21e1c157-1a1f-80d6-05e5-45f69427bd37.png)


* Splunk
* JBSVC
* ITmedia
* NTTデータ
* ServiceNow
* 日本情報通信株式会社 (IBM Watson)

広告以外だと、上位3位までは先月と変わりません。その後に NTTデータ、ServiceNow、日本情報通信と続いています。
先月表示されていたマクロセンドが、今月は表示されていません。

先月の結果はこちら --> [2022年3月 AIOps 定点観測](https://qiita.com/mkodera/items/e232f11c0024c938079a)
GitHub CLIでチーム週報を生成する

# 週報の自動生成

毎週の振り返り時に、今取り組んでいるGitHubのIssueや、今週CloseしたIssueが一覧できると進捗確認に便利です。

ということで、自分は毎週チームメンバーのための週報を生成しています。（といいうか前任の方がスクリプトでやっていたので真似しているだけですが・・）

今回はこの週報の生成方法を少し改良したので、その方法を紹介します。

# 今までのやり方

週報は素朴なシェルスクリプトで実装しており、GitHubへのアクセスはcurlで頑張って実行し、jqでMarkdownに整形していました。
認証にはGitHubのパーソナルアクセストークンを利用していました。

```bash
MEMBERS="inajob hogehoge fugafuga" #　チームメンバーを列挙する
TOKEN=XXXXX
GHUSER=ZZZZ
REPO=YYYYY
CLOSED_DATE="2022-03-25"

echo "# 今週閉じたIssue"
echo

for m in $MEMBERS
do
  echo "- $m"
  curl --silent -u $GHUSER:$TOKEN "https://api.github.com/search/issues?q=repo%3A${REPO}+assignee%3A${m}+is%3Aclosed+closed%3A%3E${CLOSED_DATE}" | \
    jq -r '.items[]| "    - [" + .title + "](" + .html_url + ")"'
  echo
done

echo "# 取り組み中のIssue"
echo

for m in $MEMBERS
do
  echo "- $m"
  curl --silent -u $GHUSER:$TOKEN "https://api.github.com/search/issues?q=repo%3A${REPO}+assignee%3A${m}+is%3Aopen" | \
    jq -r '.items[]| "    - [" + .title + "](" + .html_url + ")"'
  echo
done
```

まぁこれはこれで便利でしたが、curlのところがどうにも不恰好だったので、GitHub CLIを使うようにしてみました。

# GitHub CLI を使った方法

事前に`gh auth login`が必要です、またこのスクリプトは対象となるリポジトリをcloneしたディレクトリ配下で実行する必要があります。

APIの引数部分がコマンドライン引数になりスッキリしました。またghコマンドにjqが内蔵されているので、後続のjqコマンドも不要となりました。

```bash
echo "# 今週閉じたIssue"
echo

for m in $MEMBERS
do
  echo "- $m"
  gh issue list --assignee ${m}  --search "is:closed closed:>${CLOSED_DATE}" --json 'title,url' --jq '.[]|"  - [" + .title + "](" + .url + ")"'
  echo
done

echo "# 取り組み中のIssue"
echo

for m in $MEMBERS
do
  echo "- $m"
  gh issue list --assignee ${m} --json 'title,url' --jq '.[]|"  - [" + .title + "](" + .url + ")"'
  echo
done
```

# まとめ

GitHub中心にタスクを管理している場合は、このようにスクリプトを使って、週報を作ることができて便利です。
Python 3.10 以降では Optional や Union をわざわざインポートする必要が無くなって便利

#### Noneを返す場合のある関数の型ヒントを書く場合、以前は以下のように書いていた

```old.py
from pathlib import Path
from typing import Optional


def get_latest_log_path(search_dir: Path) -> Optional[Path]:
    """最新のログファイルのパスを取得する

    Returns:
        Optional[Path]: ログファイルが存在する場合、最新のログファイルのパス。存在しない場合は None
    """
    return max(
        search_dir.glob("**/*.log"),
        key=lambda path: path.stat().st_mtime,
        default=None,
    )


if __name__ == "__main__":
    print(get_latest_log_path(Path("log")))

```

### Python 3.10 以降では以下のように書けるようになった

```new.py
from pathlib import Path


def get_latest_log_path(search_dir: Path) -> Path | None:
    """最新のログファイルのパスを取得する

    Returns:
        Path | None: ログファイルが存在する場合、最新のログファイルのパス。存在しない場合は None
    """
    return max(
        search_dir.glob("**/*.log"),
        key=lambda path: path.stat().st_mtime,
        default=None,
    )


if __name__ == "__main__":
    print(get_latest_log_path(Path("log")))

```

#### 差分

```diff
 from pathlib import Path
-from typing import Optional

-def get_latest_log_path(search_dir: Path) -> Optional[Path]:
+def get_latest_log_path(search_dir: Path) -> Path | None:
     """最新のログファイルのパスを取得する
```

## Optional と Union について

<https://docs.python.org/ja/3/library/typing.html#typing.Union>

>Optional[X] を Union[X, None] の略記として利用することが出来ます  
>バージョン 3.10 で変更: Unions can now be written as X | Y  
>str | None == typing.Optional[str]  

つまり、`Optional[Path]` と `Path | None` は同じということ
それなら後者の方が簡潔で良いと思った

## 追記

記事を作成後に [@ye_lp](https://twitter.com/ye_lp) さんが「もっと良い書き方があるよ」
と Twitter にて教えてくださって、上記のコードになりました  
元のコードは↓でした  

```old.py
import os
from pathlib import Path


def main():
    print(get_latest_log_path())


def get_latest_log_path() -> Path | None:
    """最新のログファイルのパスを取得する"""
    logs: list[os.DirEntry] = []
    with os.scandir(Path("log")) as it:
        for entry in it:
            if entry.name.endswith(".log"):
                logs.append(entry)
    if len(logs) == 0:
        return None
    latest = logs[0]
    for log in logs:
        if log.stat().st_mtime > latest.stat().st_mtime:
            latest = log
    return Path(latest.path)


if __name__ == "__main__":
    main()
```

こうして見ると元々のコードは微妙な書き方をしていたなと思います。  
Pythonic なコードを教えて頂き感謝です！
新規サービスをリリースした話

## 概要

鉄道ファン向けの写真投稿サービス『[**レールビュー**](https://www.sagafactory.net/ProductWebAppRailView/USER/html/home/home.html)』をリリース致しました。
2022年3月リリースです。

## 開発動機

以前から、「個人でサービスが持ちたいな。」と言う思いがりました。
一応、クラウドソーシングでビジネスアイデアを募集したところ、100件くらいのアイデアを頂きました。
それらを精査して、「これなら大丈夫かな。」と言うことで、このサービスを作成することにしました。

## 特徴その１

投稿した写真が、地図の線路上に表示されます（下記の様な感じ）。

![map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207458/1cc04239-96bb-1adb-4e98-7f15944bfe10.png)

![map_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207458/d07cfe26-a5f8-8214-22d5-c211afb04d6d.png)

※一応、デモ版なので正確に線路上に表示されているわけではありません。

## 特徴その２
某SNSサイトの様に一覧表示もできます。

![sns.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207458/2f54a2e9-babf-30ca-8c8e-4b3a31c7bc4b.png)

## 特徴その３

検索条件も充実しています。
全国の鉄道データベースが入っています（多分）。

![search_condition.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207458/2f111c07-fcca-5170-e1a8-b555f97121e3.png)

## 特徴その４

ユーザ周りの機能も充実。
フォロー機能やお気に入り機能などもあります。

![mypage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207458/c4d1cfa0-8b4d-4c34-f287-1eea47a3049a.png)

## 実際の操作画面

[鉄道写真投稿サービス - レールビュー -](https://www.youtube.com/watch?v=4uuRylFOuHI)

## 開発環境
java(servlet), jQuery, docker, vps(複数), linux

## 開発期間
1年くらい

## 苦労した点

### ソフト側

**リファクタリング**
　作っては壊し、作っては壊しの繰り返しで、メンタルをやられました(\*_\*)
　（賽の河原？）

**デザイン**
　css面倒くさい(\*_\*)
　（嫌いではない）

**データ整合性**
　想定外の操作などを考え出すと頭痛い(\*_\*)

### インフラ側

**linux**
　コマンド打ちすぎて、頭痛い(\*_\*)

**設定**
　docker, ssl, mail, ドメイン, cron, アカウント, 権限, etc...(\*_\*)

## 改善点

本当はk8sを使いたかったのですが、サーバのスペック不足で使えませんでした。
CI/CDもやりたかったですが、余裕がなかった。
Fargateなども使えてない。
モダンな言語（Go + Reactとか）を使えばよかった。
~~結局、モダンな技術な何一つ使えていない。~~

## 身についた知識

割と中途半端な知識が身についた。
モダンなフレームワークを使っている訳でもなく、AWSなどのサービスを使っている訳でもない。
取り合えず、サービスの作り方が何となく分かった感じ（技術選定大事）。

## 所感

「個人でサービスが持ちたいな。」と言う思いから始めたが、面倒くさい。。
よくモチベーションが続いたなと思う。
**働きたくない**と言う思いが強いから続けてこれたんだと思うけど、取り合えず、そういった負のエネルギーをプラス（なのか？）に変えて作業を行った。
多分、「これまでの苦労はただの序章に過ぎなかった。。」みたいな展開になるんだろうな（いや、絶対なる）。
と言うことを書いて、フラグを回避しておく。
とは言え、ユーザが増えたら色々と問題は出てくると思う。

## これから

ユーザが増えてくれると嬉しい。
「面白そうだね。」と言う感想はちらほら頂いております。
ツイッターフォロー宜しくお願いします。
[【公式】鉄道写真投稿サービス - レールビュー -](https://twitter.com/RailViewService)
Jitsi MeetをさくらのVPSにインストールしてみた

# サーバーを借りる
https://vps.sakura.ad.jp/

# apacheかnginxをインストールする

# ウェブサーバーが動作しているか確認する
サーバーのIPアドレスをブラウザのアドレスに入れる。  
以下の表示ならOK。
![apache.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274766/b75c0542-9859-6a1a-8076-57247701f27a.png)
![nginx.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274766/2219b853-751b-5277-c072-f761b784e1df.png)

## サーバーが動いていない場合
* [VPSのコンパネ](https://secure.sakura.ad.jp/vps/servers)でパケットフィルターを設定して、TCPの80番と443番を開ける。
* ウェブサーバーのサービスを起動する。

# ドメインを取る
安いドメインは2年目から高くなるから気をつける。

# サブドメインを考える
ドメインを持っている人に「サブドメイン使わせて」とお願いしてもいいかもしれない。

# DNSレコードを編集して借りたサーバーに向ける
ドメインを取得したサービス（OnamaeとかValueDomainとか）から設定する。

# hostsファイルを編集する
hostsファイル（/etc/にある）を編集して、IPアドレスとドメインを対応付ける。

# JitsiMeetのためにUDP10000とTCP4443を開ける
[さくらのサイトのVPS](https://secure.sakura.ad.jp/vps/servers/4403209/network)で設定する。ufwで設定すると書いているサイトがあるが、さくらのVPSでは当てはまらない。

# JitsiMeetのレポジトリを追加してインストールする
https://jitsi.org/downloads/ubuntu-debian-installations-instructions/
rails require permit　メソッドとは

# 結論
データベースに予期せぬ値が入らないように、ユーザーが入力する値を制限するメソッドの一種。
```~.rb
params.require(テーブル名).permit(カラム名)
```

* require: 　〜を要求する
* permit :　許可を与える

# 具体例

投稿アプリにて、ユーザーがデータを入力し保存されるまでの順序

ユーザーがフォーム（記入欄）に入力
```new.html.erb
 <%= form_with model:@user, url: user_path, method: :post, local: true do |f| %>
<p>　名前　: <%= f.text_field :name %></p>
<%= f.submit "送信"　%>
# model:@user　　モデルオブジェクトにユーザーが入力したデータがないか判断する
# url: user_path　　入力した値をどこに渡すか指定
# method: :post　　入力した値がデータベースにない場合→createアクション
# 入力した値がデータベースにある場合→updateアクション
# 入力した値のHTTPメソッドを指定できる
# f.text_field :name 入力した値が nameカラムに入る
# f.submit 送信ボタンが生成されるらしい

```
値を入力し、`送信ボタン`を押すと `model:@user, url: user_path,method: :post`により
```routes.rb
Rails.application.routes.draw do

 post "/users", to: "users#create"

end
```
と処理され

```controller.rb
class UsersController < ApplicationController

 def create
      User.create(user_params)
    end

  private
    def user_params
      params.require(:user).permit(:name)
    end　　　　　　　　　　　　# ここでユーザーの入力した値がuserの中のnameだけに保存されるように制限がかけられる
end

```
となる。

かなり複雑ですね。以上です。
SuperMicroサーバー X11系 BIOS､ファームウェアのアップデート

X11系サーバーについての作業手順 (2022/3)

■Supermicroの用語
- BMC : Baseboard Management Controller
- IPMI : Intelligent Platform Management Interface



# ■ファームウェアのアップデート
BMC/IPMPの管理画面から作業可能

・Firmware Update を選択します。
![2022-04-01_12h35_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/7d2129b9-3c32-25f7-de8a-b4615aec094d.png)

・作業用のPCから、アップデートファイルを選択します。
![2022-04-01_12h43_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/62d87127-5bdf-dc9b-38cc-a7756ab01ff1.png)

・バージョンを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/51bd610d-438d-bf16-29a7-fdfd542cbe49.png)




# ■BIOSのアップデート<BR>　(電源Off必須 ※BIOS設定が初期化されます)

IPMIのライセンスにもよりますが、私の環境では、USBにてアップデートが必要でした。

### ①USBの準備

・コマンド入力が面倒なので、ファイル名を変更しました。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/088853bd-a5dd-e951-009f-cd47d0d68834.png)

・FAT32フォーマットのUSB直下に、以下のファイルをコピーしました。
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/7bdd1606-d233-1326-9c42-9e0e34fce4d4.png)


### ②電源投入後　[F11]キーをON
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/6d713d25-3277-6767-775d-297d5fd8cd3c.png)

### ②USBではなく、　"UEFI:Built-in EFI Shell" を選択
![2022-03-24_09h35_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/bc1505de-9404-c50a-7898-6679280394d1.png)


### ③次の画面でコマンド入力します。
　しかしながら、私の環境では、コマンドの入力例にある
　fs0　ではなく　fs1になっていました。
　また環境によっては､日本語キーボードと認識されませんので、":"の入力につまずきます。<BR>　※[shift]+";" で入力します。

~~~
#説明のコマンド見本
     Shell> fs0:
     fs0:\> cd UEFI
     fs0:\UEFI> flash.nsh X11DPU7.218
~~~

↓説明通りに入力しますが、fs0 に該当ファイルがないので焦ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/d156a69c-568b-4d96-ad38-99ed8a86241e.png)

↓以下の様に入力しました。

~~~
     Shell> fs1:
     fs1:\> flash.nsh X11DGQ.bin
~~~
 
以下のような画面で進んでいきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/ab0af85d-e615-6543-dffd-c8cf4b83a8c5.png)

この画面まで進めば完了の様です。電源off＆Onします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/b296f0f8-f5be-3e36-0238-2ca426e05ac1.png)


### ■最終確認　更新予定のVer3.5に反映されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/99214699-ddcf-fd19-7de5-a0fba3bfab5e.png)



#### 注意事項：ファイル名の入力ミスの場合､このような画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/c1f2d770-bfaf-c01b-e29d-135a623ab122.png)


## 参考にさせていただいたサイト
https://www.sunwaytech.co.jp/news/?c=news_detail&id=19
Google Driveに保存した画像をSORACOM Lagoonで表示する方法

Google Driveに保存した画像データをSORACOM Lagoon で表示する方法を紹介します。
![Qiita素材_イメージをLagoonで表示する方法.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1188107/85b52d00-9ccf-cf55-438d-002f1cf621bb.gif)



# 手順
- イメージファイルをGoogle Driveに格納する。
- 格納したイメージファイルを共有設定にする。
その際、「制限付き」となっているアクセス権限を、「リンクを知っている全員」に変更する。
- リンクをコピーする。
- コピーしたリンク（URL）を、メモ帳などに張り付け、以下の編集をする。
  https://drive.google.com/file/d/{ファイルID}/view?usp=sharing
  →https://drive.google.com/uc?id={ファイルID}
- 上記で作成したURLをImageパネルのImages -> Backgroundに貼り付ける。
SORACOM Lagoonの基本的な操作方法は[＞＞こちらから＜＜](https://users.soracom.io/ja-jp/docs/lagoon-v2/)

---
- 商標
※Googleは、Google LLC の商標または登録商標です。
※SORACOM は、株式会社ソラコムの登録商標または商標です。
※その他本文中に記載されている会社名および商品名は、各社の商標または登録商標です。
Beaconの個体番号（UUID,AdvA,Major,Minorなど）を確認する方法

ビーコンのUUID,AdvA,Major,Minorなどを確認する方法です。

# 手順
- スマートフォンにnRFConnectをダウンロードする。
App Storeは[＞＞こちらから＜＜](https://apps.apple.com/jp/app/nrf-connect-for-mobile/id1054362403)
Google Playは[＞＞こちらから＜＜](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp&hl=ja&gl=US)
- アプリを立ちあげ、SCANを実施する。
このときRSSIなどでフィルターをかけると識別しやすい
![beacon_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1188107/f6ce330c-b322-de5d-544d-d2cf9ced9741.png)


- 表示されたものの中から対象と思われるものを選択する。
このとき中段に表示されているのがAdvA
![beacon_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1188107/a6e69ad8-bc10-d6f3-4cc1-d023099ec143.png)

- 展開された情報の中にUUID,Major,Minorなどの値が確認できる。
![beacon_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1188107/704ef580-1e59-a1df-a4aa-4a1486543818.png)

---
- 商標
※Googleは、Google LLC の商標または登録商標です。
※iBeaconは、米国およびその他の国で登録されたApple Inc.の商標です。
※その他本文中に記載されている会社名および商品名は、各社の商標または登録商標です。
巨大動画ファイル(mp4)の一部分を高速にカット編集する(ffmpeg)

使う動画ファイルはh264/aacで3時間超ありファイルサイズにして6GB超ありますが、うち1か所をカットした動画を作るのに30秒ほどで完了しました。
Windows環境での作業を前提に記載しています。

# 準備

この記事ではffmpegというコマンドラインツールがインストールされ、パスが通っている状態での説明になっています。

（もし入っていない場合はffmpegのzipを入手して、解凍して、binフォルダを動画の作業フォルダにしてしまって、
以降の`ffmpeg`コマンドを`./ffmpeg`コマンドとして読み替えるのが手っ取り早いです。）

適当な作業用フォルダに`source.mp4`というファイル名で配置します。
![2022-04-01_12h18_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67857/6f266159-27ce-ef01-bd45-79662eccbf6b.png)

そのフォルダの何もないところをShift+右クリックして、
（Windows11の場合）「Windowsターミナルで開く」を選択します。
（Windows10の場合）「PowerShellウィンドウをここで開く」を選択します。
![2022-04-01_12h03_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67857/210983df-46d5-0363-5e74-3a68b4baeb8f.png)

`ffmpeg`とコマンドを打ってバージョン情報などが出ることを確認します。
![2022-04-01_11h54_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67857/b7575ba9-1a03-5c20-2134-76951db5f770.png)



# AパートBパートを切り出し

今回source.mp4の10秒～20秒の間を削除した動画を作りたいとして、カット部分より前をAパート、後ろをBパートとして2ファイルに切り分けます。

```:動画先頭～10秒地点までを video-A.mp4 として出力
ffmpeg -i source.mp4 -c copy -to 0:00:10 video-A.mp4
```

```:動画20秒地点～動画末尾までを video-B.mp4 として出力
ffmpeg -i source.mp4 -c copy -ss 0:00:20 video-B.mp4
```


# 結合

ffmpegではここが少し手間で、結合したいファイルを列挙したテキストファイルを`list.txt`として設置します。

```txt:list.txt
file video-A.mp4
file video-B.mp4
```

![2022-04-01_12h15_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67857/93031d87-fb08-8e6b-ea67-8a3bb6087d12.png)

テキストファイルが用意できたらAとBを結合した`video-AB.mp4`を生成します。

```:コマンド
ffmpeg -f concat -i list.txt -c copy video-AB.mp4
```

これで一部分をカットした動画が完成しました。
![2022-04-01_12h21_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67857/8cfcb9c3-7ce4-4819-a6d6-4185f903f9e3.png)


# 補足：複数個所のカット

中間を切り出す時は`-to`と`-ss`両方指定します。

```:動画10分地点～20分地点までを video-B.mp4 として出力する
ffmpeg -i source.mp4 -c copy -ss 0:10:00 -to 0:20:00 video-B.mp4
```

`list.txt`側も3ファイル以上列挙されることになります。

あるいは`video-AB.mp4`を`source.mp4`にリネームして同じ手順を実行することもできます。