OmniAICreator/Qiita-1.07M huggingface dataset:
なぜかMail Delivery Subsystemというメールが送られてくるんですけど、、


## はじめに
現職でユーザに外部のメールサーバ、独自ドメインでメールをできるようにしております。

ある日、ユーザからメールは送っていないものの、Mail Delivery Subsystemというタイトルのメールが何通も来ているとお問い合わせがありました。

最初、そのお問合せを聞いた時は、？？と思ってしまいました。ユーザがメールを送ったことを忘れていたのでは？と思っていたのですが、外部で使用しているメールサーバの会社に問い合わせたところ、ユーザから送信した履歴がないとのことでしたため、調査しました。また、メールの仕組みについてあまり知識がなかっため、学習しました。


## Mail Delivery Subsystemとは

上記のエラーは宛先のアドレス間違いや受信者のメールボックスの容量がなかった場合などに発生するエラーです。

以下記事にもエラーの詳細が書かれていますが、身に覚えのないメールは成りすましメールの可能性もあることを知りました。

https://flets-w.com/chienetta/communication/cb_mail42.html

悪意のある人がユーザのメールアカウントに不正アクセスして、誤った宛先にメールを送っている可能性もあります。

もしなりすましが行われているとしたら、メールサーバの送信履歴が残っているはずですが、それが残っていないということは別の可能性があるのでは？と思いました。

## メールヘッダーを確認
メールにはメールヘッダーというどのメールサーバを経由したのか、メールソフトは何を使用していたのかなどの詳細な情報を見ることができます。

GmailやYahooなどのメールソフトからでも簡単に見ることができます。

送信元のメールサーバに関する情報を知りたかったため、**Received**を確認しました。

>Received: from 送信したパソコン名またはホスト名など (右記のIPアドレスのホスト名[送信したコンピュータのIPアドレス])
by 受け取ったサーバ名 with SMTPなどの転送方法 id 転送時のID番号
for 宛先のメールアドレス; 処理時刻

引用元: [メールヘッダ情報の確認方法](https://memorva.jp/internet/security/mail_header.php)

メールサーバのIPアドレスを確認したところ、中国であることがわかりました。日本ではない。

また、**From**の情報を見たところ、「〇〇銀行株式会社」となっており、明らかにお客様の企業名ではありませんでした。

明らかに別の会社がユーザになりすまして、メールを送っていることがわかりました。

でも、どうやってなりすましているのだろうか？

調べたところ、Gmailのようなメールソフトでも簡単に他人のメールアドレスをFromに指定できるようです。

https://www.howtonote.jp/gmail/sent/index15.html

今回もおそらく悪意のある第三者がユーザのアドレスを使用して、スパムメールを大量に送りつけていたと推測されます。

そして、無作為に送ったメールの中には誤ったアドレスもあり、その送信エラーメールがユーザに届いていたと思われます。


## 対応方法

メールのドメインを変えてもまた変更後のメールアドレスで成りすましをされてしまうイタチごっこになってしまう可能性があるため、ユーザのメールサーバ側で差出人がエラーメール（MAILER-DAEMON@〜）だったものをブロックするようにしました。

エラーメールはゴミ箱に保存されるため、最悪ユーザがメールを送る際にアドレスを間違ってしまった場合や受信側のメールサーバの容量がいっぱいだった場合などにエラーメールはゴミ箱に保存されます。ユーザからメールが送れなかった場合にはエラーメールがすぐに見れないという欠点がありますので、もっと良い方法がありそうな気がしますが、他の方法がチームでも思いづかずでした。


## 今後

今回の問い合わせ対応でメールに関する基本的な知識が足りないことを痛感して、以下の書籍も読みました。専門的な用語についても平易に書かれており、図も用いて説明されていてとても分かりやすかったです。

[実務で使える メール技術の教科書 基本のしくみからプロトコル・サーバー構築・送信ドメイン認証・添付ファイル・暗号化・セキュリティ対策まで](https://www.amazon.co.jp/%E5%AE%9F%E5%8B%99%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-%E3%83%A1%E3%83%BC%E3%83%AB%E6%8A%80%E8%A1%93%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%9F%BA%E6%9C%AC%E3%81%AE%E3%81%97%E3%81%8F%E3%81%BF%E3%81%8B%E3%82%89%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB%E3%83%BB%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E6%A7%8B%E7%AF%89%E3%83%BB%E9%80%81%E4%BF%A1%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E8%AA%8D%E8%A8%BC%E3%83%BB%E6%B7%BB%E4%BB%98%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%BB%E6%9A%97%E5%8F%B7%E5%8C%96%E3%83%BB%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E5%AF%BE%E7%AD%96%E3%81%BE%E3%81%A7-%E5%A2%97%E4%BA%95-%E6%95%8F%E5%85%8B-ebook/dp/B0CQQ5BGC8)

今後は実際にメールサーバをAmazon SESで構築したり、Route53でSPFレコードやDKIMレコードを設定することでよりメールに関する理解を深めていきたいと思いました。
ループの制御

## はじめに

ループの制御について整理する。

### break

現在のループを強制的に中断する。
breakはifと合わせて使うのが一般的。

```php:break.php
<?php
$sum = 0;
for ($i = 1; $i <= 100; $i++) {
    $sum += $i;
    if($sum > 1000) { break; }
}
print "合計が1000を超えるのは、1~{$i}を加算したときです。";
    //合計が1000を超えるのは、1~45を加算したときです。
```

### continue

現在の周回だけをスキップし、ループそのものは継続して実行する。

```php:continue.php
<?php
$sum = 0;
for ($i = 1; $i <= 100; $i++) {
    if($i % 2 !== 0) { continue; }
    $sum += $i;
}
print "合計値は、{$sum}です。";
    //合計値は2550です。
```
ぼっちでもControl Tower連載[1/2]~とりあえず作ってみた~

# なんでControlTowerを触りたいんや？
案件で顧客にControl Towerを推奨する事になりました、一度も触った事がありません。知らないものを推奨するのは心が痛いです。

大きい組織向けのサービスだと思っていたので今まで触ってきませんでしたが、ベストプラクティスに沿った構成を自動で作れるという事で、勉強のためにぼっちでも触るべきだと考えました。
この記事ではとりあえずコンソールからLanding Zoneを立てる所まで行い、設定の確認や開発環境の構築は次回以降の記事で取り扱います。
# そもそもControl Towerとは
一口で言うと、マルチアカウント環境のベストプラクティスに沿った仕組み(Landing Zone)の基で構成するサービスです。コンソールからメールアドレスの指定等行う事で、以下の構成が一気に出来上がります。
- Organization Unit(OU) Security OUと配下のログアーカイブアカウント、監査アカウント
- 予防的ガードレール(SCP)
- 発見的ガードレール(AWS Config)
- etc...

概要はこちら
https://aws.amazon.com/jp/controltower/
詳細はドキュメント散らばってて良く分からないのよな...
https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/aws-control-tower/deploy.html
良いまとめ資料あったら教えてください。

# とりあえず管理コンソールから立ててみた
やることリスト
0. メールアドレスを用意する
1. 料金の確認とリージョンの選択
1. 組織単位(OU)の設定
1. 共有アカウントの設定
1. その他の設定
1. ランディングゾーンの確認とセットアップ
## 1.リージョンの選択
まず料金の確認とリージョンの選択です。ホームリージョンとそれに加えてガバナンスの対象にする他のリージョンを選択します。今回はとりあえず東京リージョンのみで作成します。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr1_resion_select.png)

## 2.組織単位(OU)の設定
次に組織単位の設定をします。
Security OU配下にログアーカイブアカウントと監査アカウントが作成され、デフォルトでSandBox OUが作成されます。その名前を任意で変更出来ます。
Security OUはそのまま、SandBox OUはもう本番という事で"Prod"に設定しました。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr2_ou_setting.png)

## 3.共有アカウントの設定
ここではSecurity OU配下に作成されるアカウントの設定を行いました。
まず新規に作成するか、既存のアカウントで設定するか、
新規で作成する場合はメールアドレスを指定します。
アカウントの表示名はデフォルトでLog ArchivとAuditが設定されていましたが、変更も出来るようです。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr3_account_setting.png)

## 4.その他の設定
AWSアカウントのアクセス設定:IAM Identity Centerか他か...
AWS CloudTrailの設定
基本デフォルトで良いかと思います。独自のADでIAMのIdentity Centerのユーザを作成したい場合はその設定がここで必要になると思われます。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr4_access_setting.png)

