OmniAICreator/Qiita-1.07M huggingface dataset:
[iOS]Excluded Architecturesにarm64を指定するとArchiveできなくなる解決法

こんにちは。[virapture株式会社](https://virapture.com)でCEOしながら[ラグナロク株式会社](https://ragna-rock.com)でもCKOとして働いている[@mogmet](https://twitter.com/mogmet)です。

今回はトラブルシュート記事です。

M1 MacなどのAppleSiliconなmacを使ってiosアプリをemulatorでビルド・起動しているとこんなエラーに遭遇すると思います。

> In /project/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector(aligned_new_bca0ac320467a2457b3e306bfed17856.o), building for iOS Simulator, but linking in object file built for iOS, file '/project/Pods/FirebaseAnalytics/Frameworks/FIRAnalyticsConnector.framework/FIRAnalyticsConnector' for architecture arm64

これを解決するにはBuild SettingsのExcluded Architecturesにarm64を追加すれば治ります。

しかし、今回はそれによるトラップを踏み抜きました。

いざ、Archiveしてappstoreに提出しようとするとこのようなエラーが表示されます。

> The archive contains nothing that can be signed.
> Verify that your build process has compiled binaries and copied in bundled resources.

こちらも解決する方法について紹介します。

## 解決法

https://teratail.com/questions/ktbv8i0lt55p0p

答えはこちらに記載ありました。

> Build Settings -> Excluded Architectures -> Debug/Releaseにarm64を指定していると発生する

ということで、`Any iOS Simulator SDK`だけにarm64を指定することで無事にArchiveできるようになりました！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42234/03d2f3c0-c41b-b0e2-49b4-b73c307f2176.png)

## まとめ

**Excluded Architecturesを指定するときに、arm64はAny iOS Simulator SDKにだけ書く！**


