OmniAICreator/Qiita-1.07M huggingface dataset:
【Flutter】Cameraライブラリで撮影した画像から高さ・横幅を取得する方法

# はじめに
Cameraライブラリで撮影した画像から高さ・横幅を取得する際に、Image型に変換を行わないと行けないのですが、変換方法によってはheight・widthの情報がnullになってしまい高さ・横幅を取得出来ません。
同じようなやり方で困っている方もいるかも知れませんのでそちらの変換方法について解説していきたいと思います。

# 開発環境
OS: MacOS
Flutter: 3.0.4
Flutter Channel: Stable
Dart: 2.17.5
Android Studio: Artic Fox | 2020.3.1 Patch4
cameraライブラリ: ^0.8.1+3

# 変換方法
前提：takePictureメソッドで撮影した画像が変換対象（XFile型）
①XFile型 → Uint8List型へ変換
②Uint8List型 → Image型へ変換
~~~dart: convert.dart
// 元の撮影画像
final imageForXFile = await controller.takePicture();

// ①XFile型 → Uint8List型へ変換
final imageForUint8 = await File(imageForXFile.path).readAsBytes();

// ②Uint8List型 → Image型へ変換
final imageForImage = decodeImage(imageForUint8)
~~~

# 使用例
Imageのheight・widthプロパティにアクセスできます。
ただしdecodeImageで返却される型はnull許容型のImage?であるため注意してください。
~~~dart: convert.dart
// 高さ
final height = imageForImage?.height

// 横幅
final width = imageForImage?.width
~~~

# 高さ・横幅が取得できない変換方法
変換後のImageのheight・widthの値がnullになってしまう変換方法もご紹介しておきます。
前提：takePictureメソッドで撮影した画像が変換対象（XFile型）
①XFile型 → File型へ変換
②File型 → Image型へ変換
~~~dart: convert.dart
// 元の撮影画像
final imageForXFile = await controller.takePicture();

// ①XFile型 → File型へ変換
final imageForFile = File(XFile.path)

// ②File型 → Image型へ変換
final imageForImage = Image.file(imageForFile)
~~~

# まとめ
XFileをImageに変換する場合は、XFile → Uint8List → Imageの順に変換を行う。
ご覧いただきありがとうございました。
記事に不備や補足等がある場合はコメント頂けると助かります。
[テスト見積もり技術] JSTQB FL 勉強記録①

# はじめに
### JSTQBって？
突然ですがJSTQBという資格をご存じでしょうか？
超簡単に説明するとソフトウェアテストの資格で海外でも使える資格になります。
試験はFoundation LevelとAdvance Levelの2つがあるのですがここではFoundation Levelを扱っていきます。