## 5.ランディングゾーンの確認とセットアップ
最後に設定内容の確認とControl Towerのアクセス許可をします。
最後に"ランディングゾーンの設定"ボタンを押します。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr5_allow_access.png)

# 速攻でセットアップ完了
"ランディングゾーンの設定"を押したら以下のセットアップ中の画面に遷移します
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr6_waiting.png)

セットアップに入り、30分昼寝をしている内に完了していました。早い...
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr7_completed.png)

IAM Identity Centerでユーザを確認した所、アクセス用のユーザが1件作成されていました。このユーザに管理アカウントで許可セットを割り当てて、各アカウントの操作が出来るはずです。
![ダミー画像](https://qiita-img-20240309.s3.ap-northeast-1.amazonaws.com/CntTwr8_SSOuser.png)

# 今回はここまで/次にやりたい事
今回はControl Towerを使って容易にLanding Zoneを設定する事が出来ました。
現状では、とりあえずLanding Zoneが出来ただけなので、次は各種設定を確認していこうと思います。
ラズパイで簡単にSeleniumをはじめる

# ラズパイでスクレイピングしたい
リアルタイムで監視するのにPaaSだと柔軟性がない(お金がかかる)ので諦めて一から何でもできる環境を作ることにしました

# 環境構築
- sample-envには好きな環境名を付けます。
```text:
python -m venv sample-env
```
- 環境内に入る
```text:
source sample-env/bin/activate
```
- Seleniumをインストールする
```text:
pip install selenium
```
- chromium driverをインストール
```text:
sudo apt install -y chromium-browser
```
# 実行
- 以下Pythonコードを記述し、実行
```python:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

options = Options()
options.BinaryLocation = ("/usr/bin/chromium-browser")
options.add_argument('--no-sandbox')
options.add_argument('--ignore-certificate-errors')
options.add_argument('--headless')
options.add_argument('--disable-dev-shm-usage')

service = Service("/usr/bin/chromedriver") # 
driver = webdriver.Chrome(options=options, service=service)
driver.get("最初に確認したいURL")
driver.save_screenshot("キャプチャ名")
driver.close()
```
上記のコードを修正して色々試してみてください。

# 余談
- 以下のように記述すれば一致するクラス名のテキスト要素を取得できます。

```python:
# 単一クラスの場合
class_names="class-a"
# 複数クラスの場合
class_names=".class-a.class-b"

elements = driver.find_elements(By.CSS_SELECTOR, class_names)

for element in elements:
    text = element.text
    print(text)
```
Fusion360でラックアンドピニオンを作る

# 設計条件
* モジュールは0.8
* ピニオン
    * 歯数は12
    * つまりピニオンの基準円は、歯数×モジュール=12*0.8=9.6mm
* ラック
    * モジュールは0.8なので基準線は、0.8mmの距離のところ

# ギアの作成
FM Gearsというアドインを使って作成

https://apps.autodesk.com/FUSION/en/Detail/Index?id=6294804001663532834&appLang=en&os=Win64&autostart=true

1. アドインを上記リンクからインストール
1. タブのUTILITIES → ADD-INS → Scripts and Add-Ins → FMGearsを選択してRunを押す
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/7cb99279-a583-0475-c791-6bd4d8d07112.png" width="400">

1. Createタブの下の方にFMGearsがあるのでこれをクリックして起動
1. Rack GEASでラックを作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/52cd2c0d-cedf-7f85-abdf-103b6da8246a.png" width="200">
 
1. Spur GEASでピニオンを作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/c763383d-7d89-08ae-fd4f-dfbf53a30835.png" width="200">

# アセンブリ
スケッチは直径に正接する形で引けばかみ合います
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/f55a70cc-86d7-3185-7920-4c4f554c99fa.png" width="300">

## ラック
ラックがぶれないための壁を作成。
壁とラックの距離は、ラックとのクリアランス+ラック自体の幅-モジュールとします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/8179dee0-3a71-be24-a840-e949781ca0b4.png" width="300">

壁からの距離とラックの背を選択し、ジョイントを設定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/7b9ad1b9-e8d7-ecc7-652d-f2b1ebacc7d6.png" width="400">

モーションタイプはスライダーを選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/66e98153-8536-316a-42c1-9aaa0824983c.png" width="300">

## ピニオン
ピニオン用の軸を作成し、軸とピニオンの中心穴を選択してジョイントを設定します
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/37a13b08-f57a-d2d8-e935-6605f1b6d6de.png" width="500">

モーションタイプはくるくる回るRevoluteを選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/f6aaa784-0e55-c375-8fc4-d093e2958f7a.png" width="200">

## ジョイントリンク
ギヤ同士がかみ合いながら回るようにジョイントリンクを設定します

アセンブリ→モーションリンクを選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/0c4566af-b9c0-576d-0a35-d33e10c961fb.png" width="400">

先ほど設定したジョイント2つを選択し、パラメータを入力。Distanceは、何を指しているか分かりづらいですが、ピニオン1回転あたりのラックの移動距離です。今回は、歯数12、モジュール0.8のため、12×0.8×3.14mmが1回転の移動距離となります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/46e10894-eed3-e41c-8ad4-4392c11d6f22.png" width="500">

回してもギヤのかみ合いがずれなければOK
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3324101/a7873e4f-6d58-3522-8d2e-ae5d18780243.png" width="400">

# おわりに
久々にやったら思い出せなかったのでメモです
3Dプリンタ製なのでクリアランスは大き目にしています
余力あったらもう少し見やすくします
諸事情で英語版を使っていますが、日本語訳分かる人いたらお知らせください。
【Git Bash】aliasの登録を永久化する

# 要点
・git bashでaliasを永久的に使えるようにする
・他のシェルセッションやログイン時にも利用できるaliasの設定をする

# 経緯
git bashからリポジトリにpushするまでの一連のコマンド入力を時短したかったので、調べたことをまとめました。
Tab補完でも充分ですが、いちいちgitまでは入力しないといけないので、よりタイパよく作業したい方の参考になればと思います。
また、git bashでは再起動すると設定したaliasが消えてしまっていたので、永続化する方法を模索しました。


# aliasとは
**既存コマンドにつけることができる別名** のことです。
<br>

例えば、
```
alias ll='ls -l'
```
上記のように設定しておけば、llと入力するだけでいちいちls -lと入力する必要がなくなります。
長いコマンドを省略したり、本記事のように時短テクとして使っています。


# 手順
① ホームディレクトリに **.bashrc あるいは .bash_profile**があることを確認
```ruby
ls ~/.bashrc
```
※1) なければ、touchコマンドで作成
※2) .bashrcと.bash_profileの違いに関しては以下参照

https://qiita.com/magicant/items/d3bb7ea1192e63fba850

<br>

<br>
② viエディタを起動し、aliasを書き込む

```ruby
vi  ~/.bashrc
```

```ruby
(viエディタ)
alias ga='git add .'
alias gc='git commit'
...etc
```

<br>
<br>
③sourceコマンドor再起動で反映する

```ruby
source ~/.bashrc
```

# 注意点
・aliasの表記に関して、=周りのスペースには要注意です。
ついプログラミングのノリで、
alias ga = 'git add .'
としてしまったのですが、次回起動時にエラーが出てしまいました。


# 参考サイトなど
・chatgpt
・やまだのログ 様
・Webエンジニア学習部屋 様

https://appdev-room.com/web-linux-alias

https://ken17yama.github.io/next-blog/posts/devops_alias
[Python]特定の文字（あるいは数字）を配列から探す方法

# in

簡単ですが、初心者が気になる問題を持ってきました。

今日は特定の文字（あるいは数字）を配列から探す方法について勉強しました。

色々方法があると思いますけど、今度は関数inを使います。

<br>

ここに果物が三つあります。
ここからorangeを探します。
```python
fruit = ["apple", "pear","orange"]
```
<br>

orangeが出力されました。
```python
if "orange" in fruit:
    print("orange") # orange
```

<br>

今回は配列にない場合を出力してみました。

inの前にnotだけ追加してくださればいいです。

```python
if "banana" not in fruit:
  print("banana") # banana
```

以上で、特定の文字（あるいは数字）を配列から探す方法について勉強しました。
ありがとうございます。
Astro + Svelte + SST ionでサーバレスなWebアプリ雛形キット準備するよ！