最後に、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[Camecon](https://camecon.me/)や[Offcha](https://offcha.com/)、問い合わせ対応が簡単にできる[CSmart](https://csmart.jp/lp)、フリーランスのコミュニティの[NextFreelance](https://next-freelance.net/)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/

https://csmart.jp/lp

https://next-freelance.net/

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

https://virapture.com/

ラグナロクでもエンジニアやデザイナーのメンバーを募集しています！！楽しくぶち上げたい人はぜひお話ししましょう！！

https://ragna-rock.com
Vueアプリのパフォーマンス改善でやったこと

chromeのdevtoolを見ながらやったこと

# Networkタブ

不要なリクエストがないか（重複リクエストとか）
キャッシュはできないか

# Performanceタブ

メモリ使用量減らせるか
どのタイミングで処理が重いか

# Performance Insightsタブ

Render blocking requestsを探して対処
FCP, LCPを早くできないか

# Vue devtool

MutationやActionsで長い奴がないか
同じ処理が連続してないか

同じ処理が連続する場合、一度にまとめて処理する

一度にまとめて処理が多すぎてユーザの操作が固まる場合はn個ずつ、n秒待ちながら処理するなど

重いコンポーネントでrenderが何回も呼ばれてないか


# Vue部分

mounted等でデータ取得している場合、一度にいろんなcomponentから取得すると渋滞するのでタイミングを変える

たとえば見えたタイミングでデータを取得するとか(Infiniteやv-lazyのような感じ)

globalなmixinで変なコードがないか

クリックした時に反応までラグがないか


# JS部分

arrayの検索や置換で遅い時がある
MapやSetを使う

# おまけ

一度にまとめて処理するとこでふと「firestoreのデータも複数一気に取得したら早くなるんじゃないか？」と思ったけど別にそんなことないみたい（事実未確認）
https://stackoverflow.com/questions/46721517/google-firestore-how-to-get-several-documents-by-multiple-ids-in-one-round-tri


# 以下のような記事も参考にしました

https://qiita.com/miyaoka/items/5183c143e195455ae65e
https://qiita.com/shun91/items/be4ae465c32b131a27f2
https://scrapbox.io/ninjinkun/Vue%E3%81%AE%E3%83%AA%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E7%90%86%E8%A7%A3%E3%81%97%E3%81%A6%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E4%BD%8E%E4%B8%8B%E3%82%92%E9%98%B2%E3%81%94%E3%81%86_(Roppongi.vue_%234_)
https://ja.vuejs.org/guide/best-practices/performance.html

# おわり

他にもやったら追記したい

公式のベストプラクティス分かりやすくて泣いた :godmode: 
https://ja.vuejs.org/guide/best-practices/performance.html
【GAS】スプレッドシートのセルを超高速で読み書きする方法【初心者】

# はじめに
Googleスプレッドシートでスクリプトを利用する仕組み GoogleAppsScript(以降GAS)を使うと非常に便利です。
ExcelのVBAみたいなものですね。
GAS初心者の筆者がさっそく使ってみました

# セルに書き込む

それでさっそく一番左上のセルに書き込みましょう

``` sample.js
function myFunction() {
  const sheetOut = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('書き込むシート名');
  sheetOut.getRange(1, 1).setValue('hello GAS World');
}
```

完成です。終了
というわけにはいきません。今度は九九の表を作ってみます。

``` sample.js
function myFunction() {
  const sheetOut = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('書き込むシート名');
  for(let j = 1; j <= 9; j++) {
    for(let i = 1; i <= 9; i++) {
      sheetOut.getRange(j, i).setValue(j*i);
    }
  }
}
```

どうですか？動きましたか？
やたらと時間がかかっていますね。
実行中にスプレッドシートを見るとセルに値が入っていくところを見ることが出来る
それぐらいのスピードです。

GASが遅いのでしょうか？
そうでは無いようです。
どうも **sheetOut.getRange(j, i).setValue(j*i);** 命令が遅いようです。
VBAとは異なり命令実行の度に外部？のスプレッドシートが参照されて・・・とか複雑なことになっているようです。

# 高速に書き込む

スプレッドシートに高速に書き込む場合は配列に入れておいてそれを一気に書き込めば解決します。
二次元配列 **oValues** に値を入れて最後に **sheetOut.getRange(1,1,9,9).setValues(oValues); **で書き込んでいます。 

``` sample.js
function myFunction() {
  const sheetOut = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('書き込むシート名');
  let oValues = [[]];
  oValues.length=0;
  for(let j = 1; j <= 9; j++) {
    oValues.push([0,0,0,0,0,0,0,0,0]);
    for(let i = 1; i <= 9; i++) {
      oValues[j-1][i-1] = j*i;
    }
  }  
  sheetOut.getRange(1,1,9,9).setValues(oValues); 
}
```

# セルを高速に読み込む
読み込みも遅いと思うのでこっちも高速化しましょう

``` sample.js
function myFunction() {
  const sheetIn = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('読み込むシート名');
  const iValues = sheetIn.getRange(1, 1, 9, 9).getValues(); 
}
```
これで **iValues** に二次元配列の値として読み込まれるので  **let no = iValues[i][0];** のようにして値を取り出すことが出来ます。

セルの位置は **1** から始まるのに配列の添え字は **0** から始まるところだけ注意が必要です。

# 最後に
いかがでしょうか？
これでGASプログラムが早くなるのですが面倒ですね。

GASを始めて3日目なのでよくわかっていません。

実験するときの注意としては読み込むシートと書き込むシートはわけておかないと
ちょっと間違えてシートのデータが消えるなんてこともあるので注意しましょう。
cURL を Windows11 にインストールしてみた。

Windows の cURL は、環境や使い方によって機能などが異なり、 Mac や Linux 向けに解説されている curl の操作と異なる場合があります。
今回は、こういったトラブルを回避するために Linux などで使われている本物の cURL を Windows11 にインストールしてみました。


# まえがき
`PowerShell` で `curl` をそのまま叩けばいいじゃん？
…と思っていた時期が私にもありました。
ただ cURL を使いたいだけなのにドハマったので、改めて整理してみました。

# 色々な cURL
cURL をインストールする前に色々と存在する cURL の違いなど簡単に紹介します。

## Invoke-WebRequest
1つ目は `Invoke-WebRequest` です。

Windows に標準で搭載されている `Windows PowerShell` では、 `curl` コマンドを叩いても `Invoke-WebRequest` が起動するようになっています。
これは、 `curl` が `Invoke-WebRequest` にエイリアスされているためです。

試しに `Windows PowerShell` で `curl` のエイリアスを確認してみると、この様になっています。
![rapture_20230216150257.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/88e73cf4-a52d-429c-5984-80f81dd9d5ee.png)

そして困ったことに、この `Invoke-WebRequest` と `curl` とでは、コマンドが全く異なります。

## curl.exe
2つ目は `curl.exe` です。

Windows には、上記の `Invoke-WebRequest` とは別に `curl.exe` も実装されいます。
`curl.exe` は、 `curl` とほぼ同じように操作できますが、パラメータや扱えるプロトコルなどで異なる部分が存在します。
また、コマンドを実行する際は `curl` ではなく `curl.exe` と入力する必要があります。

```bash
curl.exe https://vantiq.com
```

パラメータが微妙に異なるなど、地味に使いづらいやつです。

## Windows PowerShell
3つ目は `Windows PowerShell` です。
`Windows PowerShell` 自体は `cURL` ではないのですが、 `cURL` に関わってくるので紹介します。

Windows では `PowerShell` にも違いがあります。
Windows が標準で搭載している `PowerShell` は `Windows PowerShell` と呼ばれるもので、最新版の `PowerShell` とは機能面などで大幅に異なっています。
そして、上記でも説明した通り `Windows PowerShell` では `curl` が `Invoke-WebRequest` のエイリアスとして設定されています。
一方、 `PowerShell` の場合は、エイリアスが設定されていません。
![rapture_20230216150814.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/212972/359de66b-449d-01b4-32ee-ab7b6dcecdcc.png)


# cURL のインストール方法
cURL はインストーラーが用意されていないため、手作業でインストールする必要があります。

## cURL のダウンロード
下記のリンクから cURL をダウンロードします。
今回は `Windows  64-bit  7.87.0  binary` をダウンロードしました。

https://curl.se/download.html#Win64

## インストール
ダウンロードしたzipファイルを解凍し、任意のフォルダに展開します。
今回は `C:\Program Files\curl` フォルダに展開しました。

## Path の設定
`curl` フォルダ内の `bin` フォルダに `Path` を通します。
今回は `C:\Program Files\curl\bin` に `Path` を通しました。

以上で `cURL` のインストールは完了です。
C#サーバーでプロファイルを取る方法

## はじめに

ここでいう`プロファイル`とは、CPU使用時間やメモリ使用量などの計測結果のことを指します。

この記事の目的は[dotnet-trace](https://learn.microsoft.com/ja-jp/dotnet/core/diagnostics/dotnet-trace)の紹介です。
最新の情報は公式ドキュメントをご参照ください。

## プロファイルを取る手順

### ツールのインストール

多くの場合はDockerイメージを使用していると思います。
そんな時は以下のような手順で`dotnet-trace`をインストールするとよいでしょう。
また、必要に応じて`PATH`に設定してもよいかもしれません。

```dockerfile
# ツールをtoolsという名前のイメージの`/tools`にインストールする
FROM mcr.microsoft.com/dotnet/sdk:{バージョン} AS tools
RUN dotnet tool install --tool-path /tools dotnet-trace

# 最終イメージの記述
FROM mcr.microsoft.com/dotnet/aspnet:{バージョン} AS final
...
# toolsイメージからインストールしたツールを最終イメージの`/tools`へコピーしてくる
WORKDIR /tools
COPY --from=tools /tools .
```

> 参考 : [マルチステージビルドの利用
](https://matsuand.github.io/docs.docker.jp.onthefly/develop/develop-images/multistage-build/)

### 書き込み先の用意

書き込み権限のある領域を作成しておきます。
単なるDockerイメージの場合は不要かもしれません。(未確認)

- kubernetesの場合
    ```yaml
          volumeMounts:
            - mountPath: /tmp
              name: tmp
    ...
      volumes:
        - name: tmp
          emptyDir: {}
    ```
- docker-composeの場合
    ```yaml
        tmpfs:
          - /tmp
    ```

### 実行

#### 起動と同時にプロファイルを取る場合

初めからプロファイルを取る目的で起動する場合は`ENTRYPOINT`あたりで`dotnet-trace`を指定してあげると良さそうです。(未確認)

```dockerfile
# 動作未確認
ENTRYPOINT ["/tools/dotnet-trace", "collect", "--", "dotnet", "exec", "DLL名"]
```

#### 起動しているプロセスからプロファイルを取る場合

多くの場合は既に起動しているプロセスからプロファイルを取りたいと思います。(主観)
その場合は以下のように指定することでプロファイルを取ることができます。

```bash
# processIDを取得する
$ /tools/dotnet-trace ps
 20  dotnet  /usr/share/dotnet/dotnet  dotnet {DLL名}

# プロファイルを取る
# /tmpは上で作成した書き込み用ボリュームを、profile.nettraceには好きな名前を指定できます。
$ /tools/dotnet-trace collect -p 20 -o /tmp/profile.nettrace
...(出力内容省略)...
Press <Enter> or <Ctrl+C> to exit
```

`--duration`など、オプションについては[公式ドキュメント](https://learn.microsoft.com/ja-jp/dotnet/core/diagnostics/dotnet-trace#dotnet-trace-collect)をご確認ください。

#### コンテナからプロファイルファイルを取得する

- kubernetesの場合
    - [kubectl cp](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#cp)でできます。
- docker-composeの場合
    - [docker compose cp](https://docs.docker.com/engine/reference/commandline/compose_cp/)でできるらしいです。(未確認)
        - 手元で試したところ`Error: No such container:path: f09c185d91f7:/tmp/profile.nettrace`と言われて詰まったorz

### プロファイルの内容を精査

VisualStudioにファイルをドラッグ&ドロップすればプロファイルの内容が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47699/f7929b55-40b5-01ee-a1a4-68eb964dd802.png)

## まとめ

最適化はちゃんとプロファイル取ってからやりましょう！

```
Rules of Optimization:
Rule 1: Don't do it.
Rule 2 (for experts only): Don't do it yet.
> Michael A. Jackson. Principles of Program Design, Academic Press, 1975
```

### Tips

- `dotnet-trace`は常に入れておくと急遽必要になった時に焦らないのでお勧め。
    - セキュリティの関係で`dotnet install`ができないことも多いのであらかじめ仕込んでおくの大事。
fehでスライドショー

いつもなら先人たちの記事を見てヒャッフーなんですが
Google先生がファイヤーなんちゃらヒーローズの記事ばかり紹介するので渋々manとにらめっこしたのでメモ代わりに。

1.スライドショーの時に使うオプション

    *** 必須 ***
    -s スライドショーで起動
    -D スライドショーのディレイタイムを秒で指定（例:-D 5）
    -. 画像を表示サイズに合わせて縮小
    -Z 画像を表示サイズに合わせて拡大

    *** 表示対象 ***
    -z 表示順をランダム
    -n 表示順を逆順
    -r 下層ディレクトリの画像も表示する
    
    *** 表示サイズ ***
    -x ボーダーレスウィンドウ（ウィンドウ移動はAlt+左ドラッグで可能）
    -g ウィンドウサイズを指定（例:-g 800x600）
    -F フルスクリーン

    *** その他 ***
    -B 余白の色を指定（例:-B gray 又は -B #808080）
    -Y マウスカーソルを非表示

2.使用例（画像ディレクトリは ~/Pictures）

    ・5秒間隔、800x600ボーダーレス、余白をグレー
        $feh -s.ZD 5 -xg 800x600 -B gray ~/Pictures
    ・1.5秒間隔、ランダムかつ下層ディレクトリの画像も表示、フルスクリーン、マウスカーソルを非表示
        $feh -s.ZD 1.5 -zr -F -Y ~/Pictures
oracleとmysqlの比較

# 副問い合わせのalias
* mysql → 必須
* oracle → 省略可

# substrのposの先頭
* mysql → 1
* oracle → 0または1

# null判定関数
* mysql → IfNull()
* oracle → nvl()

# from句の省略
* mysql → from以下省略可
* oracle → from以下省略不可。from dualと記述する

# バインド変数
* mysql → SET @BIND_VARIABLE = 'hoge';
* oracle → :BIND_VARIABLE

# 関数の戻り値
* mysql → STRING、INTEGER、REAL、DECIMAL 
* oracle → PL/SQLでサポートされているすべてのデータ型。Object(Record)型も対応可

# 空文字列との比較結果
以下のSQLをMySQLとOracleで実行した結果を比較。
```
> select case when '' <> 'foo' then 1 else 0 end as VAL from dual
```
* mysql → 1 
* oracle → 0
    * substr()の返却値をwhere文等で比較する場合は注意が必要。
envoyのメトリクスを削減する

envoyのメトリクスでカーディナリティの高いものがあり、Prometheusのリソースを逼迫させていたので特定のメトリクスを出力しないようにしたかった

設定ファイルに以下の記述を入れればOK
```yaml:envoy-bootstrap.yaml
...
stats_config:
  stats_matcher:
    exclusion_list:
      patterns:
        - contains: upstream_cx_length_ms
...
```

参考：

https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/metrics/v3/stats.proto#config-metrics-v3-statsmatcher

メモ：`histogram_bucket_settings`を減らすのもメトリクスの削減に有効そう
blastengineのPHP SDKを使ってメールを一括配信する

blastengineはエンジニアフレンドリーなメール配信サービスです。APIとSMTPリレーの両方をサポートしており、手軽に素早くメール配信ができるようになっています。

今回はPHPでblastengineのAPIを簡単に使えるSDKを開発しましたので、使い方を紹介します。今回は大量の宛先に対して一括配送する、バルクメールの使い方です。

[blastengine/blastengine \- Packagist](https://packagist.org/packages/blastengine/blastengine)

## インストール

インストールはcomposerで行ってください。

```
composer install blastengine/blastengine
```

## インポート

autoloaderでファイルを読み込みます。

```php
require __DIR__ . '/vendor/autoload.php';
```

## 初期化

Blastengineを初期化します。ユーザ名とAPIキーを使います。

```php
// 初期化
Blastengine\Client::initialize($your_user_id, $your_api_key);
```

## メールオブジェクトの用意

バルクメールは `Blastengine\Bulk` を使います。まず、件名やテキスト本文などを指定します。

```php
$bulk = new Blastengine\Bulk();
$bulk
	->from('info@example.com')
	->subject('Test subject')
	->text_part('This is test email to __name__');
```

## 添付ファイルの追加

添付ファイルは `attachment` メソッドで、ファイルのパスを指定してください。

```php
// 添付ファイルがある場合
$bulk->attachment('./test.php');
$bulk->attachment('./image.png');
```

## メールオブジェクトの保存

一度メールオブジェクトを保存します。

```php
$bulk->save();
```

## 宛先の追加

宛先は `to` メソッドで追加します。そして、登録したら `update` メソッドを実行します。なお `name` はメール本文で `__name__` として指定されている箇所と差し替えられます。


```php
$bulk
	->to('user1@example.jp', ["name" => "User 1"])
	->to('user2@example.jp', ["name" => "Name 2"])
	->update();
```

大量のアドレスを登録する場合は、 [blastengineのPHP SDKを使ってCSVで宛先を一括取り込みする \#PHP \- Qiita](https://qiita.com/goofmint/items/85baa0786aa965814e14) にあるCSVアップロードを利用してください。

```php
$job = $bulk->import("./example.csv");
while ($job->finished() == false) {
	sleep(1);
}
```

## 送信

送信は `send` メソッドになります。返値は真偽値（常にtrue）になります。エラーがあると、例外処理に入ります。

```php
try {
	$bulk->send();
} catch ( Exception $ex ) {
	// Error
}
```

## まとめ

PHP SDKを使えばWebアプリケーションで簡単にメール送信できるようになります。ぜひblastengineのPHP SDKを活用ください！

[blastengine/blastengine \- Packagist](https://packagist.org/packages/blastengine/blastengine)
blastengineのPHP SDKを使ってメールを即時配信する

blastengineはエンジニアフレンドリーなメール配信サービスです。APIとSMTPリレーの両方をサポートしており、手軽に素早くメール配信ができるようになっています。

今回はPHPでblastengineのAPIを簡単に使えるSDKを開発しましたので、使い方を紹介します。現在はトランザクションメール（即時発信メール）のみサポートしています。

[blastengine/blastengine \- Packagist](https://packagist.org/packages/blastengine/blastengine)

## インストール

インストールはcomposerで行ってください。

```
composer install blastengine/blastengine
```

## インポート

autoloaderでファイルを読み込みます。

```php
require __DIR__ . '/vendor/autoload.php';
```

## 初期化

Blastengineを初期化します。ユーザ名とAPIキーを使います。

```php
// 初期化
Blastengine\Client::initialize($your_user_id, $your_api_key);
```

## メールオブジェクトの用意

トランザクションメールは `Blastengine\Transaction` を使います。件名やテキスト本文、宛先などを指定します。

```php
// トランザクション（即時発送）メールオブジェクトの作成
$transaction = new Blastengine\Transaction();
// 情報を適用する
$transaction
	->to('user@example.jp')
	->from('info@example.com')
	->subject('Test subject')
	->text_part('This is test email');
```

## 添付ファイルの追加

添付ファイルは `attachment` メソッドで、ファイルのパスを指定してください。

```php
// 添付ファイルがある場合
$transaction->attachment('./test.php')
$transaction->attachment('./image.png')
```

## 送信

送信は `send` メソッドになります。返値は真偽値（常にtrue）になります。エラーがあると、例外処理に入ります。

```php
try {
	$transaction->send();
} catch ( Exception $ex ) {
	// Error
}
```

## まとめ

PHP SDKを使えばWebアプリケーションで簡単にメール送信できるようになります。ぜひblastengineのPHP SDKを活用ください！

[blastengine/blastengine \- Packagist](https://packagist.org/packages/blastengine/blastengine)
blastengineのPHP SDKを使ってCSVで宛先を一括取り込みする

[blastengine](https://blastengine.jp/)はエンジニアフレンドリーなメール配信サービスです。APIとSMTPリレーの両方をサポートしており、手軽に素早くメール配信ができるようになっています。

今回はPHPでblastengineのAPIを簡単に使えるSDKを開発しましたので、使い方を紹介します。この記事ではバルクメール（一括送信メール）で、メールの宛先をCSVからインポートする方法を紹介します。

## インストール

インストールはcomposerで行ってください。

```
composer install blastengine/blastengine
```

## インポート

autoloaderでファイルを読み込みます。

```php
require __DIR__ . '/vendor/autoload.php';
```

## 初期化

Blastengineを初期化します。ユーザ名とAPIキーを使います。

```php
// 初期化
Blastengine\Client::initialize($your_user_id, $your_api_key);
```

## CSVを用意する

インポートに利用するCSVファイルは以下のようなフォーマットになります。2列目以降は置き換えキーワードと、その文字列になります。

```
email,__example__,__name__,__date__
test1@example.jp,テスト,テスト太郎,20200101
test2@example.jp,サンプル,サンプル次郎,20210101
test3@example.jp,テスト,テスト太郎,20200101
```

## バルクメールを作成する

まず最初にバルクメールを作成します。まず宛先のない状態で、テンプレートを保存します。

```php
$bulk = new Blastengine\Bulk();
$bulk
	->from('info@example.com')
	->subject('Test subject')
	->text_part('This is test email to __name__')
	->save();
```

## CSVをインポートする

インポートは `import` メソッドにCSVファイルを指定して実行します。このCSVインポートは非同期（バッチ）処理で、この時点ではまだ完了していません。

```php
$job = $bulk->import("./example.csv");
```

なお、初期設定では1件でもデータに不備があると全体の取り込みが失敗します。これを無視する場合には2つ目の引数に true を渡してください。

```php
$job = $bulk->import("./example.csv", true);
```

### CSVインポートのステータスをチェックする

`finished` メソッドでインポートが完了したかチェックします。途中の場合は `percentage` プロパティに取り込み状況（パーセンテージ）が入っています。

```php
while ($job->finished() == false) {
	sleep(1);
}
```

### Jobクラスのプロパティ

Jobクラスでは以下のプロパティを用意しています。

| プロパティ | 意味 |
|----------|----------|
| total_count    | 全ての取り込んだ件数 |
| success_count  | 取り込み成功した件数 |
| failed_count   | 取り込み失敗した件数 |
| error          | エラーがあれば true |
| error_file_url | 取り込み失敗した結果の書かれたファイルのURL |

### エラー内容を確認する

error がtrueだった場合、 `error_report` メソッドでエラー内容を取得できます。CSVの文字列になっています。

```php
if ($job->error) {
	// エラーがある場合
	$report = $job->error_report();
}
```

メッセージは、例えば以下のような内容です。

```
"エラーメッセージ","email","__prop1__","__prop2__","__prop3__"
"[email: [test3@example: 無効なEメールアドレス形式です。]]","test3@example","テスト","テスト太郎","20200101"
```

## まとめ

PHP SDKを使えばWebアプリケーションから簡単にblastengineが呼び出せます。ぜひblastengineのRuby SDKを活用ください！

[blastengine/blastengine \- Packagist](https://packagist.org/packages/blastengine/blastengine)
プリザンターで使われるSAML認証とは・・・？

 # はじめに
業務でSAML認証に関する問い合わせがあったため、SAML認証について詳しく調べてみようと思います。

 # SAML認証とは・・・？
SAML認証とはシングルサインオンの一つです。そもそもシングルサインオンとは一度のログインによって複数のサービスを利用できるシステムです。シングルサインオンでは、異なるドメイン間での連携が難しいという課題がありましたが、SAML認証によってこの課題が解決しました。ドメインというのはインターネット上の住所のようなもので、URLの一部を指します。つまり、SAML認証というのは一度のログインで異なるアプリケーションであっても、対応しているアプリケーションを利用できる仕組みのことです。

 # SAML認証の仕組み
SP(=Service Provider)：ログインされるクラウドサービスを提供する
IdP(=Identify Provider)：保有している登録情報とログインしているユーザーの持つ登録情報が合致するか認証する
 1. ユーザーはログインするためにSPへアクセスします
 1. SPはSAML認証要求を作成します
 1. SPはユーザーのアクセスをSAML認証要求とあわせてIdPへ送ります
 1. IdPはSAML認証要求を受け取って、適切なユーザーであるか確認します
 1. IdPはSAML認証応答を作成します。SAML認証応答には、SPへログインしたいユーザーが登録されているものか認証します
 1. SPは受け取ったSAML認証応答にもとづいて、ユーザーのログインを許可／拒否します

 # SAML認証のメリット
 ### ユーザーの利便性向上
今まではサービスを利用する際に都度都度ユーザー情報(ユーザーIDやパスワードなど)を入力しログインしなくてはいけませんでしたが、SAML認証によってその必要がなくなります。そのため、時間のロスも削減され、ユーザーがサービスを利用する際の利便性が向上されます。

 ### コスト削減
従来はサービスごとにユーザー情報を登録していました。そのため、管理しなくてはいけない情報量が多く、管理するためのコストが増えてしまいます。しかし、SAML認証によって一人に対して、一つのユーザー情報を登録するだけで済むため、膨大な量の情報を管理しなくてよくなります。そのため、情報を管理するためのコストを抑えられました。

 ### セキュリティ向上
異なるサービスごとに異なるパスワードを設定している場合、利用するサービスに比例して覚えなくてはいけないパスワードも増えてきてしまいます。覚えなくてはいけないパスワードの数が増えると、付箋やメモ帳などテキストとしてパスワードを管理することが多くなると思います。テキストなどでパスワードの管理をしてしまうとソーシャルエンジニアリング(※1)の被害にあう可能性があります。そのため、管理するパスワードを1つにすることでテキストではなく、頭で記憶したりすることが可能となり、結果的にソーシャルエンジニアリングの被害が少なくなります。
※1.パスワードなどのユーザー情報を情報通信技術を使用せずに盗み出す方法のこと

# おわりに
以上からSAML認証がどのようなものかなんとなくわかりました。ひとまず仕組みを理解できたため、業務で活用していこうと思います。少し基本情報で学習した内容を小出ししてみましたが、いかがだったでしょうか。これから、今までの学習内容を振り返るきっかけとしても記事の投稿を続けていこうと思います。

# 参考資料

https://boxil.jp/mag/a2950/
blastengineのバルクメール編集リミットに達した場合の対応

blastengineはエンジニアフレンドリーなメール送信サービスです。メールを即時配信するトランザクションと、一括配信するバルクメールに対応しています。

バルクメールでは、まずテンプレートを保存します。そして、そのテンプレートに対して宛先情報を登録して送信を行います。その際、以下のようなエラーメッセージが出る場合があります（エラーメッセージは加工済み）。

`Exception: Error in main: number of EDIT statuses exceeds 10.`

これは、EDIT（メール送信されていない状態）のメールテンプレートは10個までしか保存できないことに起因して発生するものです。こうした状態のメールは適宜削除、または送信処理を行う必要があります。

## 削除対応について

今回はNode.js SDKを使って削除してみます。インストールは `npm` コマンドで行います。

```
npm i blastengine
```

### ライブラリのインポート

ライブラリをインポートします。TypeScriptなので、実行時には `ts-node` も必要でしょう。

```js
import { BlastEngine, Bulk } from 'blastengine';
```

### 初期化

blastengineのユーザー名、APIキーを用いて初期化します。

```js
USER_NAME = 'YOUR_USER_NAME';
API_KEY = 'YOUR_API_KEY';
new BlastEngine(USER_NAME, API_KEY);
```

### 処理をループする

2022年12月現在、EDITのメールだけを検索するといった機能がないので、ループ処理で対応します。

```js
MIN_NUMBER = 1;
MAX_NUMBER = 1200;
for (let i = MIN_NUMBER; i < MAX_NUMBER; i++) {
	// 以下はこの中に記述
}
```

### バルクメールを作成する

バルクメールオブジェクトを作成し、デリバリーIDを適用します。

```js
const bulk = new Bulk;
bulk.delivery_id = i;
```

### 情報を取得

メールの情報を取得し、そのステータスがEDITであれば削除します。

```js
try {
	await bulk.get();
	if (bulk.status === 'EDIT') {
		console.log(`Delete: ${bulk.delivery_id}`);
		await bulk.delete();
	}
} catch (e) {
}
```

### ウェイトする

API負荷を考慮して、500ms処理待ちします。

```js
await new Promise((res) => setTimeout(res, 500));
```

これで完成です。

## 全体の処理

全体の処理は以下のようになります。

```js
USER_NAME = 'YOUR_USER_NAME';
API_KEY = 'YOUR_API_KEY';
MIN_NUMBER = 1;
MAX_NUMBER = 1200;
new BlastEngine(USER_NAME, API_KEY);
for (let i = MIN_NUMBER; i < MAX_NUMBER; i++) {
	const bulk = new Bulk;
	bulk.delivery_id = i;
	try {
		await bulk.get();
		if (bulk.status === 'EDIT') {
			console.log(`Delete: ${bulk.delivery_id}`);
			await bulk.delete();
		}
	} catch (e) {
		console.log(`${bulk.delivery_id}`);
	}
	await new Promise((res) => setTimeout(res, 500));
}
```

## まとめ

将来的には10個までの制限撤廃や、EDIT状態のメール検索などが提供されるかも知れません。取り急ぎ2022年12月現在では、以上のような対応になります。もしblastengineを使っていて上記エラーが出たら、今回の対応を行ってください。

[エンジニア向けメール配信システム「ブラストエンジン」](https://blastengine.jp/)
EC-CUBEのSMTPサーバーとしてblastengineを設定する

オープンソースのECパッケージとして知られるEC-CUBEにて、blastengineをメール送信サーバとして設定する方法です。

今回は [itaboo1014/docker-for-eccube4](https://github.com/itaboo1014/docker-for-eccube4) を利用しています。

## EC-CUBEセットアップ

[itaboo1014/docker-for-eccube4](https://github.com/itaboo1014/docker-for-eccube4) を使えばDocker Composeで立ち上げられるので手軽です。公式にもあるのですが、どうもうまくいかなかったので…

```
$ git clone https://github.com/itaboo1014/docker-for-eccube4.git
$ cd docker-for-eccube4
$ make ECCUBE4.2.0
$ make PHP8.1
$ make MySQL8.0
$ docker-compose build app
$ docker-compose up
```

これで `http://localhost` または `http://127.0.0.1` でEC-CUBEのセットアップ画面にアクセスできます。

## SMTP設定

インストール中にSMTP設定を行う場面があります。以下の情報を入力します。

| 項目 | 値 |
| --- | --- |
| SMTPホスト | http://smtp.engn.jp/ |
| SMTPポート | 587 |
| SMTPユーザー | blastengineのユーザー名 |
| SMTPパスワード | 下記参照 |

![0552_127.0.0.1_-_0119171037.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/478d0186-df52-28a2-5e13-3b94bb90a7ea.jpeg)


### SMTPパスワードについて

blastengineの管理画面の設定にて、SMTPリレーを開きます。パスワードを自由に設定してください。そして、そのパスワードをSMTPパスワードとして、上記画面にて設定してください。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/ca430f29-685b-9621-938d-e702af9ad8ef.png)


## 設定の変更について

### SMTP設定の変更

もし設定をミスしていたり、変更する場合にはEC-CUBEのルートディレクトリにある `.env` ファイルを開きます。 `MAILER_DSN` という項目に書かれているのが、SMTP設定になります。書かれている文字列は以下のように構成されています。

```json

MAILER_DSN=smtp://(SMTPユーザー):(SMTPパスワード)@(SMTPホスト):(SMTPポート)?auth_mode=plain
```

### 送信元の変更

メールの送信元やReplyToを変更する場合には、EC-CUBE管理画面の設定＞店舗設定＞基本設定にて変更してください。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/75d6498c-e89e-0d17-d14b-6ae3313aa653.png)


## まとめ

設定する内容によって編集する場所が異なるので注意してください。SMTPサーバーの変更は `.env` ファイルを編集するだけなので簡単にできます。ぜひ既存のSMTPサーバーからblastengineへ乗り換える際の参考にしてください。

[エンジニア向けメール配信システム「ブラストエンジン（blastengine）」](https://blastengine.jp/)
blastengineのメールエラーWebhookで送られるデータについて

[blastengine](https://blastengine.jp/)はエンジニアフレンドリーなメール送信サービスになります。APIまたはSMTPを使って、簡単にメール送信を行えます。

先日、blastengineではエラーメールに対するWebhook通知に対応しました。この記事では、その使い方（PHP）と実際に受け取れるデータについて解説します。

## 設定

Webhook設定はblastengineの管理画面で行います。設定の中にあるWebhookをクリックします。

![001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/059adabd-6159-2cd4-12d3-9d98c075b1c2.jpeg)


設定は主に2つです。

- エンドポイントURL
Webhookを受け取るURLです
- 通知イベント
Webhookを受け取るイベントです

エンドポイントURLにはPOSTメソッドで呼ばれます。Content-Typeはapplication/jsonになります。通知イベントは以下の3つが用意されています。

- ドロップ
エラー停止リスト内にあるメールアドレス宛に送信しようとした場合のエラーです
- ハードエラー
ホストがない、ユーザーがいないといった恒久的なエラーです
- ソフトエラー
メールボックスがいっぱいなど一時的なエラーです

## 注意点

2023年01月現在、Webhookはトランザクションメール（即時配信メール）に対してのみ有効です。バルクメール（一括配信）は未対応です。

## データを受け取ってみる

では実際にデータを受け取ってみます。今回はPHPで以下のようなコードを書きました。PHPではContent-Typeがapplication/jsonで送られた場合、 `$_POST` にデータは入ってこないので注意してください。

```php
<?php
$f = fopen('log.txt', 'a+');
$json = file_get_contents('php://input');
$data = json_decode($json, true);
fwrite($f, json_encode($data));
fwrite($f, "\n");
fclose($f);
http_response_code(200);
echo '';

```

以下はドロップの例です。 `error_message` にエラー内容が入ります。

```json
{
	"events": [
		{
			"event": {
				"type": "DROP",
				"datetime": "2023-01-26T16:17:54+09:00",
				"detail": {
					"mailaddress": "user@drop.example.com",
					"subject": "Test subject",
					"error_code": "554(errors)",
					"error_message": "エラー停止リストに含まれる宛先であるため、配信がドロップされました",
					"delivery_id": 1128
				}
			}
		}
	]
}
```

以下はハードエラーの場合です。ホストがない、ユーザーが存在しないなど恒久的に発生する場合のエラーです。

```json
{
	"events": [
		{
			"event": {
				"type": "HARDERROR",
				"datetime": "2023-01-26T16:22:43+09:00",
				"detail": {
					"mailaddress": "user@harderror.example.com",
					"subject": "Test subject",
					"error_code": "550",
					"error_message": "宛先のメールアドレスがありません",
					"delivery_id": 1129
				}
			}
		}
	]
}
```

## まとめ

データが受け取れれば、後はSlackへ通知したり、データベースと連携するなどが容易になります。配信先リストを自動クリーニングするのにも使えるでしょう。

一例としてZapier経由でSlackへ通知するデモを[blastengineのwebhook機能でメール送信が失敗したらzapier経由でSlackに通知できるようにした。 - Qiita](https://qiita.com/hirosatooooo/items/21573f79fbb8599090d1)にて紹介しています。ご覧ください。

[エンジニア向けメール配信システム「ブラストエンジン（blastengine）」](https://blastengine.jp/)
一周年記念に"THE FIRST CODE"を完走した感想

# `THE FIRST CODE` ってなあに？

`THE FIRST CODE`　という遊びを皆様ご存知でしょうか？

「一度だけの実行しかできない条件で、指定のプログラムコードを開発する」という、プログラミング系Youtuberの間で流行っている遊びです。
音楽系Youtubeチャンネルの `THE FIRST TAKE` をモジったタイトルになっています。

YoutubeやTwitterで #thefirstcode というタグで検索するといろいろな方がUploadしている動画を見ることができます。

# ゆにるユニ一周年記念でやってみた！

ところで私はゆにるユニ、IT TechLeadのVTuberです。はじめましての方ははじめまして！
普段は [プログラミング言語を作った](https://qiita.com/yuniruyuni/items/2c4aa81097a4e5578d46) り、Yuniru's HackerNews Summaryとして、ハッカーなニュースを毎週まとめてお伝えしています。。。！

実は2月5日に一周年を迎えたので、その日の遊びとして `THE FIRST CODE` を私も実際にやってみました……！

お題は3つ: `FizzBuzz`, `BubbleSort`, `CellularAutomaton`

動画アーカイブ → https://youtu.be/cgDPtcRO0O0

この記事には `THE FIRST CODE` を完走した感想、もとい反省とか気づいたTipsを書こうと思います！

# 完走した感想

## 事前準備で気をつけるべき点

やる前に準備をするフェイズで結構手間がかかりました。
まずあまり強いコード支援が効いてる状態だと興ざめになっちゃうので、VisualStudioCodeなどのエディタでは各種Extensionを無効化しておくことが必要になると思います。
私は設定を戻せなくならないよう、プロファイルを分割＆この遊び専用のワークスペースを作ってから、ワークスペース設定としてExtensionの無効化作業を行いました。この方針は結構うまく機能したように思います。
またVisualStudioCodeを撮影に入れるので.envなどが展開されないように一般に配信上注意が必要です

## 便利な文法を正しく思い出せないなら、より安い文法を使うことで回避できる

あえて便利な文法ではなく、もう間違いなく思い出せる文法を使って組み合わせて書いちゃうことでコンパイルしやすくできたシーンがありました。(具体的にはgolangで配列内の2要素を交換したかったときに `tmp` 変数に一度退避しています。実際はgolangは `a, b = b, a` が通るので、そのほうがきれいです)
この作戦はうまく成功してBubbleSort実装は通すことができました。

また(ちょっと情けないことに)boolとintの相互変換について、あまり自信がなかったので最初から変数をintで定義したりしています。

## 添字、もしかしたら1文字は避けたほうがいいかもしれない

途中で `i` と `j` を間違えて打っていました。
ビルド前にすごくガッツリレビューをして目視で見つけているのですが、これはかなり危なかったです。
THE FIRST CODEの場合、こういったミスが致命傷なので、ループの添字だとしても1文字じゃなくてもっと明瞭な名前をつけてあげたほうがいい可能性を感じました。

## 作るべきコードの仕様は明確化したほうがいい

見てくれる人もそうだけど自分自身悩んでしまうので、作るべきコードの仕様は割と明確化したほうがよいです。また曖昧なままだと結果の成否も確認しづらくなるので良くないです

今回の場合は有限長の一次元セルオートマトンを開発する気だったんですが、明白に一次元と言ってなかったのでコンウェイのライフゲームのほうを想像する方もいました。
また有限長ではなく自動的に左右に広がっていく無限長の一次元セルオートマトンを想像された方もいました。
私自身、無限長のサイズに拡張したほうがいいかもなあと途中で思ったりしてます。。

ちなみにもしかしたら、テストは事前準備で定義しておいて、それの上で当日書いたコードが動くことを確認してもいいかもしれないです。

## 文法理解の曖昧な部分が、すごく明瞭になる

この遊びを通じて、正しく記憶できている文法と、そうでない文法がすごく明確になります。
なので意外と訓練としても、いいかもしれません。

よかったら皆様もお試しください！
WordPressでblastengineをメール送信サーバーにする設定手順

[blastengine](https://blastengine.jp/)はエンジニアフレンドリーなメール送信サービスになります。APIまたはSMTPを使って、簡単にメール送信を行えます。

今回はWordPressでblastengine向けに設定する手順を紹介します。

## WP Mail SMTPを使う

WordPressでSMTP設定を行う場合、本体に手を入れたり、設定がデフォルトで用意されている訳ではありません。プラグインを使うのが一般的です。幾つか同様の機能を提供するプラグインがあるようですが、一番有名なのはWP Mail SMTPでしょう。

![Untitled.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/396b7a21-832e-9c57-30ac-6771f21febb4.jpeg)


基本的な機能は無料ですが、有料プランへのリンクや紹介が多いのが難点です。今回紹介するSMTPの変更だけであれば無料の範囲で利用できます。

## 設定ウィザード

設定はウィザードに沿っていくだけで完了できます。まず大事なのは `その他のSMTP` を選ぶことです。

![Untitled 1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2dd80faa-d693-a874-9a49-1fcbfd136448.jpeg)


そしてSMTPホストは `[smtp.engn.jp](http://smtp.engn.jp)` として、暗号化は `TLS` 、SMTPポートは `587` とします。

![Untitled 2.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/9d908fde-2f83-dec9-b18f-4af75bfb3b46.jpeg)


認証情報は有効にして、SMTPユーザー名とSMTPパスワードを入力します。

### SMTPユーザー名とSMTPパスワードについて

SMTPユーザー名とSMTPパスワードは、blastengineの管理画面にてSMTPリレー設定で指定できるIDとパスワードを指定してください。

![Untitled 3.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/ca5ff817-1c5b-7f4b-1dbb-05c8c400196d.jpeg)


## ウィザードの続き

フォーム名というのは `From名` のtypoのようです。送信元メールアドレスはSPFやDKIMが設定されているドメインのものを指定してください。この2つが設定されていないと迷惑メールと判定される原因になります。このメールを強制使用は必要に応じて有効化してください。

![Untitled 4.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/c563c760-2973-edfc-ae0c-fba1656bccee.jpeg)


## テストメールを送信する

プラグインのツールにてテストメールが送信できます。指定した宛先にちゃんとメールが届くことを確認してください。blastengineから送信されていれば `[besender-s.jp](http://besender-s.jp)` からメールが来ているはずです。

![Untitled 5.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/2102e3e5-4598-4721-3195-8a51045f8e36.jpeg)


## まとめ

WordPressでは他にも多数のSMTPプラグインがあります。それぞれ設定方法は異なりますが、SMTPサーバーやポート番号の設定は同じです。使いやすいプラグインを利用してください。

blastengineを使うことでメールの大量配信でも問題なく、安定して受信箱に配信できます。ぜひご利用ください。

[blastengine](https://blastengine.jp/)
TerraformのLifecycleブロックを使用して管理対象をカスタマイズする

概要
---
TerraformのLifecycleの引数であるignore_changesを使用することで管理外のブロックを明示的に指定できます。
要するに積極的にコード管理しない機能です。
コンテナ定義をアプリ開発者側でupdateするといったケースでは、リビジョンのインクリメント毎に差分が発生してしまうので、リソースブロック内で差分が出ないことを目的に使用しています。
図では緑矢印のcodepipelineとecsserviceの箇所です。
![スクリーンショット 2023-02-08 15.38.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58788/c6b95676-32dd-7bb7-c575-f9654848bdc5.png)


対象
---

下記ではtask定義とtaskの起動数およびロードバランサーを対象にしています。
（B/Gデプロイメント戦略ではtarget_groupをignoreにしたいが、できない？（検証できてない）のでロードバランサー自体をignoreにしています。）
```hcl
resource "aws_ecs_service" "example" {
  # ...

  lifecycle {
    ignore_changes = [
      desired_count,   # Updated possibly by auto scaling
      task_definition, # Updated by deployments
      load_balancer,
    ]
  }
```

下記のケースでは開発段階ではターゲットとなるブランチが頻繁に変更されるため、
サービスブロック内でネストされた引数であるブランチ名をignoreにしています。

```hcl
resource "aws_codepipeline" "example" {
  # ...

  stage {
    name = "Source"

    action {
      category = "Source"
      configuration = {
        "BranchName"           = "feature/xxxxxx"
        "ConnectionArn"        = xxxxxx
        "FullRepositoryId"     = "xxxxxx/xxxxxx"
        "OutputArtifactFormat" = "CODE_ZIP"
      }
    }
  }

  lifecycle {
    ignore_changes = [
      stage.0.action.0.configuration.BranchName,
    ]
  }
```
rex0220 計算式プラグイン 金額を月で案分計算

計算式プラグインを使って、金額を月で案分計算してみます。

# 概要

日付１と日付２と金額を入力したら月ごとの日数で金額を案分

![2023-02-08_15h31_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/9d5627c0-4f9e-3895-0735-3a958325004c.png)

# 操作例

- 編集画面
![2023-02-08_15h38_12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/8ce2af12-660f-f7ce-f54a-909b9237ac98.gif)

- 一覧画面での編集
編集中は、テーブルの表示に反映されません。
![2023-02-08_17h58_24.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/7ebaf218-ba74-f5cf-a615-ebed0ef731c8.gif)

- モバイルでの編集
![2023-02-08_18h05_28.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/b55e1515-c66c-544f-44b2-3bf1b97180be.gif)


# 計算式プラグインの設定

- 月数分のテーブル行を作成
- 月毎の日数を計算
- 月毎の日数で金額を案分
    - 金額は、切り捨て
    - 最後の月は、余った分

~~~
// 月数
IF(AND(日付１,日付２,日付１<=日付２),
  DATE_DIFF(
    DATE_STARTOF(日付１,"month"),
    DATE_STARTOF(日付２,"month"),
    "months"
  )+1,
  0
)
// 日数
IF(AND(日付１, 日付２),
  DATE_DIFF(日付１, 日付２, "days")+1
)
// 月日数の合計
SUM(月の日数)
// 月金額の合計
SUM(月金額)
// テーブル
IF(月数>0,月数,1)
// No
ROWNO(テーブル)+1
// 年月
IF(日数>0,
  DATE_ADD(日付１,ROWNO(年月),"month","YYYY-MM-01")
)
// 月の日数
IFS(
  DATE_FORMAT(日付１,"YYYY-MM")=DATE_FORMAT(日付２,"YYYY-MM"),
    DATE_DIFF(日付１, 日付２, "days")+1,
  DATE_FORMAT(年月,"YYYY-MM")=DATE_FORMAT(日付１,"YYYY-MM"),
    DATE_ENDOF(年月, "month","DD")-DATE_FORMAT(日付１,"DD")+1,
  DATE_FORMAT(年月,"YYYY-MM")=DATE_FORMAT(日付２,"YYYY-MM"),
    DATE_DIFF(年月, 日付２, "days")+1,
  DATE_ENDOF(年月, "month","DD")
)
// 月金額
LET(cno,ROWNO(テーブル),
  IF(DATE_FORMAT(年月,"YYYY-MM")=DATE_FORMAT(日付２,"YYYY-MM"),
     金額-SUMIF(ROWNO(テーブル)<cno,月金額),
     ROUNDDOWN(金額*月の日数/日数)
  )
)
~~~


![2023-02-08_16h14_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/ff70722e-e431-f3f6-d8fa-7d540c95df91.png)
個人開発をはじめよう！クリエイター25人の実践エピソード   14章 ｢モチベが続かない｣ を要約 +α

# 前置き
***この記事は以下の本の14章の要約です｡***

https://www.amazon.co.jp/dp/4844378147

# 原因1:構想が大きすぎる

## 自分ひとりで作り切れるサイズだろうか?
目安として開いている時間をすべてつぎ込んで1ヶ月で核となる部分を作れるかどうか
ダメそうならサービスのサイズを考えるべき

## 削るところは削ってまずは最低限の核となる部分を絞りだす
最低限の機能を備えた製品を作り､顧客の反応(自分含め)を見ながら改善する

例 twitterみたいなsns
マイページ､フォローなどは一旦おいておく｡
肝心な投稿､投稿した文章をみる機能を最初に作る

## 公開できる部分を切って､そこだけ作ってユーザーテストする
自分で簡単に使い方を説明して､触ってもらう
馬鹿にされるかもしれないが､モチベが逃げる前にプラスの声を聞いて歩みを止めないようにする

## 作る時間を取る計画をする
一人開発合宿としてビジホに1泊など

# 原因2:技術的にしんどい
使ったことがない技術の練習として作るのと､新規開発は分けるべき
仮に新規開発開発で使ったことがない技術を採用する場合､1つ2つなどのごくわずかに留めるべき
技術習得中は､サービス開発が進んでないように見えて心が病む｡

技術を習得する場合は､できるようになった事や学んだ事を記録してステップアップが見えるようにするとよい｡

## 汚いコードでも､まずは動くものを作る
よく聞く｢完璧を目指すよりまず終わらせろ｣というやつ
個人開発なら最初はファイルわけしたり､コンポーネントで分けずに1つのファイルに詰めても徐々にリファクタリングをすれば良いと思う｡

## 動的な部分も静的なもので一旦作る
後から入る動的な数字などもまずは決め打ちで書く｡
細部にこだわるより一旦全体を通して使えるものにするのが先
例
* いいね数などの数字が入る部分に[100]など直接書いてみる
* ユーザーアイコンは仮画像を埋めておく

## 最初は1つのosだけに対応する
仮にwebアプリを作る場合はまずはパソコン版だけ作って後にスマホなどに対応させる｡
スマホをメインにする場合は､`scale`を使ってスマホの画面を引き伸ばしす｡

## cssを書かずにコアな部分だけ先に作る

## 外注する
どうしても自分が苦手な部分は外注してみる｡

# 原因3:作っている間に似たようなサービスが出てきた
## 同じ事を考える人は沢山いるので気にしない
***Googleは世界初の検索エンジンではない***というように世界中探せば､自分が知らないだけで似たようなサービスは沢山ある
違う人が似たようなサービスを作ったとしても､全く同じサービスにはならず､機能は似ていても思想まで一致することはほとんどない｡
(google meetとzoom)(slackとdiscord)

# 原因4:他のメンバーが動かない、自分の負担が大きい
チームで開発する場合

## 割り切って全部自分でやる
動かないメンバーは無視
下手に頼るとコミュニケーションコストが高くなって、意思決定が遅くなる。
権利関係でもめないように、納得のいく形で証拠を残す。
口を出すだけのメンバーにアイデアを出したから俺にも取り分よこせと言われないように決めることは決める。

## 非情になる
なぁなぁで事をすすめない。
プロジェクトにとってプラスになることしかしないと割り切る。

## 一旦解散する 
一旦解散して再度あつめる。
解散するときは資産を誰が持ち帰るのかきめる


# 原因5:作ったけど誰にも使われていない
## 自分でとにかく使う
自分で使ってみてサービスのいいところがあったら､また友達に見せる
サービスを色々と使っていくうちに新しい機能を思いついたり､今後の展開を思いつくことがある｡

## 自分がほしい部分を作る
自分がファーストユーザーになれないサービスは作っても面白くない｡
自分のための機能を作っていく｡
自分のためだけのサービスになってもそれはそれでよし｡

## 他の開発者と交流する
ツイッターのハッシュタグを使う
コミュニティに参加

# 駄文(自分の考え,自分語りなど)
> ***Googleは世界初の検索エンジンではない***というように世界中探せば､自分が知らないだけで似たようなサービスは沢山ある

ここに私の考えを加えると､海外の人が作ってあるけど､日本語版はまだ作られていないパターンがあると思う
私も卒業制作でマイナーなテーブルゲームを日本語アンドロイドアプリ(厳密には日本語と英語)としてリリースした｡
それまでにあったアプリでは､
* 対戦ロボットがない
* 日本語に対応していない
なので狙い目だった｡
しかし､リリース1週間前に企業が完全上位互換を先にストアにだした｡
企業に先に越されたそのアプリが英語に対応しているかはわからない｡

> 作ったけど誰にも使われていない

個人開発投稿,個人開発宣伝サービスを使ってみる｡(放課後アプリ部など)
twitterで告知

> 自分でとにかく使う

私はとあるwebアプリを作っている｡
そしてサービスの一番の顧客は私でありそもそも私の困りごとを解決するために作ったもの｡
使っているからわかるバグや改善点が見つかる
(業界でこれをドッグフーディング､もしくはアイスクリーミングというらしい)

> 他の開発者と交流する

個人開発LT大会などに登壇して宣伝して､他の人の開発物をみて刺激を受けるとよいと思う｡
conpass,techstreetなどで探してみると良いかも｡
youtubeにLT大会などのアーカイブが残っていたりする場合もあるので検索してみると良いかも｡
見ている方が学生さんならばぜひ***技術祭***というイベントに参加してみてほしい｡
Backlog上のgitリポジトリのファイルを開くURLをVSCodeのGitLensで取得する

VSCodeの拡張機能のGitLensの機能で、右クリックメニューの「形式を指定してコピー」から、そのファイルをgitリポジトリのホストサイト上で開けるURLを取得することができると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3123353/8dc4d026-c43d-c93d-d683-c0a2f1333d30.png)

しかしこの機能はBacklog上のリポジトリの場合、デフォルトのままだとURLをちゃんと取得できなかったので、設定内容のメモです。

（参考：https://github.com/gitkraken/vscode-gitlens#remote-provider-integration-settings- )

## 設定内容

`.vscode/settings.json` に次の内容を適当に変更して追加すれば完了です。

```json:.vscode/settings.json
    "gitlens.remotes": [{
        "domain": "hoge.backlog.jp",
        "type": "Custom",
        "name": "hoge namae",
        "protocol": "https",
        "urls": {
            "repository": "https://hoge.backlog.jp/${repo}",
            "branches": "https://hoge.backlog.jp/${repo}/branches",
            "branch": "https://hoge.backlog.jp/${repo}/commits/${branch}",
            "commit": "https://hoge.backlog.jp/${repo}/commit/${id}",
            "file": "https://hoge.backlog.jp/${repo}?path=${file}${line}",
            "fileInBranch": "https://hoge.backlog.jp/${repo}/blob/${branch}/${file}${line}",
            "fileInCommit": "https://hoge.backlog.jp/${repo}/blob/${id}/${file}${line}",
            "fileLine": "#${line}",
            "fileRange": "#${start}-${end}"
        }
    }]
```

backlogだと行の指定が`#12`のように数字の前に`L`が不要なのがgithubと違っていて注意ですね。
Zoom Webhookのvalidationを通す PHP編

Zoom APIのWebhook機能を使用しようと思い、endpoint URLを入力したところこんなエラーが出ました。

```
Event notification endpoint URL - URL validation failed. Try again later
```

[Using webhooks](https://marketplace.zoom.us/docs/api-reference/webhook-reference/#validate-your-webhook-endpoint)

ドキュメントを読むと、CRCという形式を返さないとvalidationが通らないとのこと。
Node.jsのサンプルしかなかったので、PHPで書き直してみました。

# challenge-response check (CRC)

まず、Zoomアプリに記載されているSecret Tokenを用意します。
そしてエンドポイントのPHPを書きます。

```php
$body = file_get_contents('php://input');
$bodyJson = json_decode($body, true);

// Secret Token
$consumer_secret = '***';

$hashForVerify  = hash_hmac( 'sha256', $bodyJson['payload']['plainToken'], $consumer_secret, false );

// CRCのレスポンス
$response =  [
    'plainToken' => $bodyJson['payload']['plainToken'],
    'encryptedToken' => $hashForVerify,
];

// ステータスコード200をセット
http_response_code ();
echo json_encode( $response );
```

これで無事validationが通りました。
Python基本文法1(リスト、辞書、for文 編)

# 概要
Python初心者です。自分の備忘録用です。
どなたかの助けになれば幸いです。

# 勉強したサイト
[Progate](https://prog-8.com/dashboard)

# 文法

#### 複数のデータを取り出す
```Python:ターミナル.
変数名s = [要素0,要素1,要素2,...]
#↑変数名は複数形が多い。
```
```Python:例.
animals = ['dog','cat','owl']
```
全体のまとまりをリストと呼ぶ。


```Python:例.
animals = ['dog', 'cat', 'owl']
print(animals)
```

```Python:例の結果.
['dog','cat','owl']
#↑リスト全体で取得できる
```

要素のインデックス番号は0,1,2,3...と続いていく
上の例で見ると0=dog,1=cat,2=owl

#### リストの要素を取得する
```py:文法
変数名s = [要素0, 要素1, 要素2...]
print(変数名s[インデックス番号])
```
```py:例
animals = ['dog','cat','owl']
print(animals[0])
```
```text:例の結果
dog
```

#### リストの要素を全て取得する

```py:文法
変数名s = [要素0,要素1,要素2]
for 変数名 in 変数名s(リスト):
# ↑イメージは「変数名」に変数名s(リスト)をinする（代入する）イメージ
     print(変数名)
```
```py:例
animals = ['dog','cat','owl']
for animal in animals:
    print(animal)
```
```text:例の結果
dog
cat
owl
```

#### 辞書
辞書はリストと同じように複数のデータを保管するものですが、リストと違いここの要素をキーという名前をつけて管理します。

```text:文法
変数名s = {'キー0': '値0', 'キー1': '値1', 'キー2': '値2'}
```
```py:例
vegetables = {'greenpeppar': 'green', 'pampkin': 'yellow', 'tomato': 'red'}
print(vegetables) 
```
```text:例の結果
{'greenpeppar': 'green', 'pampkin': 'yellow', 'tomato': 'red'}
```
```py:例
vegetables = {'greenpeppar': 'green', 'pampkin': 'yellow', 'tomato': 'red'}
print('私の好きな色は' + vegetables['tomato'] + 'です')
```
```text:例の結果
私の好きな色はredです
```

#### 辞書の要素を更新
```py:文法
変数名s = {'キー0': '値0', 'キー1': '値1', 'キー2': '値2'}
変数名s['キー0'] = '値0x'
# ↑この値0から値0xに更新！
```

```py:例
fruits = {'キー0': '値0', 'キー1': '値1', 'キー2': '値2'}
fruits['キー0'] = '値0x'
print(fruits['キー0'])
```

```text:例の結果
値0x
```

```py:例
vegetables = {'greenpepper':'green','pampkin':'yellow','tomato':'red'}
vegetables['tomato'] = 'black'
print(vegetables['tomato'])
```
```text:例の結果
black
```

#### 辞書の要素を追加

```py:例
vegetables = {'greenpepper': 'green', 'pampkin': 'yellow', 'tomato': 'red'}
vegetables['watermelon'] = 'green_and_black'
print(vegetables)
```
```text:例の結果
{'greenpepper': 'green', 'pampkin': 'yellow', 'tomato': 'red', 'watermelon': 'green_and_black'}
```

#### 辞書の要素をまとめて取り出す
```py:例
vegetables = {'greenpepper':'green','pampkin':'yellow','tomato':'red'}
for vegetable_key in vegetables:
    print( vegetable_key + 'の色は' + vegetables[vegetable_key] + 'です')
```
```text:例の結果
greenpepperの色はgreenです
pampkinの色はyelllowです
tomatoの色はredです
```
vegetables_keyだけを呼び出すとキーそのものを呼びだす。vegetables[vegetables_key]で呼び出すと、キーとしての働きをしてくれて、値を呼び出してくれる
AWS Security Hub を有効化したが「データなし」と表示される

# 課題
AWS Security Hub を有効化したものの「データなし」と表示される。
■セキュリティスコアが出ていない状態
![0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/c710f60a-a224-dadb-9497-1c8369825a67.jpeg)

# 解決できなかったが実施したアクション
有効化を一気に実施したためデータが取れなかったんじゃないか？と推測。
1個1個「無効化」「有効化」を試したものの値とれず。

# AWS公式情報
AWS Security Hub を有効にするには AWS Config でレコーダーが有効になっている必要がありそう。
https://aws.amazon.com/jp/security-hub/faqs/
![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/b0869c99-7631-6e48-01e5-c75d70a73d5c.jpeg)
■AWS Config のレコーダー状況を見ると「オフ」な状態
![41.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/17198482-7962-25a8-e66a-a6137972bebd.jpeg)


# 解決アクション1
AWS Config のレコーダーの記録を有効化。
■編集画面で「記録を有効化」を選択すると情報が色々表示されてくる
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/64edaf17-d0aa-4e4b-3d02-7dd995f80bcf.jpeg)
■記録がオンの状態
![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/0c5b95ae-93cd-f22b-d0e1-4add01297b05.jpeg)

# 解決アクション2
再度 SecurtyHub で「無効化」→「有効化」を実施。
※ その日はセキュリティスコアは出てきませんでした。

# 実装後の翌日
翌日の朝も変わらず表示されていませんでした。
昨日の AWS Config 有効化実施から、24時間くらい経ったくらいにセキュリティスコアが表示されました。
■セキュリティスコアが表示される
![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2292878/1a77700d-8333-7ffc-175f-697476672930.jpeg)
Azure Administrator(AZ-104)：Azure での ID とガバナンスの管理「Azure AD B2Bによるゲストユーザアクセス」

Microsoft Learnを用いたMicrosoft Azure Administrator(AZ-104)の試験内容をまとめる投稿です。
この投稿ではMicrosoft Learnで学ぶ内容をできるだけ簡潔にまとめております。
Azureの勉強を一から始め、AZ-900取得後の資格としてAZ-104取得を目指している方の参考になれば幸いです。
※AZ-900の内容は本投稿では省略しております。再度復習したい方は下記をご参照ください。
[Azure Fundamentals(AZ-900)：試験合格に向けた学習内容のまとめ](https://qiita.com/fsd-jume/items/e4ec76959adb547ac5e8)

## 今回の学習内容
**◆「Azure AD B2Bによるゲストユーザアクセス」**
◇対応するMicrosoft Learn ラーニングパス
[AZ-104:Azure での ID とガバナンスの管理](https://learn.microsoft.com/ja-jp/training/paths/az-104-manage-identities-governance/)
◇対応するモジュール
[ゲスト アカウントと Azure Active Directory B2B を使用して共同作業を行う](https://learn.microsoft.com/ja-jp/training/modules/create-users-and-groups-in-azure-active-directory/6-collaborate-guest-accounts-azure-ad-b2b)

※前回の投稿は下記から確認できます。
[Azure Administrator(AZ-104)：Azure での ID とガバナンスの管理「グループにおけるアクセス権付与の方法」](https://qiita.com/fsd-jume/items/704dfa2017b98ed29e7e)

## Azure AD B2Bによるゲストユーザアクセス
### Azure AD B2Bコラボレーション
・Azure ADテナントにゲストユーザを招待し共同作業を可能にする
・外部ユーザに一時的/制限されたアクセスを付与する際に使用

**◆外部ユーザの招待**
・外部ユーザを招待することでAzure ADにゲストアカウントとして追加
・電子メール/直接リンクで招待可能
・デフォルトでAzure ADのユーザと管理者は招待可能
⇒全体管理者が設定変更可能

**◆外部ユーザのID**
・ 外部ユーザは各自の資格情報を使用するため、外部ユーザのID管理が不要
※各自の資格情報：職場/学校/ソーシャルのID
・アカウントの同期/ライフサイクルの管理も不要

### B2Bコラボレーションとフェデレーション
**◆Azure ADとのフェデレーション**
・フェデレーション(ID連携)：信頼が確立されたドメインのコレクション
・オンプレミス側にADフェデレーションサービス(AD FS)が必要
⇒ Azure AD Connectを使用してAD FSとAzure ADでフェデレーションを構成可能
・Azureリソースへの認証と承認をオンプレミスのIDで可能
・外部ネットワークで認証を試みるユーザがいる場合は、Webアプリケーションプロキシが必要

**◆B2Bコラボレーションとフェデレーションの比較**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851723/f2009b9a-2b50-c06c-e48d-c3810eeaf6e9.png)

## 次の学習内容
**◆Azure ADセルフサービスパスワードリセットの詳細**

https://qiita.com/fsd-jume/items/4fdcdc519bc7cd2bdcaa

## 学習内容をまとめたページの一覧

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
python-oracledbでDPY-3015が出た時の対策

# TL;DR


```terminal:terminal
$ pip install oracledb
```

```Python:test.py
import oracledb

oracledb.init_oracle_client() # <- これが重要！！！

con = oracledb.connect('%connect_string%')
cur = con.cursor()

cur.execute('SELECT * FROM dba_users')
for row in cur:
    print(row[0])

cur.close()
con.lose()
```


cf. [3.1. Enabling python-oracledb Thick mode](https://python-oracledb.readthedocs.io/en/latest/user_guide/initialization.html#enabling-python-oracledb-thick-mode)

// 本文は後でかきます
Androidアプリを作りたい【開発環境編】

## 概要
　自分が利用してみてよかったサイトのまとめになっています。開発環境の作成から、簡単なアプリの作成までのまとめです。よかったら参考にしていってください。
## 導入
　ここ最近はJavaの勉強を行っているのですが、その勉強の一環としてwebアプリを作成しました。ざっくりいうと2人専用の割り勘アプリなのですが、人数を増やそうとしたところ難易度が飛躍的に跳ね上がったため、ひとまずの完成ということで、新しいものに手を出そうと思います。
　今回は、母があると便利かもということを以前言っていた「__カメラで認識して、楽に家計簿を作成できるアプリ__ 」を目指していこうと思います。
## Androidアプリの開発環境を準備する

https://www.sejuku.net/blog/8462

:::note warn
エミュレータ―を起動しただけでは、仮想端末は起動されていないため、作成したアプリを実行しようとするとエラーが出る。
:::
:::note
解決方法
・仮想端末の電源を入れる。
　→左上に電源ボタンのマークがあるので、押す。
:::

　ここから各種OSに合わせた環境構築に飛ぶことができました。自分はWindowsであるので、Windows前提で進めさせていただきます。

　自分の場合、サブ機にandroid端末を利用しているため、エミュレーターの設定は後回しにしようと思ったのですが、実機での実行は __Andoroid11+__ とのことでできませんでした。仮想マシンで試す場合は、__エミュレーターの記事__ を飛ばさずに読むことをお勧めします。
　エミュレーターの設定が終わったらこちらの記事を参考に簡単なゲームを作成してみると、Android Studioの使い方が動的にわかる気がします。

https://style.potepan.com/articles/31261.html

https://style.potepan.com/articles/31311.html

:::note
Design画面ですべて作成しようとすると、むしろ大変なので最初ひとつDesign画面でどういう書き方かを確認すると、スムーズに調整や作成することができる。
:::
## まとめ
　簡単なAndroidアプリを作成することができたが、リリースなどはしていない。実際にリリースできるだけのクオリティのあるアプリを作るまでは、まだまだかかりそうだと思うものの、そこそこ納得できるだけのクオリティが確保出来たら、簡単なアプリでよいので配信してみたいと思う。
iOSでAPNGを作成してみる

iOSでAPNGを作成したいので調べてみた。
適当なライブラリを調べたら[APNGKit](https://github.com/onevcat/APNGKit)が出てきたが表示機能に限定していた。

C++から作れないのかなとlibpingを調べたらパッチを当てるのに票が集まらないわ、ライブラリがメンテナンスしてないわと混迷を極めていた。
[APNG support on main libpng library? #267](https://github.com/glennrp/libpng/issues/267)

調べていくと、こちらのページ下部にlibpngにapngパッチ当てたリンクやサンプルコードへのリンクがある。
http://littlesvr.ca/apng/

## Apple Document Archive

ニッチ過ぎて情報を見つけるのに手間取ったが、AppleのDocumentにAPNGの作成方法があった。

[Image I/O Programming Guide](https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html)

適当に修正したらうまく動いた。

```Swift: Animation.swift
import UniformTypeIdentifiers

enum AnimationError: Error {
    case create
    case finalize
}

func Animation(_ images: [UIImage], toUrl: URL, loopCount: Int = 0) throws {
  let frameCount = images.count
  
  let fileProperties = NSMutableDictionary()
  fileProperties.setObject(kCGImagePropertyPNGDictionary, forKey: NSDictionary(dictionary: [kCGImagePropertyAPNGLoopCount: loopCount]))
  
  let frameProperties = NSMutableDictionary()
  frameProperties.setObject(kCGImagePropertyPNGDictionary, forKey: NSDictionary(dictionary: [kCGImagePropertyAPNGDelayTime: 1.0 / Double(frameCount)]))
  
  guard let destination = CGImageDestinationCreateWithURL(toUrl as CFURL, UTType.png.identifier as CFString , frameCount, nil) else {
    throw AnimationError.create
  }
  
  CGImageDestinationSetProperties(destination, fileProperties.copy() as? NSDictionary)
  
  for image in images {
    autoreleasepool {
      CGImageDestinationAddImage(destination, image.cgImage!, frameProperties)
    }
  }
  
  if !CGImageDestinationFinalize(destination) {
    throw AnimationError.finalize
  }
}
```

評価用にプロジェクト内のPNGを探してテンポラリのAPNGにまとめる処理を書いてみた。
なお、画像は同じ幅のPNGのみ配置。


```swift: Example.swift
func test(){
  let path = Bundle.main.bundlePath
  let items = try? FileManager.default.contentsOfDirectory(atPath: path)
  
  do {
    let fileUrl: URL = FileManager.default.temporaryDirectory.appendingPathComponent("test.png")
    
    if FileManager.default.fileExists(atPath: fileUrl.path) {
      try FileManager.default.removeItem(at: fileUrl)
    }
    
    var images = [UIImage]()
    if let items = items {
      try items.sorted().forEach { f in
        if f.hasSuffix(".png") {
          let fileUrl = URL(fileURLWithPath: path).appendingPathComponent(f)
          if FileManager.default.fileExists(atPath: fileUrl.path) {
            let data = try Data(contentsOf: fileUrl)
            if let uiImage = UIImage(data: data) {
              images.append(uiImage)
            }
          }
        }
      }
    }
    try Animation(images, toUrl: fileUrl)
    print(fileUrl)
  } catch {
    print(error.localizedDescription)
  }
}
```
回復されない！HDDなどストレージを処分する祭のデータ消去(Windows)

# 漏洩怖いからストレージは破壊する？！
もったいないですよね。まだまだ、使えるのに。ということで、主に譲渡する際に、以前保存していた中身を完全に消去する方法です。
## とはいえ完全にアクセスできないようにするには破壊するしかないのかも？
ここで紹介する方法は、完全ではないかもしれませんが、ある程度確証のある消去の方法です。
# 大まかな流れ
1. すべてのパーテーションを削除
1. 全容量で新しいパーテーションに新しいドライブを作成、Windowsにマウントする
1. 新しいドライブをBitlockerで暗号化、適当なパスワードを設定、
1. 記憶やメモからパスワードを消去
1. 作成したパーテーションを削除
# ポイント
パーテーション削除してもデータの残骸が残っているから、Bitlockerで暗号化してパスワード設定。で、残骸にもアクセスできないようにするところです。
#注意
マイクロソフトアカウント等にBitlockerパスワードや回復キーが保存されていたりするので、それもスッキリ削除しましょう。