詳しくは以下のサイトをご参照ください。
[JSTQB認定テスト技術者資格とは？Foundation Levelの難易度は？](https://www.bing.com/ck/a?!&&p=9683e4f25d37a6a2JmltdHM9MTY1ODQ1MzU0MSZpZ3VpZD1mNDQwZTE0OC03MTUwLTRiZjktOTI4YS1mODQ5YjE5MDQ2Y2UmaW5zaWQ9NTE4Mw&ptn=3&hsh=3&fclid=212c1396-095e-11ed-9eb0-2be109d57778&u=a1aHR0cHM6Ly93d3cuc29mdHdhcmVqb2JzLmpwL21lZGlhLzAwMDAyNQ&ntb=1)
### 注意点
自分はもうある程度「JSTQB Foundation シラバス2018対応 第4版」を読んでいます。個人的に「ここ試験にでそう」と思ったところや、「ここ複雑でわかりにくいんだよな・・・」と思ったところを気づいた段階でまとめていくので内容が前後するかとは思いますが、あくまで自分用なので悪しからず。

# 5.2.6 テスト見積もりの技術って？
### 大きく分けて２種類ある
その2種類とはズバリ・・・

**・メトリクスを活用する**
**・専門家の知識を基にする**

の２つだ。その中でも**アジャイル開発**と**シーケンシャル開発**でも分かれてくる。
つまり「バーンダウンチャート」みたいな代表的な名前が４種類出てくる。
こ、これは混同しそう・・・。(^^;
というわけで、一発で視覚的にわかるようにテーブルにしてみた。
|テスト見積もり方法|開発|使用される技術|
|:---:|:---:|:---:|
|メトリクス|アジャイル開発|バーンダウンチャート|
|メトリクス|シーケンシャル開発|欠陥除去モデル|
|専門家の知識|アジャイル開発|プランニングポーカー|
|専門家の知識|シーケンシャル開発|ワイドバンドデルファイ法|

実際に1つ1つ説明していこうとするときりがないため、使用される技術４種類を学ぶにあたって参考にしたサイトを紹介しておく。
(試験合格だけが目標なら上のテーブルを覚えればとりあえず大丈夫な気もする)

---
### バーンダウンチャート
[バーンダウンチャートとは？メリットや作成方法、導入時の注意点を解説](https://www.bing.com/ck/a?!&&p=7942a6ffb2771b34JmltdHM9MTY1ODQ1ODA1MiZpZ3VpZD01ZjI2Y2ZiNS0xNWM5LTRiYjQtYjhhNi1kZjI5OTMzZTVhNmEmaW5zaWQ9NTE1NQ&ptn=3&hsh=3&fclid=a2537b9e-0968-11ed-b2c9-d5c07b2c5867&u=a1aHR0cHM6Ly9wcm9kdWN0cy5zaW50LmNvLmpwL29icG0vYmxvZy9idXJuLWRvd24tY2hhcnQ&ntb=1)
### 欠陥除去モデル
これは正直「これだ！」というサイトがなく、JSTQB Foundation シラバス2018対応 第4版から引用しておきます。（良いサイトがあったら教えてください）
>シーケンシャル開発で使用される欠陥除去モデルは、メトリクスをベースにした技術の1つと言えます。過去のプロジェクトで検出した欠陥数、およびその欠陥を除去するためにかけた時間をベースに、対象のプロジェクトの工数を見るからです。

### プランニングポーカー
[プランニングポーカーとは？～アジャイル開発の工数見積もり算出～](https://www.bing.com/ck/a?!&&p=bb88a53309c38669JmltdHM9MTY1ODQ1OTMwNSZpZ3VpZD01OTAwNjlhMS02MDhiLTRjMjQtOTczZi00MzE1ZmE3ZTczYTgmaW5zaWQ9NTE1OQ&ptn=3&hsh=3&fclid=8d3c8bc7-096b-11ed-97a4-ab647b9e2c79&u=a1aHR0cDovL21pc2xlYWQuanAvNzg2OC5odG1s&ntb=1)

[みんなで見積もれ！アジャイルな見積もり手法「プランニングポーカー」のやり方](https://www.bing.com/ck/a?!&&p=55be412bbd3b482cJmltdHM9MTY1ODQ1OTMwNSZpZ3VpZD01OTAwNjlhMS02MDhiLTRjMjQtOTczZi00MzE1ZmE3ZTczYTgmaW5zaWQ9NTE3OA&ptn=3&hsh=3&fclid=8d3d0a0b-096b-11ed-943e-61c5f7d614e1&u=a1aHR0cHM6Ly93d3cubW9mLW1vZi5jby5qcC9ibG9nL2NvbHVtbi9hZ2lsZS1lc3RpbWF0aW9uLXBsYW5uaW5nLXBva2Vy&ntb=1)
結構おもしろそう・・・。

### ワイドバンドデルファイ法
[情報マネジメント用語辞典　デルファイ法（でるふぁいほう）](https://www.bing.com/ck/a?!&&p=e2efa419c28a6bdeJmltdHM9MTY1ODQ1OTg2NiZpZ3VpZD1hZTZlYjYxZS01MGJiLTQ2OGMtODA5ZS1lNjcwNGVlOGU2YjQmaW5zaWQ9NTIxNw&ptn=3&hsh=3&fclid=db32966f-096c-11ed-9a4d-3c9b4cfde204&u=a1aHR0cHM6Ly93d3cuaXRtZWRpYS5jby5qcC9pbS9hcnRpY2xlcy8wODA1LzI2L25ld3MxMzAuaHRtbA&ntb=1)

# 結局、メトリクスと専門家どっちがいいの？
### 答えは状況次第、どちらも一長一短！
過去のプロジェクトと似ているプロジェクトであれば、過去のメトリクスを参考にできるため、メトリクスの方が効果を発揮するだろう。
しかし、規模が違ったり、ドメインが違ったりすると、あまり効果を得られなくなる。
　
専門家が見積もる方式もテスト工数を見積もるのではなく、納期と費用に合わせて帳尻を合わせるようなバイアスがかかる恐れがあるため、妥当な数字か確認する必要がある。よって、
### できれば両者の数字を見比べるのが良い！
という結論に至る。
# おわりに
今回の記事はあくまで自分が見直す用ですが、同じJSTQBの受験を考えている人の参考になったのなら幸いです。
気が向いたらまた投稿しようと思います。
「AWS CDKのコマンドオプションでサービスのENV変えてーなー」と感じた方へ

CDKでStackを簡単に組めるのはとても便利ですよね。

ただ、デプロイ先のアカウント(dev / test / prodなど)によって、
例えば
・APIの制限
・消費ユニットの制御
・テストデータの参照
などをしたい時、変数で簡単に制御できればいいなと思ったので、
今回復習も兼ねてデプロイオプションでサービスのenv値を変更する方法を紹介します。

編集するファイルは全部で3つです。
① app.py
② cdk.json
③ *_stack.py (自身のstackファイル)
   →以下StackファイルはDynamoLambdaStackとします。

### ① app.py
```python
#!/usr/bin/env python3

from aws_cdk import App

from dynamodb_lambda.dynamodb_lambda_stack import DynamodbLambdaStack


app = App()
# 以下2行追加
cdk_env = app.node.try_get_context("cdk_env")
print(f"you will deploy env_state for {cdk_env}")

DynamodbLambdaStack(app, "hoge-service",cdk_env=cdk_env)

app.synth()
```

cdkでデプロイする時に、このファイルでcdkのコマンドファイルを見てくれます。
ここでは、コマンドに```cdk_env=```の値を見てくれます。
```print(f"you will deploy env_state for {cdk_env}")```は、どのオプションでデプロイしたかをlogで出すために書いています。

### ② cdk.json
```python
{
  "app": "python3 app.py",
  "watch" :[
    "./lambda/**"
  ],
  "context": {
    "cdk_env": "dev", ← 追加
    "environment": {
      "dev": {
        "state_name": "dev"
      },
      "prod": {
        "state_name": "prod"
      }
    }
  }
}

```
cdkデプロイをしたときの初期値をここで編集することができます。
例えば、「cdk_env="prod"のときは"prod"を値で見て欲しいけど、特に何も指定しないなら"dev"で値を見てほしい」
といったケースに有効です。
以下、例です。
```
$ cdk --profile sandbox1 deploy
cdk_env=dev で反映
$ cdk --profile service-prod deploy -c cdk_env=prod
cdk_env=prod で反映
```

### ③ *_stack.py
```python
class DynamodbLambdaStack(Stack):
    """ AWS HogeStack
    > HogeServieの説明
    """
    def __init__(self, scope: Construct, id: str, *, cdk_env, **kwargs) -> None:
        super().__init__(scope, id, **kwargs)
　　　　　　　　　　　　　　　
        # 以下サービスリソース定義
        # cdk_envでコマンドオプションのcdk_env値をみてくれます。

        # Lambda
        get_data_lambda = aws_lambda.Function(
            self,
            "get-data",
            runtime=aws_lambda.Runtime.PYTHON_3_8,
            code=aws_lambda.Code.from_asset("./lambda/step-functions/parallels/get-data"),
            handler="lambda_function.lambda_handler",
            log_retention=aws_logs.RetentionDays.ONE_WEEK
        )
        ## 環境変数
        get_data_lambda.add_environment("TZ", "Asia/Tokyo")
        get_data_lambda.add_environment("CDK_ENV", cdk_env) ←設定

```

```def __init__()```の```**kwargs```の1個前に```cdk_env```を追加すると、このStack内でcdk_envの値を参照できます。
今回の例では、Lambdaの環境変数```CDK_ENV ```に直接cdk_envの値を設定しています。
他にも、SSMのパラーメーター参照やStack内でifで分岐を作れば条件に応じて柔軟に変数を変えたり、あるいはテストサービスだけデプロイするなど大胆自由に設定できます。


# まとめ
APIの使用制限やサービスの使用管理などに有用かなと思います。
MySQL5.6->MySQL8.0へのバージョンアップで対応したこと4つ

MySQL5.6->MySQL8.0という大幅なバージョンアップで、コード修正しなければならない部分が多数あったので、今回対応した4つの変更点について書きます。

# MySQL5.7以降の変更
## distinct使用時、selectリストにない項目でORDER BY すると怒られるようになった

例えば以下のように注文テーブル、ユーザーテーブルがあるとします。

ORDERTABLE
| orderid(主キー) | orderdate | fixdate | userid |
|:-:|:-:|:-:|:-:|
| OD0001 | 2022-07-10 12:34:56 | 2022-07-20 12:34:56 | 0001 |
| OD0002 | 2022-07-12 12:34:56 |  | 0002 |
| OD0003 | 2022-07-11 12:34:56 |  | 0001 |

USERTABLE
| userid(主キー) | username |
|:-:|:-:|
| 0001 | 山田太郎 |
| 0002 | 田中花子 |

「fixdateが入っていない」レコードから「orderid, username」を「orderdate」順に取得したい場合、下記のようなSQLになります。
```sql
SELECT distinct o.orderid,u.username
FROM ORDERTABLE o INNER JOIN USERTABLE u ON o.userid = u.userid
WHERE o.fixdate IS NULL
ORDER BY o.orderdate;
```
MySQL5.7以降だとこのSQLで怒られます。
```
PDOException: SQLSTATE[HY000]: General error: 3065 Expression #1 of ORDER BY clause is not in SELECT list, references column 'db_name.o.orderdate' which is not in SELECT list; this is incompatible with DISTINCT
```
これは、MySQL設定の ONLY_FULL_GROUP_BY モードが有効になっているから。
MySQL5.7から設定デフォルト値として「ONLY_FULL_GROUP_BY」が追加になったため、設定を変えなければこのエラーに遭遇すると思います。

対策としては下記が挙げられます。
* selectリストにORDER BY項目を追加
* ORDER BY項目にselectリストのカラムを追加
* ONLY_FULL_GROUP_BY モードを無効にする
* サブクエリでORDER BY項目ありのselect distinctをし、外側でORDER BYする

今回はselect項目を増やしたくなかったので、以下のサブクエリで対応しました。
```sql
SELECT t1.orderid,t1.username
FROM (
    SELECT distinct o.orderid,o.orderdate,u.username
    FROM ORDERTABLE o INNER JOIN USERTABLE u ON o.userid = u.userid
    WHERE o.fixdate IS NULL
    ) AS t1
ORDER BY t1.orderdate;
```
なお、今回distinct項目に「o.orderdate」が追加されていますが、同テーブルの主キー（ユニークキー）である「o.orderid」も入っているので、この変更によって取得行数が増えることはありません。（o.orderid,u.usernameが同じ かつ o.orderdate が異なることはあり得ない）

### 参考
* [MySQL :: MySQL 8.0 リファレンスマニュアル :: 12.20.3 MySQL での GROUP BY の処理](https://dev.mysql.com/doc/refman/8.0/ja/group-by-handling.html)
* [MySQLでdistinctが効かない。mysqlバージョン5.7からの変更。｜ prophet STAFF BLOG](https://blog.prophet.jp/2951/)


# MySQL8.0以降の変更
## 「GROUP BYによる暗黙のソート」がされなくなる
今までGROUP BYで暗黙のソートが行われていましたが、MySQL8からソートしなくなったので取得順が一定でなくなりました。
プロダクトコードで暗黙のソートに依存してしまっている場合はもちろん、ユニットテストで並び順を検証している場合もテスト実行結果が変わってくるので対応が必要になります。

公式リファレンスより：
> 以前に GROUP BY ソートに依存していたクエリーでは、以前の MySQL バージョンとは異なる結果が生成される場合があります。 特定のソート順序を生成するには、ORDER BY 句を指定します。

対応策としては公式の通り、ソートしたいときはちゃんとORDER BYしてねというところですね。

### 参考
* [MySQL :: MySQL 8.0 リファレンスマニュアル :: 2.11.4 MySQL 8.0 での変更](https://dev.mysql.com/doc/refman/8.0/ja/upgrading-from-previous-series.html#upgrade-sql-changes)
* [日々の覚書: MySQL 8.0では「GROUP BYによる暗黙のソート」がされなくなるよ](https://yoku0825.blogspot.com/2018/11/mysql-80group-by.html)


## datetime(date)型に変換できない文字列とdatetime型を比較するSQLが不正になった
datetime型カラムと空文字と比較しているSQLでエラーが出るようになりました。
datetime型カラムにはそもそも空文字は登録できないので、比較している所を削除することで対応しました。（なんで比較してるんだって感じですが）

### 参考
* [MySQL 8.0.15 の前後で変わった文字列と DATE 型の比較について - それが僕には楽しかったんです。](https://rabbitfoot141.hatenablog.com/entry/2020/07/01/215728)


## Rankが予約語になった
これは何とも…「Rank」って普通にカラム名とかで使うような気がするんですが…困っちゃいますね…
対応策としてはエスケープするか、予約語になっていない「〇〇_RANK」とかにカラム名変えるかしかないですね。
今回対応したときはカラムの使用箇所が少なかったので、カラム名変更で対応しました。

### 参考
* [MySQL :: MySQL 8.0 リファレンスマニュアル :: 9.3 キーワードと予約語](https://dev.mysql.com/doc/refman/8.0/ja/keywords.html#keywords-new-8-0-R)
Zig言語をUbuntu(WSL2)にインストール

WSL2ではSnapdが使えないため、代わりに[リリースページ](https://ziglang.org/download/)のビルド済みバイナリを用いてインストールする。

```bash
# ビルド済みバイナリをダウンロード（URLは適宜置き換え）
$ wget https://ziglang.org/builds/zig-linux-x86_64-0.10.0-dev.3051+b8bf5de75.tar.xz

# インストールディレクトリを作成
$ sudo mkdir /usr/local/zig

# インストールディレクトリに解凍（ファイル名は適宜置き換え）
sudo tar Jxvf zig-linux-x86_64-0.10.0-dev.3051+b8bf5de75.tar.xz -C /usr/local/zig --strip-components 1
```

特に問題がなければ`/usr/local/zig`にZigの実行ファイル等が配置されるはずである。  
忘れずにパスも通しておこう。

```bash:~/.bash_profile
export PATH=/usr/local/zig:$PATH
```

追記できたら、`~/.bash_profile`を再度読み込んで`zig version`を実行してみよう。

```bash
$ source ~/.bash_profile
$ zig version
0.10.0-dev.3051+b8bf5de75
```

バージョンが表示されていればインストール成功である。
【EC-CUBE4系】mailcatcherの設定方法

# 現象
EC-CUBEのローカル環境でメールを使ったテストを行う際に重宝するmailcatcherですが、なぜか公式のとおりに設定してもうまくいきませんでした...

開発環境はもちろんDockerでコンテナも作成、立上げ済み。なのに使えない！
docoker-compose.ymlには以下のように記載。
```docker-compose.yml
version: '3'

services:
  mailcatcher:
          image: schickling/mailcatcher
          ports:
              - "1080:1080"
              - "1025:1025"
```


.envも公式のいう通り以下のように記載。

```.env
MAILER_URL=smtp://127.0.0.1:1025
```

これでhttp://127.0.0.1:1080/ にアクセスするが
EC-CUBEからメールを飛ばしているのに何も出ない！

![スクリーンショット 2022-07-22 120533.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748791/45597461-b8f2-416f-b353-807c1923625f.png)

# 原因
バージョンによってMAILER_URLの書き方が違うらしいです。（知らんがな）
上記の書き方は**ver.4.0.5以降**の書き方のようです。

ちなみにverの確認方法はこちら↓

https://www.thank-u.net/blog/eccube/how_to_know_eccube_version/#toc6


私のEC-CUBEは4.0.3でしたのでこの書き方だと動きません。。。

4.0.5以下の場合はこのように書くそうです。
```.env
MAILER_URL=smtp://mailcatcher:1025
```


これで無事メールが確認できる状態になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748791/98eaca32-fbb4-5a43-abb4-cf9e23a9058c.png)
Ansible でblock に付けたwhen は内包される各task に展開される

## 概要  
Ansible でblock とwhen を組み合わせて使っていた際に個人的に直感に反した挙動をしたので共有

## 動作確認バージョン  
ansible-core 2.13.1

## 当初想定していた動作  
以下のようなPlaybook があったとします
```yaml
---
- name: Test block when condition
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Block statement
      when: block_condition is not defined
      block:
        - name: Set fact block_condition
          ansible.builtin.set_fact:
            block_condition: hoge

        - name: Debug block_condition
          ansible.builtin.debug:
            var: block_condition

```

リファレンスを読んでいなかった私はブロック入る際の条件を`when` で指定できると思っていました

なので、`Block statement` タスク開始時点で`block_condition `が定義されていなければ、
`block` 内の全てのタスクが実行されるだろうと思っていた訳です


## 実際の動作  
ところが、実際は`block` に付けたディレクティブ(`when`) は内包される各`task` に展開されるようです

こんなイメージ

```yaml
---
- name: Test block when condition
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Set fact block_condition
      ansible.builtin.set_fact:
        block_condition: hoge
      when: block_condition is not defined

    - name: Debug block_condition
      ansible.builtin.debug:
        var: block_condition
      when: block_condition is not defined

```

実際に1つ目のPlaybook を動かすと2番目のタスクはスキップされることが分かります
```
PLAY [Test block when condition] ************************************************************************************************************************************************************************************************

TASK [Set fact block_condition] *************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Debug block_condition] ****************************************************************************************************************************************************************************************************
skipping: [localhost]

PLAY RECAP **********************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0  
```

## よく見るとリファレンスに書いてありました😇  
https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html

> For example, a when statement is applied to the tasks within a block, not to the block itself.

`loop` を除いた全てのディレクティブで同じことが言えるそうですね
> All tasks in a block inherit directives applied at the block level. Most of what you can apply to a single task (with the exception of loops) can be applied at the block level, so blocks make it much easier to set data or directives common to the tasks. The directive does not affect the block itself, it is only inherited by the tasks enclosed by a block

## 気をつけること  
今回の例のように、`block` で指定した`when` の条件が、その`block` 内で変化する場合、知らないとハマります

そうでない場合、知らなくても普通に使えちゃいますね・・・
Cloud Pak for DataにICOS内のデータをフォルダーごと登録する

 IBMのCLoud Pak for Dataにデータを登録する際にフォルダーごとカタログに登録する方法を紹介します。

やりたいことは
　ICOS内のフォルダーにデータがある
　そのフォルダー内データをまとめてCloud版のCloud Pak for Dataに登録する。
　ついでに全てのデータにタグ付けする。

-ICOS(IBM CLoud Object Storage)内のデータ
以下のようにdataフォルダーの中にデータが入っているとします。（MSP 360 Exlorerというツールを使ってICOSの中を表示してます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/62754446-6475-e744-781c-ebeef393a1c4.png)

-Cloud Pak for Dataの中のカタログとプロジェクト
以下のようにBig-dataというプロジェクトとカタログがあります。（登録するのはカタログですがプロジェクト内のツールを使うのでプロジェクトとカタログの両方が必要になります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/490d98dd-24eb-72b3-33d2-571a0d9aa7f0.png)
なおプロジェクト内にICOSへの接続ができている必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/c8600995-d8c9-3bed-29ec-1b1e1bdda660.png)

ではやってみましょう
プロジェクトの画面に行って
右上の青の新規作成をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/15ee73c7-f26e-462d-5a2f-d28b9ff9ea8b.png)
メタデータのインポートを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/0d5089b6-96cd-d1ae-d535-9f1ae159684f.png)
任意の名前をつけます。ここでタグをつけておけば全てのデータの同じタグをつけられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/05b7e7ef-a92b-d526-d8fb-d023712a7b7b.png)
データを登録するカタログを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/c13c0102-ffca-360e-c027-3ee188c53421.png)
データソースを選びます。ここでICOSのフォルダーを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/dc8e6a60-56df-0133-a8b0-47b50b2556fa.png)
そのまま進めるとカタログに全てのデータが登録されます。タグも付けられています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59741/bb7aa725-9148-8f06-e1b2-59343512e1f5.png)
[Flutter] VSCodeでAuto Importがきかない

VSCodeのAutoImportプラグインをInstallしてあるのにできないときは、
dependenciesにインポートしたいライブラリが設定されていることを確認する。

```pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
```

dev_dependenciesにあるものはインポート対象外になるので注意。

```pubspec.yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
```
フライバックコントローラーはどれほど「シンプル」でしょうか？

従来の絶縁型フライバックコンバータのアーキテクチャでは、コンバータの電力レベルは通常最大約60Wです。トランスの巻数比を調整することにより、電源電圧を一次側スイッチと出力電圧の助けを借りて出力電圧に変換することができます。情報は、この出力電圧を可能な限り安定に保つために、フィードバックパスを介して一次側のPWMジェネレータに送信されます。出力電圧が高すぎたり低すぎたりすると、PWMジェネレータのデューティサイクルが調整されます。
![sadas.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648141/48bc36bd-ec57-9800-ea6b-43072258246c.jpeg)
このフィードバックパスはコストを追加し、ボード上のスペースを占有し、トランスの絶縁電圧と連動して回路の最大絶縁電圧を決定します。オプトカプラーは一般に経年変化し、その特性は時間とともに変化し、85°Cを超える温度には一般的に適していません。

オプトカプラに加えて、3番目のトランス巻線を使用して出力電圧の状態に関する情報を提供できます。この情報に基づいて出力電圧を調整できます。ただし、この余分なトランス巻線はトランスをより高価にし、出力電圧の調整は特に正確ではありません。

より良い代替案は、オプトカプラーとオプトカプラーの二次側制御モジュールを交換用デバイスと交換することです。iCouplerを使用した[ADuM3190](https://www.chimicron.com/product/06869172-ADUM3190ARQZ.html)が誕生しました ® 絶縁技術は、誘導結合を介してガルバニック絶縁を介してフィードバック信号を送信します（つまり、オプトカプラーは必要ありません）。

しかし、それを超えて、別のオプションがあります。特に優れた解決策は、離散フィードバックパスを完全に排除することです。図2は、ディスクリートフィードバックループを必要としないフライバックコンバーターを示しています。図2は、適切なコンバータICであるADIの[LT8300](https://www.chimicron.com/product/06783316-LT8300MPS5-TRPBF.html)を示しています。これは、二次側から一次側への電圧フライバックによってPWMジェネレータによって生成されるデューティサイクルを調整するかどうか、および調整する方法を識別できます。このソリューションの利点は、フォトカプラやその他のフィードバック回路が不要なことです。これにより、コストとスペースを節約できます。また、フィードバックパスの最大絶縁電圧に関連する制限効果はありません。使用する変圧器が特定の絶縁電圧用に設計されている限り、回路全体がその最大絶縁電圧で動作できます。 

この概念は、境界モードの規制に基づいています。この時点で、2次電流は各サイクルでゼロアンペアに低下します。次に、フライバックトランスの一次巻線の出力電圧を測定して、一次調整に使用できます。
![hjkmhj.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648141/56bf06f4-f95e-ccf3-fe9b-cb94c0e72fde.jpeg)
ディスクリートフィードバックパスのないこのような回路を特定のアプリケーションで使用できるかどうかは、必要な出力電圧レギュレーションの精度に大きく依存します。この精度は±1％よりも優れている場合がありますが、アプリケーションによっては偏差が大きくなる場合もあります。

出力電圧は次のように計算できます。
![gfbbcf.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648141/96fad9b4-e250-2bb5-9ca0-5ceaef2a0307.jpeg)

Rfbを図2に示します。これを使用して出力電圧を調整できます。Npsは使用するトランスの巻数比、Vfは2次フライバックダイオードの両端の電圧降下です。通常、温度と密接に関係しています。12Vや24Vなど、より高い値に設定された出力電圧の場合、温度によるVfへの絶対的な影響は少なくなります。3.3 V以下に設定された出力電圧の場合、温度は出力電圧に非常に大きな影響を及ぼします。オプトカプラーのない一部のシリーズには、さまざまな温度でのさまざまな整流ダイオードの電圧降下を補償するための温度補正が組み込まれています。

電圧調整機能が適切に機能するために、通常、出力には最小負荷もあります。LT8300では、最大可能負荷の約0.5％です。

結論

フライバックコントローラーはディスクリートフィードバックパスを使用しませんが、一次トランス巻線を介して制御できるため、設計が簡素化され、故障しやすいオプトカプラーの使用を回避できます。

LT8300

•入力電圧範囲：
6V〜100V•260mA、150V内部DMOS電源スイッチ
•低静止電流： 
•70µA（スリープモード）
•330µA（アクティブモード）
•境界動作モード（高負荷時）
•低リップルバーストバーストモード® （軽負荷時）
•全出力の最小負荷<0.5％（標準）
kalidofaceを使ってZoomでVtuberになる方法〜ウィンドキャプチャ編〜

# 最初に
ここではMacの環境で動作させていますが、Windowsの環境でも同じ手順で動作すると思います。

# 目次
[動作環境](#動作環境)
[obsのダウンロード](#obsのダウンロード)
[obsの準備](#obsの準備)
[kalidofaceの準備](#kalidofaceの準備)
[問題点](#問題点)
[参考文献](#参考文献)

# 動作環境
- macOS version 12.3.1 Monterey Intel Core i9(FaceTime HD Camera) 
- OBS Studio 27.2.2(64bit)
- Zoom version 5.11.1(64bit)

# obsのダウンロード
まずはOBSをダウンロード。[この記事](https://haishin.you-soku.net/obs-installation)がとても分かりやすいので、この記事を参考にOBSをダウンロードします。初回起動時の自動構成ウィザードの設定が~~めんどくさい~~よく分からない人は[こちらの記事](https://vip-jikkyo.net/how-to-download-obs#auto-configuration-wizard)を参考にして、全てキャンセルを選択してください。

# obsの準備
まずはOBSを開いて、Sourcesのところで左下の+ボタンを選択。そして出てきた選択肢からWindowCaptureを選択します。
![Screen Shot 2022-07-19 at 14.39.26.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/570f71aa-f7d0-ccfe-b871-7864f84d607b.jpeg)
名前や詳細を書くタブが出てくるので、`Window Capture`を好きな名前に変えて（今回はchromeとする）OKボタンを押してください。

Window Captureを始めて使う人は、Captureする選択肢の中にウェブブラウザー（今回はchrome）のウィンドウがないはずです。
![Screen Shot 2022-07-19 at 14.40.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/33b77881-1158-4803-4006-0e2e9b8fcc76.jpeg)

なので、Macのシステム環境設定からセキュリティとプライバシーを選択して、画面収録の項目でOBSにチェックを入れます。
![Screen Shot 2022-07-19 at 14.41.13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/69dbdc57-9821-5416-90b8-679d1c33b93f.jpeg)

OBSにチェックを入れて、OBSを再起動すると、`Google Chome`のウィンドウが選べるようになるはずです。（chomeのウィンドウを開いておく必要あり）
![Screen Shot 2022-07-19 at 17.12.27.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/44c1b33d-f68d-14c6-b179-687ff71c4b2a.jpeg)

`Google Chome`のウィンドウを選択すると、OBS内のウィンドウサイズが大きくなっているので、ちょうどいい大きさに調節します。
![OSBWindowSizeChange.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/a078462c-1939-ec63-f77d-cdaa11a40083.gif)

# kalidofaceの準備
ウェブブラウザーのウィンドウで[kalidoface](https://3d.kalidoface.com/)を開きます。kaidofaceを開いたら、右上のSettingsからフェイストラッキングとボディトラッキングの選択や使うカメラの選択をします。Settingsが終わったら、右下の青いカメラボタンを押してカメラを起動させます。初回時は時間がかかるので気長に待ちましょう！
![Screen Shot 2022-07-22 at 11.02.16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/4575899c-b514-5d47-82d6-042c6cf46c89.jpeg)

kalidofaceでカメラを起動させ、アバターが動くようになったらOBS内で`仮想カメラを開始`を選択してください。
![Screen Shot 2022-07-19 at 17.53.07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/021d7e65-474d-4a20-8992-e146aa14aee8.jpeg)

あとはZoomのビデオ設定を`OBS Virtual Camera`に変えれば完了です！ 
<iframe width="560" height="315" src="https://www.youtube.com/embed/DAhktA0lBws" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 問題点
kalidofaceを開いているウェブブラウザーのウィンドウを最小化すると、OBSのWindowCaptureが使えなくなるので、Zoomにkalidofaceを映している間はウィンドウをそのままにしておく必要があります。
![OBS Window erorr.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2700137/b744d6ba-49b4-960a-d120-e244c00c0f85.gif)

# 参考文献
- https://haishin.you-soku.net/obs-installation
- https://vip-jikkyo.net/how-to-download-obs#auto-configuration-wizard
- https://www.miucrescent.com/2020/03/03/195/
- https://3d.kalidoface.com/
AWS S3のCSV(Shift JIS)をLambda+pandasで読み込んでみた

windowsユーザー経由で上がるファイルはsjisなことが多く、csvの文字コードがUTF-8じゃないが故に
```
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 0
```
といったあるあるエラーをS3ファイル->Lambda pandasで解決してみました。

問題はCSVがUTF-8でないことはもちろんですが、もう１つはS3のレスが```StreamingBody```なところ。
既存ファイルをUTF-8で上げなおしてくれなんて言えないのでシステムで読み込む方法を模索。
今回のケースでは、
```python
pandas.read_csv(response, encoding="shift-jis")
とか
pandas.read_csv(response, encoding ="cp932")
```
と単純にぶっ込んでも同じエラーになりました。

そこで、
```python
with codecs.open("file/to/path", "r", "Shift-JIS", "ignore") as file:
    df = pd.read_table(file, delimiter=",")
    print(df)
```
という感じの記事を見つけましたが、
```codecs.open()```はファイルパスを第１引数に要求しているので、今回のオブジェクトタイプでは使えません。
※ S3オブジェクトを一旦Lambdaのtmpにおいてpathを読み込ませたらワンチャン策として良いかもしれませんが面倒なのでやめました。

なので、以下のようにしてみると読み込めました。
```python
import json
import os
import boto3
from lib import AwsUtils
import pandas
from io import StringIO

def lambda_handler(event, context):
    """ S3にアップロードされたファイル(sjis)を読む """

    # events
    file_name = event.get('file_name')
    # constants
    BUCKET_NAME = os.environ['BUCKET_NAME']

    # ファイルはshift_jisを想定
    s3_client = boto3.client('s3')
    s3_obj = s3_client.get_object(Bucket=self.BUCKET_NAME,Key=f'uploads/{file_name}')
    origin_file_data = s3_obj['Body'].read()
    
    # ↓ココ
    domain_file_data = pandas.read_csv(
        StringIO(origin_file_data.decode("shift_jis")),
        encoding='cp932'
    )
    return domain_file_data

```


pandasの```read_csv()```は path or Object Likeな何か を第１引数に要求しています。
初めに、受けとったオブジェクトを```decode("shift_jis")```し、```encoding='cp932'```で特殊文字の読込みも対応するようにし、pathは渡せないのでdecodeしたオブジェクトをStringIO()でラッピングすることでread_csvに対応させました。

sjisとcp932の違いは下記記事が参考になると思います。
https://qiita.com/nukorea/items/73c2438b1bbc0035b3a5

同じエラーで困ってる方は参考にしてみてください。
【python】であっち向いてホイ



じゃんけんゲームの作り方を見て同じような方法であっち向いてホイもできるのではないかと思い
下記のサイトを参考に作りました。
この後は連勝数の表示や勝った時だけ音が鳴るようにしたいと考えています。


https://flytech.work/blog/16310/#tkinter-2

<h4>環境</h4>
OS Windows10<BR>言語 Python3.8.3<BR>IDE visual studio code


```python
from cProfile import label
from email.mime import base
import tkinter as tk
import random


def btn_click():
    num = random.randint(0,3)
    
    def return_screen():
        baseGround_new_screen.destroy()
    
    baseGround_new_screen = tk.Tk()

    baseGround_new_screen.geometry("300x300")

    baseGround_new_screen.title("結果")

    if i == num:
        label_filename = tk.Label(baseGround_new_screen, text = "あなたの勝ちです。")
    
    elif i != num:
        label_filename = tk.Label(baseGround_new_screen, text = "あなたの負けです。")
    
    label_filename.pack()

    #ボタン処理
    btn_return = tk.Button(baseGround_new_screen, text = "前の画面に戻る", command = return_screen)
    btn_return.pack()
    baseGround_new_screen.mainloop()

baseGround = tk.Tk()

baseGround.geometry("300x300")
baseGround.title("あっち向いてホイ")

label_1 = tk.Label(baseGround, text = "あっち向いてホイをします。")
label_1.pack()

label_2 = tk.Label(baseGround, text = "上下左右のどれかを選択してください。")
label_2.pack()

btn = tk.Button(baseGround, text = "OK", command = btn_click)
btn.place(x = 120, y = 140)

# ラジオボタンに表示する文字
item = ["↑上", "↓下", "←左", "→右"]

# Intavarオブジェクトを作成して変数に代入
val = tk.IntVar()

for i in range(len(item)):
    tk.Radiobutton(baseGround, value = i, variable = val, text = item[i]).pack()

baseGround.mainloop()

```
【備忘録】ミッションプラン作成時にARM/DISARMを指示する方法

## はじめに
Ardupilotで自動運転を行う場合
ARMしてモータを動かせるようにする　⇒　AUTOモードに切り替えて自動運転に移行
といった手順で動かすと思うのですが、機体のそばに人が居る場合は注意しないと怖いかなと思います。
（わかっていると思うので問題はないと思いますが。。。）

ですからAUTOに切り替えてから間をおいて動くように出来たら良いのでは？と思いました。
（切り替えた後に周囲への注意喚起して安全を確保するイメージです）
調べていくと、物理SW（送信機のSWなど）とは別にミッションプラン作成時にモーターのARM/DISARMさせる方法について動画がありましたので、解説されていた方法を基に試した方法について記載したいと思います。

## 環境
|項目|詳細|
|-|-|
|Auto Pilot|Cube Orange|
|PC（OS）|windows11|
|GCS|Misson Planner（1.3.77）|
|Firmware|Ardupilot Rover 4.2.2|

## 設定方法
ミッションプラン作成時に『Waypoint』や『Delay』と併せて『UNKNOWN』コマンドを使用します。
以下に『UNKNOWN』コマンドの設定方法について記載します。

#### 【手順】
　1．フライトプラン画面からコマンド UNKNOWN を選択します
　2．Mavlink ID 入力と表示された画面が表示されますので 218 と入力します
　3．一番左側のセルにファンクション番号 153 を入力します
　4．左から二番目のセルSWの状態を表す番号（0/2）を入力します

## 解説
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064722/bb7fd180-ba5c-640f-8f4a-84d428996b23.png)

UNKNOWNコマンドを使うと MAVLINK ID で設定された動作を行えるようになっています。
補助機能として送信機に割り当てられたSWの機能を実行する MAVLINK ID 218 を入力することで、これらのコマンドが読み込まれて実行されます。

### MAVLINK IDの確認
補助機能として送信機に割り当てられたSWの機能を実行するIDは[こちら](https://github.com/ArduPilot/mavlink/blob/master/message_definitions/v1.0/ardupilotmega.xml)に記載されています。
DO_AUX で検索すると ID 218 がこの機能を実行するIDであることが解ります。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064722/d41b7f78-99d4-0b49-d911-127b66308f3f.png)

### ファンクション番号の確認
ファンクション番号は[こちら](https://github.com/ArduPilot/ardupilot/tree/Rover-4.2/libraries/RC_Channel) に記載されています
ARM で検索すると Rover4.2では 153 が ARM/DISARM に該当するファンクション番号であることが解ります。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064722/325d6bec-62d1-54b7-ee07-ab072010c9b0.png)

## ミッションプラン作成例
AUTO MODE 移行後 10 秒毎に ARM/DISARM を10回繰り返すミッションプランの例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1064722/0ad00b4a-e875-24e2-ca00-9618cd48c73c.png)

## 参考
https://www.youtube.com/watch?v=BK3OsNJoF8A

※ 6:00～12:00 位にかけて紹介した内容について解説されています。
Lineミニアプリ開発の入門の入門

# 
Line内で動作するアプリケーションに興味が出てきて調査した内容をここではまとめています。
Lineミニアプリの概要を知ってもらうにはちょうど良いかなと。

# ミニアプリ
そもそもミニアプリとはなんなのか。これは特定のアプリ上で動作するアプリケーションのこと。
通常、アプリケーションはOS上で動作する。例えばLineはiOSやAndroidといったOS上で動作するアプリ。
で、このOS上で動作するアプリの中で動作するアプリのことをミニアプリと皆さん呼んでいます。
ちなみにミニアプリを持つアプリのことをスーパーアプリとも呼んでいたりする。

# Lineミニアプリを開発するには
Line内で動作するアプリ（ミニアプリ）を開発したい場合、LINE Front-end Framework（略称はLIFF）と呼ばれるLine社が
提供するフレームワークを使用することで開発することができる。そして、LIFFを使って開発されたアプリのことをLIFFアプリと呼ぶ。

# LIFFアプリとLineミニアプリの違いは？
LIFFアプリとLineミニアプリはどちらもLIFF上で動作するアプリケーションなので、LineミニアプリもLIFFアプリと言える。
Lineミニアプリをリリースするという言葉とLIFFアプリをリリースするという言葉には大きな違いがある。
どういうことかというと、Lineミニアプリをリリースしたい場合はLine社の審査を通過しないとリリースすることができない。
一方でLIFFアプリは審査なくリリースすることができる。
基本的にはLIFFアプリの開発メインで考えれば良いと思うが以下のリンクのようにLineミニアプリでしか利用できない機能もあるので、要件次第ではLineミニアプリの開発をしないいけない。
https://developers.line.biz/ja/docs/line-mini-app/discover/custom-features/

LIFFアプリとLineミニアプリは現状では混同されて使われてしまっているので開発をする場合はどっちを開発しようとしているのかを事前にちゃんと決めておくことが大事。

# Liffアプリをとりあえず動作させる
とりあえずざっと動作させてみたい人は以下のリンクにあるLiffスターターアプリを作ってみることから始めるのがおすすめ。
https://developers.line.biz/ja/docs/liff/trying-liff-app/

# まとめ
Lineミニアプリの概要をまとめてみました。
ちょっと触ってみて興味が湧いてきたらデモアプリも用意されているので色々みてみると開発の参考になると思います。

# 追記
Lineミニアプリのことがよくまとまっている記事がありましたのでリンクを追加しておきます。
以下の記事を読んでもらえるとよりLineミニアプリの理解が深まります。
https://qiita.com/stachibana/items/ce52fbcab84ac3f42300


# 参考資料
https://www.youtube.com/watch?v=6eXoa_vk9YI
https://developers.line.biz/ja/docs/liff/overview/
https://lineapiusecase.com/ja/api/miniliff.html
All-in-One WP Migrationで１G以上のサイトをインポートする方法

１G以下であればphp.iniか.htaccessを編集し、サイトの最大アップロードサイズを変更すれば可能です。ただ１G以上になるとphp.iniなどで最大アップロードサイズを引き上げてもインポートの途中で止まってしまいエラーになります。そういった場合に使用する方法です。

All-in-One WP Migration ver6.68を使用します。
まずはこちらからver6.68のプラグインをインストールします。
→ https://drive.google.com/drive/folders/1XamXIzpAnhuqYSSeKDSwpcM7QdcXoTaI?usp=sharing

インストールが完了したら、
管理画面>ツール>プラグインエディターからAll-in-One WP Migrationを選択し、contants.phpを開きます。
ファイル内で"Max"と検索をかけて、以下のコードを見つけてください。

~~~ php
// =================
// = Max File Size =
// =================
define( 'AI1WM_MAX_FILE_SIZE', 536870912 );
~~~

こちらのコードで最大アップロードサイズを512MBとしています。なのでこれを×10にすることで５Gまでのファイルアップロードが可能になります。コードは以下のようになります。

~~~ php
// =================
// = Max File Size =
// =================
define( 'AI1WM_MAX_FILE_SIZE', 536870912 * 10 );
~~~

こちらでサイトの最大アップロードサイズが５Gになると思います。
コーポレートサイトなどであれば１Gで十分かもしれませんが、メディアを運営していくと画像などによってサイトに含まれるファイルは大きくなっていくため、ドメインの移行やデプロイの際にこちらの方法を使用してください。

以下参考にしたサイトです。
https://purikuro.com/2021/02/03/allinonewpmigration_max/
20行弱のTerraformコードで600本のLambda関数をデプロイしてみた

# 概要
600本あるLambda関数をデプロイする必要があったのでその時の対応をメモ
- 1つずつ書く → さすがにこれはNG
- Function名一覧を定義してfor_eachでまわす → Function名一覧作るんもツライ

ってことでどうにか楽できないかというので取った対応です

# 前提
まず前提条件として、全Lambda関数が全て同じ設定になるようにする必要があります
- 環境変数はLambda内でSSM Parameter Storeから取得する
- zipファイル名は Function名.zip とする
- ランタイム・アーキテクチャは全て同じにする

# コード
### zipファイルがローカルにある場合
filesetでzipファイルの一覧を取得して Function名 => zipファイル名 というmapを作成
```
locals {
  functions = {
    for f in fileset(var.dir_path, "*.zip") :
      trimsuffix(f, ".zip") => f
  }
}
```
これをもとにfor_eachでまわします
```
resource "aws_lambda_function" "batch" {
  for_each = local.functions

  function_name    = each.key
  filename         = "${var.dir_path}/${each.value}"
  source_code_hash = filebase64sha256("${var.dir_path}/${each.value}")
  handler          = "lambda_function.lambda_handler"
  role             = aws_iam_role.lambda.arn
  runtime          = "python3.9"
}
```

### zipファイルがs3にある場合
aws_s3_bucket_objectsでファイル一覧を取得し、これも同じように Function名 => zipファイル名 というmapを作成
```
data "aws_s3_bucket_objects" "files" {
  bucket = var.bucket_name
}
locals {
  functions = {
    for f in data.aws_s3_bucket_objects.files.keys :
      trimsuffix(f, ".zip") => f if can(regex("\\.zip$", f))
  }
}
```
これをもとにfor_eachでまわします
```
resource "aws_lambda_function" "batch" {
  for_each = local.functions

  function_name = each.key
  s3_bucket     = var.bucket_name
  s3_key        = each.value
  handler       = "lambda_function.lambda_handler"
  role          = aws_iam_role.lambda.arn
  runtime       = "python3.9"
}
```

ローカル用で16行、s3用で19行で書けました
さすがに実行時間は・・・ですが（笑）

# まとめ
実行時間を考えると、Lambda関数の更新のたびにTerraformを実行するというのはなかなか難しいですね
（実際に、初回だけTerraformでデプロイし更新については別の仕組みでデプロイしています）
XcodeでUnable to installって表示されるンゴねぇ

# 起きたこと
実機でrunしたときに表示された。

# 原因
`detail`が見られるので、見てみると

```
This application’s application-identifier entitlement does not match that of the installed application. These values must match for an upgrade to be allowed.
```

なるほど、ちょうどtarget > singing > teamを変更したのでそれが原因か
既に入っているアプリとこれからインストールしようとしているアプリの情報が違うのでインストールできないらしい

# 解決
アプリを削除して再インストールするだけ
FlexプラグインTips（ダイヤルパッドで発信番号を選択する）

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズ、Twilioエバンジェリストの高橋です。

今回もまたまた Flex Plugin についての記事となります。

[前回の記事](https://qiita.com/mobilebiz/items/11e72c49c61a02bde4d5)でも書いたように、Flex で発信をするときには、Dialpad を使います。

しかしこの Dialpad は、設定で指定した CallerId （発信元番号）が自動的に選択されてしまうので、番号を複数利用しているプロジェクトではうまくありません。
そこで、このプラグインの登場です。

# 何ができるの？

まずは以下のスクリーンショットをご覧ください。

![スクリーンショット 2022-07-22 9.25.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/d8c953f2-c693-fd4d-2c8f-5a8a3a847a99.png)

Dialpad に CallerId の選択プルダウンが表示されているのがわかります。
このプラグインを使うと、このようにすでにプロジェクトで購入済みの電話番号が選択でき、その番号で発信が可能になります。

## セットアップ

以下の GitHub 上にソースコードを準備しています。

https://github.com/mobilebiz/plugin-outbound-number-selector

こちらを`git clone`していただき、README にかかれているようにセットアップを行いましょう。

このプラグインには Functions が含まれています。この Functions が、購入済みの電話番号のリストを取得します。
ですので、まずは Functions を先にデプロイして、そこで払い出された URL をプラグインの`.env`に指定します。

:::note warn
前回の記事のSIP発信と共存すると、StartOutboundCall アクションがコンフリクトしちゃいます。その場合は、以下のようにブランチを Clone してください。こちらは、SIP 発信機能がマージされています。
`git clone --branch outbound-sip https://github.com/mobilebiz/plugin-outbound-number-selector.git`
:::

# まとめ

正直、このプラグインは結構時間がかかりました。正味3日くらいですが。
Flex Plugin を作る際には避けて通れない以下の3つの知識が必要です。

- Flex UI をつかって、コンポーネントをデザインする（Dialpad にリストボックスを表示する）
- Action Framework を変更する（発信元番号を差し替える）
- Redux（Store）で値を管理する（選択された番号を発信時読み出す）

どれも解説することがたくさんありすぎるので、そのうちプラグイン入門講座でも開きたいと思います。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilio は音声通話、メッセージング（SMS /チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウド API サービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。

Twilioに関するご相談などがございましたら、ぜひ毎週水曜日の午後に開催しております相談会をご利用ください。
[Twilio相談会（毎週水曜日）](https://cloudapi.kddi-web.com/contact/consultation?hsLang=ja-jp)

自己紹介|　
:--|:--
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/b51a8b88-abd5-ce5e-195a-de3564ba2ae6.png" height='200' width='150'/>|高橋克己（Katsumi Takahashi） 自称「<font color='red'>赤い芸人</font>」<br />グローバル・インターネット・ジャパン株式会社　代表取締役<br />株式会社KDDIウェブコミュニケーションズ　Twilio事業部エバンジェリスト<br /><br />2001年より大手通信事業者の法人サービスの教育に携わり、企業における電話のしくみや重要性を研究。2016年よりTwilio事業部にジョインし、Twilioを使ったスマートコミュニケーションの普及活動を精力的に行っている。<br />2015 Hall of Doers<br />2019 Twilio Champions
ilasmでstack machine その14

# 概要
ilasmでstack machineやってみた。
練習問題やってみた。

# 練習問題
zundoko表示せよ。

# サンプルコード

```

.assembly extern mscorlib {
}
.assembly zundoko {
}
.method static void main() {
.entrypoint
.locals init (class [mscorlib]System.Random r)
.locals init (int32 i)
	newobj instance void [mscorlib]System.Random::.ctor()
	ldc.i4	0
	stloc	i
loop:
	ldc.i4	3
	callvirt instance int32 [mscorlib]System.Random::Next(int32)
	ldc.i4	1
	bgt		zun
	ldc.i4	0
	stloc	i
	ldstr	"ドコ "
	call	void [mscorlib]System.Console::Write(string)
	br		loop
zun:
	ldstr	"ズン "
	call	void [mscorlib]System.Console::Write(string)
	ldloc	i
	ldc.i4	1
	add
	stloc	i
	ldloc	i
	ldc.i4	3
	bgt		end
	br		loop
end:
	ldstr	"ドコ キヨシ!!"
	call	void [mscorlib]System.Console::WriteLine(string)
	ret
}


```

# 実行結果

```
>izundoko
ドコ ズン ドコ ドコ ドコ ドコ ドコ ズン ドコ ドコ ドコ ドコ ドコ ドコ ズン ズン ズン ズン ドコ キヨシ!!

>

>izundoko
ズン ドコ ズン ズン ドコ ドコ ドコ ドコ ズン ドコ ドコ ドコ ズン ドコ ズン ドコ ズン ドコ ズン ドコ ドコ ドコ ドコ ズン ズン ドコ ドコ ズン ドコ ドコ ズン ドコ ドコ ズン ドコ ドコ ドコ ズン ドコ ドコ ズン ドコ ズン ズン ズン ドコ ズン ズン ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ズン ドコ ドコ ドコ ドコ ズン ドコ ズン ズン ドコ ドコ ズン ズン ドコ ドコ ズン ドコ ドコ ドコ ドコ ズン ドコ ドコ ズン ドコ ズン ズン ズン ドコ ドコ ドコ ドコ ドコ ドコ ズン ドコ ズン ドコ ドコ ドコ ドコ ズン ドコ ズン ドコ ズン ドコ ドコ ズン ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ズン ドコ ズン ドコ ズン ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ズン ズン ドコ ドコ ドコ ドコ ズン ドコ ズン ズン ドコ ズン ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ドコ ズン ドコ ドコ ドコ ドコ ズン ドコ ドコ ズン ドコ ズン ドコ ドコ ズン ズン ドコ ズン ドコ ドコ ドコ ズン ズン ズン ズン ドコ キヨシ!!

>

```

以上。
２つのファイルのDiffを取るときの備忘録

# 概要
- ２つのファイルのDiffを取って違った場合にFAILEDになるテストスクリプトを作りたい
- 比較する２つのファイル名が同じである
- 比較する２つのファイル名が同じ場合、WinMergeで同じことができるがPCがMacのためスクリプトを書くことにした

# 参考
https://docs.python.org/ja/3/library/difflib.html

# 環境
```
# Pythonのバージョンを確認
python --version
> Python 3.7.3

# pytestのインストール
> pip install pytest

# natsortのインストール
> pip install natsort
```

# 実装
```python:test_compare.py
import os
import difflib
import glob
from natsort import natsorted
import pytest
import re


folder_1 = "/filepath/"
folder_2 = "/filepath/"

files_1 = natsorted(glob.glob(os.path.join(folder_1, "*.txt")))
files_2 = natsorted(glob.glob(os.path.join(folder_2, "*.txt")))



def get_testdata():

    testdata = []
    testdata_ids = []
    for filename_with_path in files_1:
        filename = os.path.basename(filename_with_path)
        filename = filename.split('.')[0]
        testdata.append(filename_with_path)
        testdata_ids.append(f'{filename}')
    
    return [testdata, testdata_ids]

testdata = get_testdata()[0]
testdata_ids = get_testdata()[1]


@pytest.mark.parametrize("id", testdata_ids)
#@pytest.mark.parametrize("id", range(len(files_1))) # [0, 1, 2, 3, 4]
def test_compare(id):
    
    target_file_1 = f"{folder_1}/{id}.txt"
    target_file_2 = f"{folder_2}/{id}.txt" 

    if not os.path.exists(target_file_1) or not os.path.exists(target_file_2):
        raise FileNotFoundError

    f_p = open(target_file_1).read()
    f_d = open(target_file_2).read()

    diff = difflib.unified_diff(f_p.split(), f_d.split())
    diff_str = '\n'.join(diff)
    # print(diff_str)

    assert not diff_str, f"{target_file_1}, {target_file_2} FAILED!!"


```
# 結果
```
============================================================================================= test session starts =============================================================================================
platform darwin -- Python 3.7.3, pytest-7.1.2, pluggy-0.13.1
rootdir: /Users/test
plugins: cov-2.12.1, freezegun-0.4.2, Faker-5.3.0, mock-3.7.0
collected 304 items                                                                                                                                                                                           

test_compare.py ....................................................................................................................................................................................... [ 60%]
.........................................................................................................................                                                                               [100%]

============================================================================================= 304 passed in 1.10s =============================================================================================
```
便利なツール：clibor（クリボー）の始め方と使い方

## はじめに
- Qiita初投稿
- いたらぬ点もあるかと思いますが、よろしくお願いします!

## そもそもclibor（クリボー）とは?
- コピーした文章を保存できる神ツール
  - 1個前にコピーした「単語や文章」をもう一度貼り付けしたい。ただ再度コピーするの面倒。という人類の悩みを解消してくれる。
- 1万件も保存できる
- 無料で使える
- 始め方も使い方もシンプル（偏差値50以下の僕でも自力でやれた）
- 対応環境はWindows7以降
- クリボーという名前が遊戯王で馴染みがある

## clibor（クリボー）の始め方
1. [ココ](https://www.vector.co.jp/download/file/winnt/util/fh745377.html#google_vignette)へ移動
2. 「今すぐダウンロード」をクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/bafebce6-2835-d494-906a-84f3d9f1b54c.png)
3. 圧縮フォルダを開いて「Clibor」をクリック
![キャプチャ1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/93effd9b-e79d-db79-1dd8-5ef03e715baf.png)
4. 「すべて展開」をクリック
![キャプチャ2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/eacde0ef-5c54-8430-9897-9a8c3b28295f.png)
4. 「展開」をクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/f3293a71-0da8-c444-6614-5681ae5255f8.png)
5. PC画面 右下の矢印で出てくる赤い枠のアイコンをクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/ff904812-b814-2da9-4e2a-f9d93aa66c88.png)

6. インストール完了

## clibor（クリボー）の使い方
1. キーボードの「Ctrl」キーを2回押す
1. クリボー（オレンジ色の表）を召喚
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/2aa426dc-ab9d-693a-7b13-877b8fd3e8ed.png)
1. 何も登録ないので、実際にやってみる
1. 【ラーメン・餃子・ビール・レモンサワー】をラーメンから１つずつコピー
1. コピーしたものが溜まっていく
└今は「レモンサワー」が貼り付けできる状態（今までと同じ。最後にコピーしたのが貼り付けできる状態）
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/69a10518-8e79-9241-c5b2-55bd37173f2b.png)
1. 「ビール」を貼り付けたい場合は、「ビール」をクリックor【2】をクリック
1. 「ビール」が貼り付けできる状態になる


## clibor（クリボー）をちょっと応用
- 少しの設定でより使いやすくできます。
  - 色々ありますが、2つ紹介。
### クリック1回で貼り付け
1. PC画面右下にある「Cliborアイコン」を右クリックして設定画面を開く
1. 設定をクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/41d04f73-01a9-fb10-ff62-0eae44f7400c.png)
1. 最初の画面にある「自動貼り付けを有効にする」にチェックをいれて下部の「設定変更」をクリック
![キャプチャ5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/dd69ac6c-a2c7-363d-6800-f4d4173a5256.png)

### パソコン起動時、自動でCliborも起動させる
1. PC画面右下にある「Cliborアイコン」を右クリックして設定画面を開く
1. 設定をクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/41d04f73-01a9-fb10-ff62-0eae44f7400c.png)
3. 設定画面の左下にある、「スタートアップへ登録する」をクリック
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1578844/0cc58ee1-5482-0641-0392-5b6ec7101a98.png)

## clibor（クリボー）との上手な付き合い方
- インストールしたからといって無理して多用しない
- 最初は「1個前のコピーしたやつ使いたいな」のタイミングぐらいで使う
- 慣れてきたら、テンプレ使ったり等いろいろ試してみたくなる（スマホと一緒）

## 最後に
- こんな偉大なツールを開発された方、無料で使わせて頂き本当にありがとうございます!!!
Mac + Docker + Python 3.7の環境構築

まずは下記記事を参考して
参考記事：
https://qiita.com/sugasaki/items/bc1b7cfd29e83341f467



```
docker pull python:3.7-alpine3.15
```


## Sample

```
[zzeng@Zhens-MacBook-Pro ~/OneDrive/80_zzeng/work]$ docker pull python:3.7-alpine3.15
3.7-alpine3.15: Pulling from library/python
ab6db1bc80d0: Pull complete
104c00f00fd4: Pull complete
933a3380d086: Pull complete
8a756279a8ef: Pull complete
42a15d76ef81: Pull complete
Digest: sha256:7005581339cf80bec3af32f67b1e17ea5e0e8fc68abc5865ad362f7daf5e23f0
Status: Downloaded newer image for python:3.7-alpine3.15
docker.io/library/python:3.7-alpine3.15
[zzeng@Zhens-MacBook-Pro ~/OneDrive/80_zzeng/work]$
[zzeng@Zhens-MacBook-Pro ~/OneDrive/80_zzeng/work]$ docker image list
REPOSITORY    TAG              IMAGE ID       CREATED         SIZE
python        3.7-alpine3.15   29f91664fd0f   47 hours ago    45.6MB
python        latest           930516bcf910   9 days ago      920MB
hello-world   latest           feb5d9fea6a5   10 months ago   13.3kB
apache/nifi   latest           6ca96dc25d43   16 months ago   2.03GB


[zzeng@Zhens-MacBook-Pro ~/OneDrive/80_zzeng/work]$ docker run -it <IMAGE ID> python
Python 3.7.13 (default, Jul 20 2022, 01:42:51)
[GCC 10.3.1 20211027] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

```
ログインの有無でアクセス制限をする方法【Rails】

# 目的
SNSサイトにて、ログインの有無でアクセス制限したいときの処理をメモにまとめます。

# サイト構成
作成したサイトの構成は下記です。

```ruby:routes.rb
# ログイン中はアクセス制限
get "/" => "users#login_form"
post "/login" =>"users#login"
get "/new" => "users#new"
post "/users/create" => "users#create"

# ログアウト中はアクセス制限
get "/edit/:id" => "users#edit"
post "/users/update/:id" => "users#update"
post "/logout" => "users#logout"
```

# current_userの準備
ログイン中のユーザーを`@current_user`で定義します。
これは全てのコントローラで使用する変数なので、`application_controller`で定義し、`before_action`に設定します。

```ruby:application_controller.rb
before_action :set_current_user

def set_current_user
  @current_user = User.find_by(id: session[:user_id])
end
```
ここで定義した`@current_user`は`application_controller`内の別の定義式内でも使用可能です。


# ログインしてない時のアクセス制限
```ruby:application_controller.rb
def authenticate_user
  if @current_user == nil
    flash[:notice] = "ログインが必要です"
    redirect_to("/")
  end
end
```

`users_controller`で適応させるアクションを指定します。
```ruby:users_controller.rb
before_action :authenticate_user, {only: [:edit, :update, :logout]}
```

# ログインしている時のアクセス制限
```ruby:application_controller.rb
def forbid_login_user
  if @current_user
    flash[:notice] = "すでにログインしています"
    redirect_to("/shops/index")
  end
end
```

`users_controller`で適応させるアクションを指定します。
```ruby:users_controller.rb
before_action :forbid_login_user, {only: [:login_form, :login, :new, :create,]}
```

# まとめ
ログインの有無でアクセス制限をかける方法をまとめました。
`before_action`で`only`を指定しなければ全てのアクションに対して実行されます。
Jetpack Composeで親のPaddingを無視してサイズ指定する（ネガティブマージンのようなものを作る）

こういうやつ。例えばGridで、基本的には`16.dp`の余白を左右に取るが、トップの画像だけは余白を無視したい、みたいな場合。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53293/0dca8119-aaeb-4376-50b7-ef6968df8a9a.png)

## 解決策
`Modifier.layout`で調整する。拡張関数を作っておくと良さそう。
```kotlin
fun Modifier.extraWidth(width: Dp) = this.layout { measurable, constraints ->
    val placeable = measurable.measure(
        constraints.copy(
            maxWidth = constraints.maxWidth + width.roundToPx() * 2,
        ),
    )
    layout(placeable.width, placeable.height) {
        placeable.place(0, 0)
    }
}
```

### コード例
```kotlin
LazyVerticalGrid(
    columns = GridCells.Fixed(2),
    contentPadding = PaddingValues(16.dp),
    verticalArrangement = Arrangement.spacedBy(24.dp),
    horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
    item(span = { GridItemSpan(2) }) {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .height(256.dp)
                .extraWidth(16.dp)　// ここ
                .fillMaxWidth()
                .background(Color.Cyan),
        ) {
            Text("TOP IMAGE")
        }
        Spacer(Modifier.height(16.dp))
    }

    item { Box(Modifier.size(128.dp).background(Color.Gray)) }
    item { Box(Modifier.size(128.dp).background(Color.Gray)) }
    item { Box(Modifier.size(128.dp).background(Color.Gray)) }
    item { Box(Modifier.size(128.dp).background(Color.Gray)) }
}
```

## 他
もっと賢い方法があったら教えて下さい
GitHubで特定のファイルまたはディレクトリのみをダウンロードする方法

# 前置き
最近、以下のように記事の最後にGitHubのリンクを載せていますが、
このリンクからファイルやフォルダをダウンロードする方法を知らない方もいると思い、
この記事を書いています。

https://github.com/ryomeblog/qiita/tree/master/ReactNativeでAPKファイルを作成する方法/

# GitHubで特定のファイルまたはディレクトリのみをダウンロードする方法

1. GitHubのリンク先にアクセスする
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/0f166205-fccb-0709-1a90-83fc0b23badb.png)
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/e80726c3-cafb-f77d-6f85-e11ce66ad8d6.png)
2. `.` + `【任意のキー】` を押下します。
3. ブラウザ上でVSCodeエディターで開いている状態になったら、ダウンロードしたいファイルまたはフォルダを `右クリック` し、 `Download...` を押下します。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/29d97c82-3a1f-67fb-a513-fcd5ee672340.png)
4. ダウンロード先のフォルダを選択します。
5. ダウンロード先のフォルダに先ほど選択したファイルまたはフォルダが生成されていれば成功。
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/9aab3755-5fbb-93ae-3d64-0c8e56083957.png)
検証環境のデータを消してヒヤッとした話

# 初めに
とある新規開発案件にて、結合試験中に誤って検証環境のデータを消してしまった事故の話です。

その背景と事故の対処、再発防止をしたかを自分への戒めとともに、このようなことが他でも起こらないようになればと思い、共有します。

# そもそもなにをやっていたのか
私はチームリーダー兼プレイヤーとして、結合試験の課題もやりつつ、プロジェクト管理などを担当していました。

そこで、課題対応をするために、検証環境からデータを抽出して、ローカルで取り込んでデバッグをしていました。
デバッグ都度、データの入れ替えか必要だったため、mysqlの`TRUNCATE`とデータインポートを繰り返していました。

# 事件は突如起こる
前述の通り、マルチタスクで作業していたため、接続先が「検証環境」になっていることに気づかない。
その状態で、以下の操作をしてしまう。

1. A5M2で読み取り専用で接続する。
1. TRUNCATEを実行する。
1. データをインポートする。
1. ローカルデバッグする。

あれ、さっきまでデバッグがうまく行っていたのに、意図する動作にならない。

ん？もしかして！！
と思い、参照先を見ると「検証環境」になっていた。

# 事故の処理
* テスターチームにデータが消えてしまったことと、結合試験をストップして、単画面の確認をしてもらうように手配。（消えたのは一部テーブルのため。）
* インフラチームにRDSバックアップからの復元を依頼する。（前日時点に戻るのは止む終えない）

# 負の連鎖は続く
いざ、インフラチームがRDSバックアップから復元が始まる。

エラーメールがものすごい勢いで来るぞ...
コネクションエラー？

そう、本システムでは、ジョブキューを利用して、キューの監視で定期的にDBのデータ取得が行われていました。
また、それだけでなく並列処理もあるため、約50のプロセスでひたすらコネクションエラーが発生していた。

まずいと思い、ジョブキューを停止させるとともに、アプリをメンテナンスモードに変更する。
（この対応を取るまで2時間近く経過していた）

これで一安心。

# まだ終わらない負の連鎖
エラーメールは、所属の親会社ドメインのアドレスに送られてきていて、更にそれを所属会社ドメインのメールに転送していた。
（私を含め、3名のメンバーが同様に設定していた）

```
■参考までにメールの流れ

システムからエラーメール送信
　↓
親会社の開発環境メールサーバー
　↓
Gmailから転送　
　↓
所属会社メールサーバ
```

それによって所属会社のメールサーバがパンクする。
メールの受信が遅い、メール送信がエラーになるなど周りがざわつく...
（親会社の方はGmailを使用していたため、Gmail側で制御されて、途中から受信間隔が遅延されるようになった）

社内システムに相談を持ちかける。
メールキューの削除はできるが、対象の絞り込みができない（＝本来必要なメールが消失する可能性がある）。
また、Gmailからの転送が止まっていないため、この状況がいつまで続くかわからない。

ということで、親会社に連絡してメールキューを削除できないか相談する。
幸い、今回開発用メールサーバを使用しており、メールキューにエラーメール以外ないことがわかり、削除してもらう。

これで火消しが終わる。

# 再発防止
今回の事象が本番環境で起こさないために、

* A5M2の読取専用では、`TRUNCATE`は実行できてしまうため、参照のみしたい場合には、そもそもDB側に参照用のユーザーを作成する。（mysqlではTRUNCATEはロールバックできない仕様）
* DB接続が停止した際に、アプリとしてどのような作業が必要になるか把握しておく。（もちろん試験も実施する）
* マルチタスクは極力避ける

ということをやっていきます！

# 最後に
1つのミスで、手戻りも含めると`15名×8h＝120h`のコストをロストしたことになりました。

検証環境だったから、ビジネス的な影響はありませんでしたが、とても反省すべきミスでした。

今後はこのようなミスを起こさないような確認と、「データを消す」行為をするときは細心の注意をして作業しようと思いました。

皆さんもくれぐれも気をつけましょう...