思い立ったが吉日。今すぐ構築するよ。

## ざっくり用語解説RTA

### Astro
コンテンツ駆動でReactでもVueでもSvelteでも組み込めちゃうマルチフレームワーク
https://astro.build/

### Svelte
すごいコンパイラ型フロントエンドフレームワーク。書き味は宣言的UIの系譜の中ではシンプル路線
https://svelte.jp/

### SST ion
CDKをパージして旧来の限界を越えようとしている次バージョンのSST
（現行のSST v2はCDKをベースにしたAWSサーバレス開発キット）
https://ion.sst.dev/docs/

____________________

用語が頭に入ったら、さっそく構築していくよ。

## Astro + Svelte + Tailwind CSS + SST ion構成でサーバレス開発を行うスターターキットを用意する
筆者の好みにより、表題に加えTailwind CSSも統合するよ。

**前提として、aws configureは実施しておいてね。AWSのプロファイルを指定したい人は各自読み替えてください。**

### 事前準備

BunとSST ionをインストールするよ。SST ionを開発している中核のコントリビューターはBun推しみたいで、開発途上のせいか、Bunではなくnpmを使うと予想外のバグが……ってことがある。 **そうなるとSST公式Discordをエラーメッセージで検索しながら苦しむことになるから、最初から素直にBunを使っておくのが手っ取り早い。** 将来の正式リリース時には解消されているだろうけど、そういうわけだから今はBunを使って進めるよ。

```zsh
# Bunのインストール
curl https://bun.sh/install | bash

# SST ionのインストール
curl -fsSL https://ion.sst.dev/install | bash
```

### 雛形作成

一気にコマンドで雛形を作っていくよ。

Astro雛形作成以降は、全てAstroプロジェクトのルートディレクトリで実行する前提だから注意してね。

SST ion + Astroは下記参照。

https://ion.sst.dev/docs/start/astro/

さっそく実行していこう。

```zsh
# Astro雛形
bun create astro@latest
# AstroへのSvelte統合
bunx astro add svelte
# AstroへのTailwind CSS統合
bunx astro add tailwind
# Astro構成のSST ion初期化
sst init
```

それからAstroの設定ファイルで、SvelteとTailwind CSSを統合するよう記述する。

```js:astro.config.mjs
import { defineConfig } from "astro/config";
import aws from "astro-sst";
import svelte from '@astrojs/svelte';
import tailwind from "@astrojs/tailwind";

// https://astro.build/config
export default defineConfig({
  output: "server",
  adapter: aws(),
  integrations: [svelte(), tailwind()]
});
```

これで完成！　動作確認してみるよ。

## 動作確認

まずはSST開発モードでの動作確認。

```zsh
bun dev
```

問題なく動く。もしSST ionでAPIも構築していればLive Lambda機能によりローカルのAPIのコードを本物のAWS Lambdaのコードとして実行しながら動作確認したりできるけど、今はAstroプロジェクトの雛形のみだから、単にローカルで動かしているのと同じだ。

次はAWSへのデプロイ。

```zsh
sst deploy
```

初回はちょっと時間がかかる。デプロイが完了すると、下記構成でAWS上にAstroプロジェクトがリリースされる。

- Amazon CloudFront
- AWS Lambda Function URLs
- Amazon S3

実際にデプロイ完了時に表示されたURLにアクセスしてみると...

![スクリーンショット 2024-04-02 17.11.47.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267156/731c3b17-665e-b3b7-378e-c1b23abd7573.jpeg)

ちゃんと動いてる！

これでAstro + Svelte + Tailwind CSS + SST ionの雛形プロジェクト作成はおしまい！ 実際に雛形を編集してSvelte製コンポーネントを動かしたり、Tailwind CSSでスタイリングすることも可能な状態になっているよ。

あとは自分のプロジェクトをがんがん作り込んでいこう。

## 要点まとめ

- SST ionでトラブルの遭遇を回避したいなら正式リリースまでBun推奨
- Astroに何か統合したいなら `astro add svelte` 等の便利コマンド実行して、astro.config.mjsに追記すればOK
- SST ionはAstroプロジェクトのルートディレクトリで `sst init` すれば一発でAstro構成の初期化が完了する

## おまけ

デプロイしたリソースをAWS上から破棄したければ下記コマンドで実行可能だよ。

```zsh
sst remove
```

## 筆者プロフィール

Kenpal株式会社でITエンジニアとして色々いじってる faable01 です。

https://www.kenpalinc.com/

ものづくりが好きで、学生時代から創作仲間と小説を書いたりして楽しんでいたのですが、当時はその後自分がIT技術者になるとはつゆ程も思っていませんでした。紆余曲折あり、20代の半ばを過ぎた頃に初めてこの業界と出会った形です。

趣味は「技術記事を口語で書くこと」です。

業務日報SaaS **「RevisNote」** を運営しています。リッチテキストでの日報と、短文SNS感のある分報を書けるのが特徴で、組織に所属する人数での従量課金制です。アカウント開設後すぐ使えて、無料プランもあるから、気軽にお試しください。

https://revisnote.com?202403150825
Azure OpenAI Service の非同期変更フィルターによる Stream 対応

Azure OpenAI Service では `stream = True` に設定してもチャンク単位で結果が返ってくるのでちょっと微妙でした。これはデフォルトでコンテンツフィルタリングが同期的に実行されていて、ある程度の文章の長さをバッファしてフィルタリングしているため、トークン単位ではなく、チャンク単位で Stream 結果が返ってきているからです。この同期的処理によって、常に出力コンテンツの安全性が担保されています。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3637343334342f64316335333134362d643632342d636136322d383336322d3965373261623266303462362e676966.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/d3198939-d052-8a1c-0582-41e7f55a741d.gif)

今パブリックプレビュー中の「[非同期変更フィルター](https://learn.microsoft.com/azure/ai-services/openai/concepts/content-filter?tabs=warning%2Cpython-new#asynchronous-modified-filter)」を使用すると、このフィルタリング処理を Stream 出力と非同期に実行することで OpenAI 社と同様の Stream 処理が実現できるようになりました。

:::note info
5/22 すべての Azure OpenAI カスタマーが利用できるようになりました。
:::

Azure OpenAI 限定アクセスのレビュー: 変更されたコンテンツ フィルタリング の承認から1日程度経つと、以下のようにコンテンツフィルター作成ウィザードの「ストリーミングモード」の設定を変更できるようになります。この設定を既定から「非同期変更フィルター」に変更して保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/fc41c56f-4197-3f1f-6330-2c2512b3c51b.png)

この設定のコンテンツフィルターをモデルのデプロイにセットすれば使用できるようになります。あとは、OpenAI SDK などで、 `stream = True` に設定すれば、以下のようにトリックではないヌルヌル出力が可能となります。

![002.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/d453df36-7a14-15f3-b33e-dd9dba3d86bb.gif)

この機能の注意点として、Stream が非同期で停止（最大 1,000 文字の増分）されるまでは本来フィルタリングするべき文章が出力されてしまうことがあります。そのためプログラム処理として必ず出力後に `finish_reason` および `content_filter_result` の返却値をチェックしてフィルターに引っかかっていれば速やかに UI を修正することが求められます。

# 参考
https://learn.microsoft.com/azure/ai-services/openai/concepts/content-filter?tabs=warning%2Cpython-new#asynchronous-modified-filter

# 非同期変更フィルターチェック用
https://github.com/nohanaga/azure-openai-async-mod-filter

# トリックで見せる用
https://qiita.com/nohanaga/items/eea68e0b7a0f417c5276
テストコードのコツと注意点

## はじめに

新卒入社、おめでとうございます
テストコードの初歩的なTipsについてまとめてみました

## Tips

※ コード例はRuby（RSpec）を使用しています

以下のようなBooleanを返す処理に対するテストコードを考えます
`is_xxx?`では、判定処理`check_a?`・`check_b?`両方が**true**の場合に**true**が返されます

```:ruby
class Test
  def is_xxx?
    check_a? && check_b?
  end
end
```

この場合、テストコードはこんな感じになると思います
```:ruby
describe Test do
  describe '#is_xxx?' do
    subject { instance.is_xxx? }
    
    let(:instance) { described_class.new }
    let(:a) { true }
    let(:b) { true }

    before do
      allow(instance).to receive(:check_a?).and_return(a)
      allow(instance).to receive(:check_b?).and_return(b)
    end

    context 'check_a? が false の場合' do
      let(:a) { false }
      it { is_expected.to be(false) }
    end

    context 'check_b? が false の場合' do
      let(:b) { false }
      it { is_expected.to be(false) }
    end

    context 'それ以外の場合' do
      it { is_expected.to be(true) }
    end
  end
end
```

### ポイント
`check_a?`の観点で`context`を記載する場合、
`a`を**false**に設定した場合、期待値は**false**になると思います

この時、`check_b?`の戻り値も**false**に設定してしまうと、
テストコードが不備や設定の誤りで`check_a?`の戻り値が**true**になってしまっていた場合でも、
`check_b?`の戻り値**false**により期待値**false**が満たされてしまいます

これでは正しくテストケースを実施できていないことになります

`context`に記載する要素だけでなく、
**それ以外の要素がテスト結果に影響を与えないよう**注意しましょう。

## さいごに

4月ということで、初歩的な内容ではありますがまとめてみました
新人エンジニアの方にとって役立つことを願っています
シンプルで分かりやすい選択ソートの解説 【Python3.12】

# 選択ソートとは
選択ソートは、シンプルで直感的に書けるソートアルゴリズムです。
実装が簡単なので、小さなデータをソートするのに適しています。
平均時間計算量、最悪時間計算量がともにО(n²)なので効率的ではありません。


## 配列(ハッシュテーブル)を作成
![スクリーンショット 2024-04-02 19.17.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761690/502c306a-9cb9-d375-4321-490f88ec2cd6.png)
8個のデータを持つ配列があります。
Pythonだと以下のように宣言と初期化を行うことができます。
```python
numbers = [7, 4, 3, 8, 1, 5, 2, 6]
```
`random`モジュールを使うことでランダムな配列を作成することもできます。
```python
import random

# random.randint(1, 9)で1〜9の数値を生成、for文で8回実行
numbers = [random.randint(1, 9) for _ in range(8)]
```
## 配列から最小値を見つけて入れ替える
![スクリーンショット 2024-04-02 19.54.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761690/519f53db-bc43-4698-bc92-632d9c74ced2.png)
選択ソートはまず、**最小値**を**確定したデータ**の右隣に入れ替えます。
未ソートの配列だと**確定したデータ**はまだ無いので、一番左を入れ替えます。
画像の場合、`7`と最小値`1`を入れ替えています。

![スクリーンショット 2024-04-02 19.54.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761690/9c4b90e9-b009-ca38-a619-c737db692591.png)
最小値`2`を**確定したデータ**の右隣`4`と入れ替えます。
これを繰り返し、全てのデータが確定したらソート完了です。

![スクリーンショット 2024-04-02 20.03.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761690/9a78b4e2-7514-e48c-4ad0-741d92a020c6.png)

## 選択ソートのプログラム (Python)
```python
def select_sort(data: list[int]) -> list[int]:
    len_data = len(data) # 配列の長さ
    for i in range(len_data): # リストの各位置に対して最小値を見つけ、その位置と交換するため
        min_index = i # 初期値
        for j in range(i + 1, len_data): # iより後ろの要素の中から最小値を見つけるため
            if data[j] < data[min_index]:
                min_index = j
        data[i], data[min_index] = data[min_index], data[i]
```
【生成AIアプリ100チャレンジ】（10）ChatGPT同士で議論させる。異論反論アプリ

# 公開サイト

https://ai.nuevo.jp/chat_interaction/discussion/

# 開発環境

|              |              |
|:------------:|:------------:|
| Server       | lightSail    |
| Language     | Python3.11   |
| Framework    | Django       |
| DB           | sqlite3      |

ローカル環境ではPythonのvenvを使用。エディタはvs codeです。

# 目的
ChatGPT同士で会話させたいってふと思ったことはあるはず。いまはブラウザのChatGPTでもメンションをつけさせて会話させることはできるが、アプリ上でそれをさせてみたくて実装。
ユーザー側が議題を出し、それを人工知能Aが主張。人工知能Bが反論、人工知能Cがまとめて、ユーザーに返す。という仕組み。なかなか奥深い討論をするので、面白い。
ただ、トークンをたくさん消費するのであまりやらないほうがいいかもしれない。MAX_TOKENの設定をしているのだが超えている気がする。

# コード
```python:qiita.rb
    def topic(request):

    OPENAI_API_KEY = os.environ['OPENAI_API_IMAGE_KEY']
    prompt = request.POST.get('message')
    interactions = []

    def discussion(OPENAI_API_KEY, prompt, message):
        client = OpenAI(
            api_key = OPENAI_API_KEY,
        )
        response = client.chat.completions.create(
            model="gpt-4",
            messages=[
                {
                    "role": "system",
                    "content": message
                },
                {
                    "role": "user",
                    "content": prompt
                },
            ],
            temperature=0.3,
            max_tokens=512
        )
        response = response.choices[0].message.content
        response = response.replace("\n", "<br>")
        return response

    message = """日本語で応答してください。あなたは議論に対して、肯定的な意見を主張する役割です。500文字前後で主張してください"""
    casper_response = discussion(OPENAI_API_KEY, prompt, message)
    interactions.append({'input': "議論の課題は" + prompt + "です。", 'response': casper_response})

    message =  """日本語で応答してください。あなたは意見に対して、反論意見を主張する役割です。500文字前後で主張してください"""
    balthazar_response = discussion(OPENAI_API_KEY, casper_response, message)
    interactions.append({'input': casper_response, 'response':  balthazar_response})

    message = """日本語で応答してください。あなたは意見に対して、意見まとめる役割です。500文字前後で、まとめた意見を主張してください"""
    melchior_response = discussion(OPENAI_API_KEY, balthazar_response, message)
    interactions.append({'input': balthazar_response, 'response': melchior_response})

    return JsonResponse({'message': interactions})
```

# アプリ画面
  ![スクリーンショット 2024-04-02 10.53.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398694/3d846874-bf8c-02d3-0bd4-e4a9051af81a.png)

# 感想
思った通りの動きにはなったが、討論している者同士が別人格ではなく、独り言っぽくなっているようにも見える。
APIキーを分けるか、別のモデルに問いかけるか、した方がよかったかもしれない。
分ごとのトークン超過をすぐにしてしまうので、細かい調整まではできなかった。まだまだ改善の余地は残っているが、やろうとした目的は達成できたので、今日はこれでよしとする。
JSDoc @templateの使い方

VSCodeでJavascriptを使うときの型推論の話です。
ほとんど箇条書きですが、簡単なサンプルを併記しているので それほど難しくはないかと思います。

# 関数で使える
関数を実行するときに**実際に渡した引数の型**をベースにして型を推測してくれるようになります。
templateの記述が無いときは、any型になります。
```javascript
/**
 * @template T
 * @param {T} value 
 */
function foo (value) {
  return value;
}

let a = foo(1); // number
let b = foo(''); // string
```


# クラスでも使える
関数だけではなく、クラスでも使えます。
```javascript
/**
 * @template T
 */
class Bar {
  /**
   * @param {T} value
   */
  constructor (value) {
    this.value = value;
  }

  baz () {
    return this.value;
  }
}

let bar = new Bar(0);
let foo = bar.baz(); // number
```

型注釈をつけるときは\<\>で指定できます。
```javascript
/** @type {Bar<number>} */
let bar = new Bar(0);
```
Javascript標準の Array や Set はこれに該当します。
例: `Array<string>`, `Set<number>`

# 2つ以上宣言できる
Javascript標準の Object や Map はこのパターンです。
例: `Object<string, any>`, `Map<number, boolean>`
```javascript
/**
 * @template T
 * @template U
 */
class Dictionary {
  /**
   * @param {T} key
   * @param {U} value
   */
  set (key, value) {}
}

/** @type {Dictionary<string, number>} */
let dic = new Dictionary();
dic.set('width', 100);
```


# ベースとなる型を指定できる
template と T の間に型注釈を入れれば、型を限定することができます。
```javascript
/**
 * @template {string} T
 * @param {T} value
 */
function hoge (value) {
  return value;
}

let valA = '';
let hogeA = hoge(valA); // string

let valB = 0;
let hogeB = hoge(valB); // これは警告される

// ベースよりも限定的な型は問題なく使える
/** @type {'A' | 'B'} */
let valC;
let hogeC = hoge(valC); // "A" | "B"
```

# 発展的な使い方
## その1 返り値の型補完
```javascript
/**
 * @template T
 * @template U
 */
class Dictionary {
  /**
   * @returns {Array<T>}
   */
  keys () { return []; }

  /**
   * @returns {Array<U>}
   */
  values () { return []; }
}

/** @type {Dictionary<string, number>} */
let dic = new Dictionary();
let keys = dic.keys(); // string[]
let values = dic.values(); // number[]
```

## その2 keyofとの併用
引数のプロパティをkeyofで抽出しています。
```javascript
/**
 * @template {Object<string, any>} T
 */
class ValueReader {
  /**
   * @param {T} obj
   */
  constructor (obj) {
    this.obj = obj;
  }

  /**
   * @param {keyof T} key
   */
  read (key) {
    return this.obj[key];
  }
}

let reader = new ValueReader({ id: '01', name: 'white' });
// readの引数が "id" | "name" になる
let id = reader.read('id');
```

# 雑感など
keyof や extends と組み合わせれば、インテリセンスをよりリッチにすることができます。
使いどころは探せばあるので、試してみてください。
[Atcoder備忘録記事]AtCoder Beginner Contest 344 C問題

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/ac3f7809-5319-e363-0295-bd9ebf1e7f8b.png)

# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/873007f7-7cde-1353-2ef9-97bea425cbfd.png)

# 解法
全探索で解くことが可能ですが、普通にやると実行制限時間を超過してしまうため、少し工夫が必要です。
A+B+Cの全パターンを検索するのではなくA+Bの全パターンを検索した後にそれぞれのCをたしてXに一致するかどうかを探索します。

```
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
using namespace std;

int main() {
    int N, M, L, Q;
    cin >> N;
    vector<int> A(N);
    for(int i = 0; i < N; i++) cin >> A[i];
    
    cin >> M;
    vector<int> B(M);
    for(int i = 0; i < M; i++) cin >> B[i];
    
    cin >> L;
    vector<int> C(L);
    for(int i = 0; i < L; i++) cin >> C[i];
    
    cin >> Q;
    vector<int> X(Q);
    for(int i = 0; i < Q; i++) cin >> X[i];
    
    //回答の配列。全てをNoで埋めておく。
    vector<string> result(Q, "No");

    //A+BをABsumsに格納する。unorderd_setを用いて被りをなくす。
    unordered_set<int> ABsums;
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < M; j++) {
            ABsums.insert(A[i] + B[j]);
        }
    }

    //ABsumsにCをたしてXに一致するかの確認
    for(int q = 0; q < Q; q++) {
        for(int l = 0; l < L; l++) {
            if(ABsums.find(X[q] - C[l]) != ABsums.end()) {
                result[q] = "Yes";
                break; 
            }
        }
    }

    for(int i = 0; i < Q; i++) {
        cout << result[i] << endl;
    }
}
```
CSSでアニメーションをつける

フロントエンド強化期間中のため、今回はフロントエンドのアニメーションをどのようにしてつけるのかをまとめました。

## フロントエンドのアニメーションとは？

>#### ChatGPT
>CSSアニメーションは、ウェブページの要素に滑らかな動きや段階的なスタイル変更を適用する手法です。@keyframesとCSSプロパティを組み合わせることで、時間の経過と共に要素がどのように変化するか定義します。これにより、ユーザーの注意を引く効果や、インターフェイスの直感性向上に貢献し、ウェブサイトのユーザビリティと魅力を高めます。

つまり、CSSアニメーションは、ウェブサイト上で要素を動かしたり、見た目を時間経過と共に変化させたりする技術です。また実装では@keyframesで動きの段階を定義し、CSSプロパティで適用することで簡単に実装できます。

## 実装の手順
実装の方法はレスポンシブの設定に少し似ているかもしれません。


まず最初に下記のようなHTMLコードを用意します。
```
<h1 class="slidein">アニメーションを適用するテキスト</h1>
```
次にCSSファイルに、下記のような設定を行います。
```
h1 {
  animation: fadeIn 1s ease-in-out forwards;
}

@keyframes fadeIn {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
```
### やっていること（「h1 {...」の内容）：
・animation: このプロパティは、要素に対してアニメーションを適用します。

・fadeIn: これはアニメーションの名前です。この場合、アニメーションのキーフレームが@keyframes fadeIn { ... }として定義されている必要があります。

・1s: アニメーションの持続時間を示しており、この場合は1秒間です。

・cubic-bezier(0.25, 1, 0.5, 1): アニメーションのタイミング関数を定義しています。これはアニメーションの速度曲線を指定し、アニメーションがどのように進行するか（始まりが遅く終わりが速いなど）をコントロールします。ここで指定されたcubic-bezier関数は、アニメーションの加速と減速のパターンをカスタマイズします。

・forwards: アニメーション終了時のスタイルを、アニメーションの最後のキーフレームの値に保持するよう指定しています。つまり、アニメーションが完了した後も、要素はアニメーションの最終状態を維持します。

### やっていること（「@keyframes fadeIn {...」の内容）：
・@keyframes slideIn：　CSSアニメーションで要素をスライドインさせる動きを定義しています。

・開始時（0%）: アニメーションが始まると、要素の透明度が0です。これは要素が完全に透明で、見えない状態であることを意味します。

・終了時（100%）: アニメーションが終わるまでに、要素の透明度が1に変化します。これは要素が完全に不透明になり、完全に見える状態になることを意味します。

## まとめ
アニメーションは最初javascriptで設定するイメージだったけど、CSSで簡単に設定できることがわかりました。またアニメーションの設定ではテンプレなどもたくさんネットにあるため、最初はこれらを使用することで試していくのがいいと思います。

私は「[【コピペでOK】サイトに動きをつけるCSSアニメーションまとめ（デモ・サンプルコード付き）](https://b-risk.jp/blog/2021/01/anim-reference/ "CSSアニメーションについての記事")」のサイトのテンプレを使用しながら学習しました。

## 参考文献：
[【コピペでOK】サイトに動きをつけるCSSアニメーションまとめ（デモ・サンプルコード付き）](https://b-risk.jp/blog/2021/01/anim-reference/ "CSSアニメーションについての記事")
Pythonで「日付や時刻の値を指定した書式を使って文字列として取得する」の動作を確認してみた

# 概要

Pythonで「日付や時刻の値を指定した書式を使って文字列として取得する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/python/date/index6.html

# 実装

以下のファイルを作成しました。
```sample.py
import datetime
tokyo_tz = datetime.timezone(datetime.timedelta(hours=9))
dt = datetime.datetime.now(tokyo_tz)
print(dt.strftime("%y/%m/%d %A %H:%M:%S"))
print(dt.strftime("%B %d,%Y %p %I:%M:%S%z"))
print(dt.strftime("%c"))

import datetime
import locale
locale.setlocale(locale.LC_ALL, 'ja_JP.UTF-8')
tokyo_tz = datetime.timezone(datetime.timedelta(hours=9))
dt = datetime.datetime.now(tokyo_tz)
print(dt.strftime("%y/%m/%d %A %H:%M:%S"))
print(dt.strftime("%B %d,%Y %p %I:%M:%S%z"))
print(dt.strftime("%c"))
```

以下のコマンドを実行しました。
```
$ python3 sample.py 
24/04/02 Tuesday 19:09:12
April 02,2024 PM 07:09:12+0900
Tue Apr  2 19:09:12 2024
24/04/02 火曜日 19:09:12
4月 02,2024 午後 07:09:12+0900
2024年04月02日 19時09分12秒
```

# まとめ

何かの役に立てばと。
[Atcoder備忘録記事]AtCoder Beginner Contest 344 B問題

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/b2fa215b-cd61-976d-7c25-c8a34be292ef.png)
# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/235ad3b8-6307-5016-278f-ccfceab9ebfc.png)
# 解法
Nは与えられないので制約にある通り初期値を100にします。
0が$A_i \neq 0 \quad (1 \leq i \leq N - 1)$と$A_N = 0$の制約があるので0をみつけたらループを中断し、その時点での$i$のインデックスを保存します。
後は保存したインデックスから逆向きにforループを回して終了です。
```
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;

int main() {
    vector<int> N(100);
    int punctuation = 0;
    for(int i = 0; i <= 100; i++){
        cin >> N.at(i);
        if(N.at(i) == 0){
            punctuation  = i;
            i = 100;
        }
    }
    for(int i = punctuation; i >= 0; i--){
        cout << N.at(i) << endl;
    }
}
```
AWS の EC2 の パブリックIP を固定する (Elastic IP使用)

AWS の EC2 のパブリックIPを固定するときのメモ
Elastic IP 使って簡単に出来そう
画像は 2024/04/02 現在です
ざっくりの流れは
- EC2インスタンス作成
- Elastic IP 割り当て
- Elascic IP から EC2インスタンスに関連付け

# EC2 インスタンス作成 (インスタンス起動)
とくにこだわりなく適当な設定で作っていく
一旦はデフォルト設定で作成、SSH 接続すること考えるとキーペアだけ設定する
AMI, インスタンスタイプ, ネットワーク設定はこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/d75c58cf-def4-0843-1a08-2b5651d5b860.png)

# Elastic IP 作成・割り当て
こちらは見慣れない画面なので手順メモ

1. Elastic IP アドレスを割り当て（作成）
    EC2 の左メニューにある [ネットワーク＆セキュリティ] - [Elastic IP] - [Elastic IP アドレスを割り当てる]

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/c2a84318-fe0d-d5fd-119e-99c324a595a2.png)

2. Elastic IP アドレスの設定
    デフォルトのまま [割り当て]

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/b88565db-0781-20d8-e821-203f944b14a8.png)

3. 割り当てた Elastic IP が表示、詳細ひらく
    新しく作った Elastic IP が表示されるので割り当てたIPのとこのリンクをクリック

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/ac467a4f-1853-6c31-9063-7c6dba6dafe1.png)

4. Elastic IP アドレスの関連付け

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/9a94685c-54bf-7b64-418c-685b8abc94d8.png)

5. 関連付けるインスタンスを選択して関連付け

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/30e0e697-1e8e-39e5-f506-d426ff072e90.png)

5. 関連付けしたインスタンスIDが表示、確認でインスタンスの詳細に行く
    Elastic IP の詳細にあるインスタンスIDのリンクをクリック

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/6698afd4-26ee-14eb-ab87-e3561dc05b23.png)

5. EC2 でパブリックIPv4アドレス確認、関連付けた Elastic IP になってる

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/2ed38967-2965-9d4b-b133-1367846b918d.png)
[初心者]Dockerの基本的な流れメモ

## はじめに
Dockerをインストールしている前提です。

# 基本的な流れ
### Docekrfileの作成
自分のローカルディレクトリに"Dockerfile"（拡張子なし）という名前でファイルを作成します。
このDockerfileには、ビルドするイメージのベースとなるOSや、インストールするプログラム、コピーするファイルなど、イメージに含めたい設定を記述します。

他人のDockerfileを取得することも可能です。(DockerHubなど)


### Dockerfileからイメージをビルド
ここで、-t はビルドされるイメージに名前を付けるオプションです。.はDockerfileがあるディレクトリを指しており、Dockerはこのディレクトリをコンテキストとして使用します。
このステップにより、アプリケーションとその依存関係を含むイメージが作成されます。これにより、どの環境でも同じ条件でアプリケーションを実行できるようになります。
```
docker build -t my-custom-python .
```



### イメージをrun
このコマンドは、my-custom-pythonという名前のイメージから新しいコンテナを起動し、そのコンテナ内でシェルを開始します。
-itオプションは、インタラクティブモードでターミナル入力を可能にするためのものです。これにより、コンテナ内でコマンドを実行したり、アプリケーションを手動で操作したりすることができます。
コンテナは、定義された環境でアプリケーションを隔離して実行するための軽量な実行単位です。
```
sudo docker run -it --name predict-server predict-server /bin/sh
```

これでDockerの中でCLI操作していきます。(Dockerファイルに実行まで書かれているならしなくても良くなる。任意)

# Dockerfileの書き方

```
#途中までpython3.11の公式Dokerfileです。

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#

FROM alpine:3.18

# ensure local python is preferred over distribution python
ENV PATH /usr/local/bin:$PATH


・
・
・
・


# 以下編集
# 必要なツールのインストール
RUN apk add --no-cache nano curl git bash

# 作業ディレクトリを設定
WORKDIR /app

# アプリケーションのファイルをコンテナにコピー
COPY . /app

#requirements.txtなら以下のように
RUN pip install -r app/requirements.txt
```


### メモ
> DockerfileにOSいれても、コンテナではOS動かさないんじゃないの？

DockerfileでOSを指定すると、実際にはフルOSをコンテナ内で動かすわけではありません。Dockerイメージは、カーネルを含まない軽量なOSレイヤーを提供します。これは、ホストOSのカーネル機能を利用して、必要なユーザースペースツール、ライブラリ、アプリケーションを動かすためのものです。
らしい、、、

### コマンド一覧
docker container ls
docker run -v .:/app  -dit pre-server0410
docker exec -it 764ce1de1840  bash
[Atcoder備忘録記事]AtCoder Beginner Contest 344 A問題

[AtCoder Beginner Contest 344 A問題](https://atcoder.jp/contests/abc344/tasks/abc344_a "Atcoder")
こちらの問題を解いたので、備忘録用の記事です。

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/f6026dc7-3273-645e-4c8a-5999aee7cf19.png)
# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/3808d8ed-fd88-da0c-a1c9-7f473d4049ca.png)
# 解法
この問題は"|"で囲まれている部分を削除して出力するというものです。
そのままSをforループで回すことでとけます。
```
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <cmath> 
#include <algorithm>
using namespace std;

int main() {
    string S;
    string ans = "";
    bool flag = false;
    cin >> S;
    for(int i = 0; i < S.length(); i++){
        if(S[i] == '|') flag = !flag;
        else if(!flag) ans += S[i];
    }

    cout << ans << endl;
    return 0;
}

```
Laravel 11 で id を uuid にしようと思ったらエラーになった話

# 環境
- PHP8
- MySQL8.0
- Laravel11

# やりたかったこと

Laravel 11 で作ったプロジェクトにて、（既にいくつか migration を実行した状態なんだけど） userテーブルの id に uuid を使いたかった。

# エラー内容

```
SQLSTATE[HY000]: General error: 1822 Failed to add the foreign key constraint. Missing index for constraint 'chirps_user_id_foreign' in the referenced table 'users' (Connection: mysql, SQL: alter table `chirps` add constraint `chirps_user_id_foreign` foreign key (`user_id`) references `users` (`id`) on delete cascade)

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:813
    809▕                     $this->getName(), $query, $this->prepareBindings($bindings), $e
    810▕                 );
    811▕             }
    812▕
  ➜ 813▕             throw new QueryException(
    814▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
    815▕             );
    816▕         }
    817▕     }

      +9 vendor frames

  10  database/migrations/2024_03_15_065601_create_chirps_table.php:14
      Illuminate\Support\Facades\Facade::__callStatic()
      +26 vendor frames

  37  artisan:13
      Illuminate\Foundation\Application::handleCommand()
```


users テーブルに constraint 用のキーがないため、foreign key constraint を追加できないと怒られた。
      

↓ users テーブルの migration はこんな感じだった。
```php
Schema::create('users', function (Blueprint $table) {
    $table->uuid('id');
    $table->string('name');
    ...
```

外部キーとして users テーブルの主キーを取りたいテーブルはこちら↓

```php
Schema::create('chirps', function (Blueprint $table) {
    $table->uuid('id');
    $table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
    ...
```


# 原因
users テーブルの id を uuid に変更した際、主キー指定していなかった。

```php
Schema::create('chirps', function (Blueprint $table) {
    $table->uuid('id')->primary();  // ->primary()を追加
    $table->foreignUuid('user_id')->constrained()->cascadeOnDelete();
    ...
```

↑こうしたらいけました。

```php
$table->id();
```

↑これは勝手に主キーになるが、


```php
$table->uuid()->primary();
```

↑は主キーだよ！という　```->primary()``` が必要のようでした。



# おまけ

users テーブルの主キーを uuid にしたら、 sessions テーブルの外部キーも ```->foreignUuid``` にしないとログインしてもうまく動かなかったよ！

```php
Schema::create('sessions', function (Blueprint $table) {
    $table->string('id')->primary();
    $table->foreignUuid('user_id')->nullable()->index();
    ...
```

結局色々考えて、UUID じゃなくて ULID を使うようにしたんですけどね……。
「日本人全員が佐藤さんになる」の雑すぎる計算

# エイプリールフールネタに突っ込むのもあれなのですが、計算式が想定できたので共有

なんと「２５３１年には全員佐藤さん」というニュースがエイプリールフールに流れた。論文？プレゼン？の元ネタがみつからないが、ニュースサイトにのった記事からどういう計算ロジックなのか想像してみた。

https://mainichi.jp/articles/20240331/k00/00m/040/076000c


## 前提

* 佐藤さんは全体の1.53%を占める
* 2022年から2023年の伸び率は1.0083であった

## 実装

人類の偉大なる発明の一つ「複利」でこの伸びを計算する。

```python
satou_rate=1.53
satou_growth=1.0083

def satou(year):
    return satou_rate * satou_growth ** (year-2022)
```

## 値の計算

2022年から2550年までの計算を行う。

```python
import numpy as np
x = np.arange(2022, 2550)
y = satou(x)
```

## グラフ化

```python
import matplotlib.pyplot as plt

plt.plot(x, y)
plt.hlines(100, xmin=2022, xmax=2600, color="red", linestyles='dashed')
plt.hlines(50, xmin=2022, xmax=2600, color="red", linestyles='dashed')
plt.vlines(2446, 0, 100, color="red", linestyles='dashed')
plt.vlines(2531, 0, 100, color="red", linestyles='dashed')
plt.show()
```

## 出力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184998/57018998-77a8-1a5b-9b84-ee998878fbdc.png)

2446年が50%で、2531が100%になるグラフが作れた。
ついでに、2550年には120%になる、波動砲の充填率もびっくりな数字がつくれた。

## 夫婦別姓だとどうなるか？

夫婦別姓であっても、通常子供は１つの姓しかとらないから理論上は関係がない。しかし、ここでは別姓をとると、子供２人生まれ、それぞれ別の姓を取るという前提にたてば姓を残すことができる。

## 新計算式

夫婦同姓を望むのは39.3%。佐藤さんが増えるのは夫婦同姓のときだけだから（＝２人の子供が全員佐藤さんになる場合だけだから）、増加率は
$$
1 + 0.0083 * 0.393 = 1.003262
$$
になる

## 夫婦別姓のときの計算

```python
betusei_growth = 1 + (satou_growth-1)*0.393  # 別姓のときだけ佐藤さんは増える

def betusei_satou(year):
    return satou_rate * betusei_growth ** (year-2022)    
```

### グラフ化

```python
import numpy as np
x = np.arange(2022, 3350)
y = betusei_satou(x)

plt.plot(x, y)
plt.hlines(100, xmin=2022, xmax=3350, color="red", linestyles='dashed')
#plt.hlines(50, xmin=2022, xmax=2600, color="red", linestyles='dashed')
plt.vlines(2531, 0, 100, color="red", linestyles='dashed')
plt.vlines(3310, 0, 100, color="red", linestyles='dashed')
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184998/cc122e3b-ef21-6faf-2018-30833c974eb6.png)

3310年に100％になるグラフが作れた

# 宿題

## 詐欺師の方へ

* 詐欺を行う場合は、数字を使うとうまくいくという事例としてよく学ぼう


## データサイエンティストへ

* 一体、どこで道を踏み外したのかロジックがおかしいところを指摘してみよう
* 新しい姓がつくられないので次第に姓が減ること自体は明らかだが、具体的にいつ佐藤さんがいっぱいになるのか計算してみよう
* エイプリールフールとはいえ、適当に作ったデータが力を持ってしまうことについて倫理的面から同僚と議論してみよう

## その他

### 既存研究
http://mzsmlab.blog.fc2.com/blog-entry-639.html
によれば、苗字というのはどうしてもへっていくものらしく、１８７４年にガルトン＝ワトソン過程として数学的モデルが存在するらしい。これを応用して計算してみるのもいいだろう。

### マジもんだったらしい

４月１日ものかとおもえば、３月２０日のマジモンだったらしい。

https://think-name.jp/assets/pdf/Sato_estimation_yoshida_hiroshi.pdf

離婚したケースなど、もうすこし詳細化してあるけれど考え方は想定どおり。うーむ。想定が当たったのがいいのか悪いのか。
LCA(最小共通祖先)で根が変わっても、最小共通祖先と2頂点との距離の和は同じという話

# 疑問
https://atcoder.jp/contests/abc014/tasks/abc014_4
この問題を解いていて、根を頂点1にするとACできたのですが、なぜこれでACできたのかと疑問に思いました。根の頂点をnなど他の頂点にしてもACできていて、なぜだろうと疑問に思って謎がわかったので記事にします。

# 根が変わるとどうなるのか
ペイントで描いてみました。aとbに対して新しく辺を結ぶとして、根を2パターン書いて、その場合のaとbの最小共通祖先と、その最小共通祖先との距離を書いています。
![最小共通祖先 根が違くても距離の和は同じ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3701202/927e351e-0d49-d759-6696-b85d4694df08.png)

見てみると、根が変わると最小共通祖先の頂点は変わってしまいますが、aと最小共通祖先の距離、bと最小共通祖先の距離、この二つの和はどちらも同じであることがわかります。また、当然なことではありますが、根が変わってもaとbの二頂点間の距離も変わりません。

# まとめ
変わるもの →根と最小共通祖先の頂点
変わらないもの →最小共通祖先と二頂点との距離を和。二頂点の距離
主客店頭みたいなイメージをするなら、「二頂点間のパスで通るどこか一頂点を最小共通祖先とする」みたいな感じになるかもしれません。

# おわりに
上級者の方には当然なことかもしれませんが、LCAを初めて勉強していて疑問に思ったことを記事にしてみました。ここまで見ていただきありがとうございます。
【Flutter】BottomNavigationBarで４つ以上のItemを使用した場合の注意点

## 【Flutter】BottomNavigationBarで４つ以上のItemを使用した場合の注意点

これただのメモ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2850787/3f6b8302-5d57-563b-7253-b8e98d4dc777.png)

```dart
final bar = BottomNavigationBar(
      items: items, // アイテムたち
      backgroundColor: Colors.green, // バーの色
      selectedItemColor: Colors.black, // 選ばれたアイテムの色
      unselectedItemColor: Colors.grey, // 選ばれていないアイテムの色
      currentIndex: index, // インデックス
      onTap: (index) {
        // タップされたとき インデックスを変更する
        ref.read(indexProvider.notifier).state = index;
      },
    );
```

上記のように記述してもバーの色が反映されないから困っていた。

答えは既出されていました。↓

https://nobushiueshi.com/flutterbottomnavigationbar%E3%81%A74%E3%81%A4%E4%BB%A5%E4%B8%8Abottomnavigationbaritem%E3%81%8C%E3%81%82%E3%82%8B%E3%81%A8%E7%99%BD%E3%81%8F%E6%B6%88%E3%81%88%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86/

上記を参考に修正したらバーの色が反映されてニッコリ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2850787/4aca91cb-0eec-431a-bd50-aea81dcff3cf.png)

まぁ答えは以下の文を`BottomNavigationBar`に追加するだけでした。
```dart
type: BottomNavigationBarType.fixed,
```

`BottomNavigationBar`はItem数が３つと４つで`Type`のデフォルト値が変わるっぽい。

そしてバーの色のセンスが終わっている。
Azure App ServiceへのZipデプロイが失敗する場合の対処方法

# 概要
検証作業で、App ServiceにVisual StudioからWebアプリを
Zipデプロイがなぜか失敗する現象が発生したんですが・・・
その時実施した回避策についての備忘録。

# 環境
・Visual Studio 2019
・ASP .NET Core

# 現象
Visual Studio側からデプロイしたら、こんなエラーが。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/8c52ce97-9306-1255-dc11-e953c79d0652.png)

Visual Studioの出力Windowを確認すると、
```
C:\Program Files\dotnet\sdk\5.0.408\Sdks\Microsoft.NET.Sdk.Publish\targets\PublishTargets\Microsoft.NET.Sdk.Publish.ZipDeploy.targets(55,5): エラー : https://XXXXXXXX.scm.azurewebsites.net/api/zipdeploy 経由で ZIP ファイルを公開しようとしましたが、HTTP 状態コード GatewayTimeout で失敗しました。
```
というエラー表示でした。

# 試したこと
Azure Portalにログインして、該当のApp Serviceの構成を開きます。
全般設定の中にある以下の項目を、一度どちらもオフにして、保存します。
その後、再度オンにして、保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146789/b81e85e4-ac62-1d73-d315-af1ae983cf2d.png)

この対応を行って、先ほど実施した通りVisual Studioでデプロイすると・・・
うまくいきました。

App Service側のアップデートの際に、Basic認証周りのデフォルト設定が
何か悪さしてるんじゃなかろうかと思っています・・・が、真相は闇の中・・・。
MacでSSTP方式のVPNに接続する

あるクライアントから開発業務で利用するVPNサーバ（？）が構築・提供されました。
そのVPNサーバは、SSTPで接続する必要があるのですが、MacはSSTPを利用できません。

いろいろ調べてみると [sstp-client](https://sstp-client.sourceforge.net/) なるものを発見し、MacからSSTPでVPN接続ができましたので、備忘録を残します。

# 環境
```
% sw_vers  
ProductName:		macOS
ProductVersion:		14.4
BuildVersion:		23E214
% brew --version
Homebrew 4.2.16
```

# 1. sstp-clientをインストール

Homebrewでsstp-clientをインストールします。

```
% brew install sstp-client
```

インストール後、`sstpc`コマンドが利用できるようになります。
今回は、v1.0.19がインストールされました。

```
% sstpc --version
sstp-client version 1.0.19
```

# 2. sstp-clientでVPN接続

次のコマンドを実行することで、VPN接続ができました。

```
% sudo sstpc VPNサーバアドレス --user ユーザ名 --password パスワード --log-stderr --cert-warn usepeerdns require-mschap-v2 noauth noipdefault defaultroute refuse-eap noccp --tls-ext
```

# 謝辞
参考にさせていただきました。ありがとうございます。

https://qiita.com/data_psyence/items/95844c43995fe6f8a535

https://qiita.com/atsu1125/items/82d02e6d8dd5986d8ea2
Git&GitHubの学習②

## Git - GitHub間でのファイルの更新・追加の手順

基本的には
1. ブランチの作成と切り替え
2. ファイルの更新・追加
3. git addでステージングエリアへの配置
4. git commitで変更をコミット
5. git pushで変更をリモートリポジトリに反映
6. GitHubでプルリクエストの作成
7. レビューを受ける
8. 問題なければマージ
という流れになります。

今回は1~5までの解説になります。

まずはブランチの作成
```git
git branch "ブランチ名"
```

作成したブランチへの切り替え
```git
git checkout "ブランチ名"
```

エディタ等を使用してファイルの更新・追加を行います
そして
```git
git status
```
で更新されているか確認

```git
git add -A
```
もしくは
```git
git add --all
```
でステージングエリアに配置

```git
git status
```
でステージングエリアに配置されているか確認

```git
git commit -m "メッセージ"
```
で変更をコミット

```git
git push origin "ブランチ名"
```
で変更をリモートリポジトリにプッシュ

という手順になります。

## git pullとgit merge
逆にリモートのｍainブランチから最新情報を取得して
ローカルリポジトリのmainブランチを最新状態にする場合はgit pullを使用します

mainブランチへの変更
```git
git checkout main
```

そしてgit pull
```git
git pull origin main
```

その後、mainブランチから自分のブランチに最新情報を取り込む場合は
git mergeを使用します

ブランチの変更
```git
git checkout "ブランチ名"
```

そしてgit merge
```git
git merge main
```
これで完了です。

今回は以上になります。
可変変数のおさらい

業務でとあるシステムの改修作業をしていたら$$がついた関数を見つけて頭の中が？になったので自分の中で整理してみる。

## $$は可変変数と言うらしい
恥ずかしながら今まで実務で可変変数を定義しているコードを見たことがなかったのでシステムを作った人が打ち間違えたのかと思っていました（ほんとに恥ずかしい）

## 可変変数ってなに？
変数の名前を他の値で作ることができる

### 簡単な使い方
まず変数を一つ用意
```
$hoge = "qiita"
```
次に$hogeの値を使って新しい変数名を作る
**（これが可変変数）**
```
$$hoge = "PHP"
```

既にhogeという変数には"qiita"という値が入っています。
つまり
```
$$hoge = $qiita 
```

と言うことになります。
$qiitaという変数が作られ、その中に"PHP"という値が格納されていることになります。

### 出力
```
echo $qiita; // 出力: PHP
```

## メリットとデメリットは
**コードが簡潔になる**
似たような変数を定義しなくてよいのでコードを書く量が減らせます。

## デメリット
**コードが読みにくくなる**
定義されているコードが明確になっていないため解析に時間がかかるみたいです

**デバックが難しい**
上記と関連してますが、変数の場所を探すのに時間がかかり、それによりデバックも難しくなるみたいです

まだ私は可変変数に慣れていないので使いどころに迷いそうなので、徐々に慣らすところから始めたいです。
なにもしてないのにOpenAIが壊れた

OpenAIの、とくにChatGPTおよびGPT系APIが動かなくなったときの覚え書きです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/4b2347e2-88c7-f96e-a9d6-72e65fd9dc5d.png)

ほんとに障害かな？
というわけで、まずは、OpenAIのStatusを確認します。

### 公式のOpenAI Statusを確認する

https://status.openai.com/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/0368bb55-8de1-e4e3-4ce9-51ea78eeae90.png)

バーが黄色だったり、赤くなっている場合はOpenAI側の問題です。
おとなしく復旧まで待ちましょう。

### SNS(Xなど）を確認する

SNSでほかの人にも同様の障害が起こっているか確認するのも有用です。

# ChatGPT（Webサービス）の場合

https://chat.openai.com/

### OpenAIのせい
なにもしてないのにChatGPTが壊れた場合、OpenAIの障害でないか確認します。
とくにWebのChatGPTは、わりと反応が鈍くなったり、つながらなかったりすることがあります。

### キャッシュのせい
とくに障害でもなく、ほかの人が動いていそうな場合、こちら側の問題である可能性が高いです。
ブラウザキャッシュを消して再ログインすると解消されることがありました。
どうも、内部的な認証方式の変更のせいだったようです。

### 使用の上限のせい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/b262e31f-196b-3185-4efc-2859104233bc.png)

有料のChatGPT Plusで使えるGPT-4は呼び出し制限があります。
これにひっかかると、しばらくGPT-4が使えなくなります。

GPT-3.5を使用するか、制限が解除されるまで待ちましょう。
解消される時間は予告されますが、大体１～2時間くらいといったところでしょうか。

# APIの場合

なにもしてないのにGPTが壊れてしまった。
もとい、GPTのAPIが動かなくなってしまった。

ChatGPTよりは安定していますが、ないわけではないです。

OpenAIのAPIはChatGPT Plusとは別物で、従量課金制です。
最初のお試しクレジットを使い切った後は、支払いをしなくては稼働しません。

## Statusを見る

こちらについてもまずOpenAI自体のエラーではないか、Statusを参照します。

## Playgroundで試す

APIが動いているかどうかは、Playgroundから試すとよいです。

https://platform.openai.com/playground

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/87aae022-e7a0-982f-83a6-3a184568f648.png)

## Bilingを確認する

支払情報が登録されているかどうか確認します。
最近前払いチャージ制になりました（後述）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/6d328ffb-312e-0467-53c9-634350053912.png)

### Usage limit
Usage limitはOpenAI側が「最大これくらいまで使っていいよ」と言っている量です。
課金状況によって使っているとだんだん引き上げてもらえます。
Tier1 というのが現在の使用量のランクです。

### Set a monthly budgetについて
Set a monthly budgetは自分で設定する上限です。
自分で設定した量に達すると、それ以上使えなくなります。

Set an email notification thresholdはその値以上になるとメールが飛んできます。
ストッパーがかかっていると、設定した量にすると通知しか来ないサービスよりは安心ですね。

## 2024/3/25　APIは支払方法が変わった（前払いのチャージ式になった）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/da566e5b-4651-681e-4cf3-5309e01becf8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/cae2ecd7-330d-371e-6d7f-051fd924848f.png)

全然使ってないのに、急にreached your usage limitが表示されるようになりました。
月を跨いだら解消されるだろう、と思っていたのですが待てども待てども解消されず……。

よく見てみたら、課金方式が変更されたせいでした。

以前までは使った分だけ月末に請求が来ていたのですが、先にチャージしておかないと使えなくなってしました。
メールを見ていなかった。もーっ。

Bilingから支払情報を設定し、必要に応じてオートチャージを備えておけばOKです。

## 参考

https://note.com/minotchi/n/ncc098a7e2baf