OmniAICreator/Qiita-1.07M huggingface dataset:
Docker 学習5日目

Dockerの学習を始めて5日目。
まだ全体の流れがイメージできていない、メモのような内容です。
間違っているところがあるかもしれませんし、
読みづらい記事になっているかと思います。。。
理解が深まり次第、その都度、更新・訂正します！
(間違いのご指摘や、アドバイスいただければとても嬉しいです！)


# 《目的》
1. Dockerの基本を理解　
1. docker-composeの理解　←今はここ
1. コンテナオーケストレーションツール（AWS ECS）の理解

### 2-4 depends on コマンド
前回までは、テストステージとリリースステージのコンテナを起動しました。まだ、データベースの設定ができていないので今回はここから。
#### データベース設定追加のyamlファイル
yamlファイルに追加したところは下記の通り。
![スクリーンショット 2024-01-29 9.36.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/8dce4784-ceb8-2d82-629c-0e5b4fcc2c27.png)
主な押さえておきたいポイント
* データベースのは`masql:5.7`を、dockerhubからpullしてくる
* ヘルスチェックは3秒間隔で10回まで再試行を行う
* ヘルスチェックの際に必要な情報は`enviroment`に記載している変数を参照する
* これらのヘルスチェックが通ったら`depends on`で**先にデータベースを立ち上げる**
* 設定はtodobackend.settings_releaseを参照する
#### 上記に対して行うコマンド
```docker
docker-compose up release
```
これらの設定を読みこんでアプリケーションがコンテナで起動する。


### 2-5 extendコマンド
ベースとなるコンテナを定義して、そのコンテナをカスタマイズ(拡張)して別のコンテナを立ち上げる。
#### 2つのコンテナ追加のyamlファイル

![スクリーンショット 2024-01-29 10.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/00e56312-0586-4b8a-6646-36d354919d03.png)
`release`コンテナをベースにして、`app`と`migrate`の2つのコンテナを`extends`コマンドで作成。
#### 上記に対して行うコマンド
```docker
docker-compose up migrate

docker-compose up app
```
### 2-6 volumeマウント
コンテナの特定の領域と、Dockerの特定の管理領域を紐つけることをvolumeマウントという。
(他にも、ディレクトリまたはファイルと紐つける方法のbindマウント、メモリと紐つける方法のtmpシステムファイルマウントがある。)

今回は、
Volumeを作成して、`app`コンテナにそのVolumeをマウントして、そのVolume内に静的コンテンツ(Djangoのスタイルシート)を集約して、Djangoがアクセスできるようにする。
Set-CookieのDomainの先頭のドットは無視される

Set-CookieのDomainの先頭のドットは無視されるということを、Railsのアップデートに関する調査をしている際に知りました。

↓ Cookieを確認すると表示される、この `.qiita.com` の先頭のドットです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/7174c8ea-f8d0-3203-ad25-216365c09c4b.png)

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value

> Contrary to earlier specifications, leading dots in domain names (`.example.com`) are ignored.

## 背景等について

RailsのPull Requestに詳しく説明されていました。

https://github.com/rails/rails/pull/48036

> This leading dot was required in cookies based on [RFC 2965](https://www.rfc-editor.org/rfc/rfc2965#section-3.2) (October 2000), but [RFC 6265](https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1) (April 2011) changed that behaviour

とあるように、仕様が変わったようです。

現在では、レスポンスヘッダの `Set-Cookie`で`domain`の先頭にドットがついていても無視されるので、動作の確認をしてみたところ、

- 元々ドット**あり**でCookieをセットしていたとしても、ドット**なし**のSet-Cookieで値を上書きできる
- 元々ドット**なし**でCookieをセットしていたとしても、ドット**あり**のSet-Cookieで値を上書きできる

という挙動をするので、Set-Cookieのdomainの値を変更しても挙動に影響がなさそうでした。

ただ、Chrome等の開発者ツールでCookieの値を確認すると (冒頭の画像) 先頭にドットが表示されたままでした。

## 補足: Set-Cookieの `domain` について

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value

MDNから引用しますが、

> Defines the host to which the cookie will be sent.

Domainはどのホストにcookieを送信するかを定義します。

> Setting the domain will make the cookie available to it, as well as to all its subdomains.
> If omitted, this attribute defaults to the host of the current document URL, not including subdomains.

Domainを指定しない場合、現在のURLのホストのみにCookieを送信しますが、
Domainを指定する場合、そのサブドメインにも送信されるようになります。

そのため、サブドメインに送信する必要がないのであれば、`domain`を指定しないほうが条件が厳しくなります。

## 参考

https://github.com/rails/rails/pull/48036

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value
Dockerで立ち上げているDBが急に動かなくなった時の対処メモ

# はじめに
また何もしていないのにDockerが動かなくなりました。
その対処方法について記載します。

原因はVolumeサイズの肥大化です。

# 状況
- ローカルの開発環境
- docker compose upを繰り返し行っていた
- 動かないのはPostgreSQLのコンテナ
- 実行時のエラー内容は以下
```
Error: P1001: Can't reach database server at `db`:`5432`
Please make sure your database server is running at `db`:`5432`.
```
- コンテナ内部でのログは以下
```
PostgreSQL Database directory appears to contain a database; Skipping initialization
FATAL:  could not write lock file "postmaster.pid": No space left on device
```


# 対処方法
DockerのVolumeを開放する。
```
docker system prune 
```


# 終わり
まれによく止まるので慣れてきました。
そのうち設定を見直したいですね。
DeepLearningのトラブルシューティングメモ

## lossの値が急に大きくなった

学習が発散した可能性
- 学習率(learning rate)が高すぎる

## 学習が収束しない

- 学習率(learning rate)が低すぎる
- データに元々特徴がない(⇒まずはmodelが正しいか、確実に法則のある恣意的なデータで実験)
- データに外れ値が存在する(yが1.1, 1.0, 1.2...辺りで遷移しているところに突発的に100とか来る)
    - データの正規化を行う
    - データの正則化を行う
    - 意図的に訓練データから削除しておく
- 入力と出力の特徴がある程度わかっているのならば、あらかじめわかっている特徴に加工して入力を与える

例)入力が数値の推移で、出力が数値の推移変化の場合入力としても数値の推移変化を与えてあげたほうが良い
参考:https://playground.tensorflow.org/
データが円形になるとある程度分かっている場合はX^2のデータを与えたほうが圧倒的に早く収束する

- 場合によってはlossは下がり切らなくても良い(極端な例外値が含まれるデータにおいてdropout/正規化した場合lossの値は減らないが例外値以外では正常に動作する場合もある)

## 訓練時はlossの値が低いのに、テストや本番時には高い
過学習(Over Fitting)

- epochをさげて、学習データを増やす
- dropoutの追加を検討/dropのパラメータを強めに
- 正則化(Regularization)を検討

## 出力がすべて同じ値に
UnderFittng

- dropoutの値を弱めに
- 正則化の値を弱めに
- 学習を多めに
- 学習データ数を多めに
    - ShuffleをTrueにするのも多少の効果あり
- 勾配消失問題の可能性？
    - activationをreluにする
    

## lossがNaN

https://qiita.com/Ryu_long/questions/bb447bc0f8726e7043df
でご質問いただいたものを回答した内容

- 入力の絶対値が大きい可能性
  - 正規化、標準化の検討(入力の絶対値が大きくて計算過程でNaN化している場合)
  単純に割り算を入れて小さい値にしたり。
  `(x - x.mean()) / x.std())`等の式を入れたり。※ただしこれだと外れ値の影響を受けやすくなる
  sklearnで
```python
#最大値最小値スケーリング
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
```
```python
# 標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
```
```python
#レイヤ側で(keras
normalizer = Normalization(axis=-1)
normalizer.adapt(X_train)

model = Sequential([
    normalizer,  # 正規化レイヤ
    ....
])
```
  
- 0除算などにより次の層への入力がinfになった
  - clippingを使って0になりそうなときは小さいサイズに修正(1e-9は適当に設定した小さい値)
`hogehoge / (tf.clip_by_value(【0になりそうな式】, 1e-9, 1.0))`


------

対処方法がおかしかったり、他にもあるあるのトラブルありましたら是非コメントください。
これ系にハマると結構時間とられますので、あるあるがあるとすごい助かります。
Windowsでごみ箱を経由せずに即削除（完全削除）

# ごみ箱を一切使わずに即削除でいい場合

ごみ箱上で右クリック → 『プロパティ』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/3bd61661-7334-927a-6bf7-160765a07421.png)

『ごみ箱にファイルを移動しないで、削除と同時にファイルを消去する』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/9cab157d-4066-fd59-8c22-b539f692c1a9.png)

# 普段はごみ箱を使うが特定ファイルだけ即削除したい場合

ごみ箱のプロパティは『カスタムサイズ』にしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/58e4aa32-108f-5d0d-c953-e599e387fcd3.png)

即削除したいファイルを選択して **Shift + Delete** 。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/6b1bbc2f-1d66-3054-ba7e-92182a869a60.png)

# 蛇足

1. 開発中のexeがセキュリティソフトでウィルスと誤判定されてしまう。
1. 特定フォルダーだけ検知から除外設定。
1. ```C:\$Recycle.Bin\``` から始まるパスのファイル = ごみ箱に移動したファイルがが誤検知されてしまう。

みたいなことがたまにあるので完全削除のやり方まとめました。

# バージョン

Microsoft Windows [Version 10.0.19045.3996]
stablediffusion IP-Adapter-FaceID で同一キャラ作成

参考

https://itdtm.com/stablediffusion-ipadapter-faceid/

# IP-Adapter-FaceIDとは？

同じ顔のキャラを作ることが可能

まず、webui は v1.5が必須みたい。
ということで確認

起動したときに

```
Python 3.10.11 xxx
Version: v1.5.1 <- ここが1.5になっているか？(1.5.1でもok)
Commit hash: 68f336bd994bed5442ad95bad6b6ad5564a5409a
```

# ダウンロード

https://huggingface.co/h94/IP-Adapter-FaceID/tree/main

より

ip-adapter-faceid-plusv2_sd15.bin	
「webui」→「extensions」→「sd-webui-controlnet」→「models」フォルダーにファイルを保存

ip-adapter-faceid-plusv2_sd15_lora.safetensors
「webui」→「models」→「Lora」フォルダにファイルを保存

をダウンロードしてそれぞれ保存

あとは、参考サイトを参考に Contorlnetを使っていく。

元画像
![hoge2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/d35c1896-3e13-a86d-7f3d-3b083d11d540.png)



before
![00063-1387186477.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/b8478a08-4641-13a8-1425-cb7c347ce63e.png)

after
![00002-1387186477.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/153f9ddb-d9c8-4d1f-e493-9ef2b8bc77ac.png)


そして

before
![00008-3577442233.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/89d75673-8b8b-92a9-781e-75cd9d3a4b0b.png)

after
![00003-3577442233.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/d40c0ffa-7dc7-da60-cf67-bed5d37b6734.png)

たしかに顔がそのままですね。
（メモ）ANY.RUNがLinuxをサポートしていた

# ANY.RUNとは

マルウェアのサンドボックス解析の有償サービスである。
確かこれまではWindows環境での解析をサポートしていた。


# Linux環境での解析サポート開始の情報
Ubuntu22.04.2 をサポートしている模様。

https://any.run/cybersecurity-blog/linux-in-anyrun/




# 解析結果のサンプル
ANY.RUNのXでのポストで３件の検体の解析結果のリンクが紹介されている。

https://twitter.com/anyrun_app/status/1750486842879692898

https://app.any.run/tasks/47b8d73b-fa26-47e6-9ae3-05a6f759d610/

https://app.any.run/tasks/15ea3d1f-06f8-4bed-ae27-9396159dc7de/

https://app.any.run/tasks/e27b95d0-a67b-48fb-adc5-a5e2446fa893/

# その他公開されている解析結果
以下のページで検索できるが、現時点では"Linux"とか"Ubuntu"などのタグでフィルタリングできないようだ。
うまくフィルタリングできるようになったら、解析結果を見ていきたい。

https://app.any.run/submissions
AWS CDK(Python)用の環境をDockerで構築する

DockerFileとdocker-compose.ymlと、appディレクトリ（中身は空でOK）を同階層に配置。

DockerFile
```
FROM nikolaik/python-nodejs:python3.12-nodejs20-slim

RUN apt update && apt clean

RUN pip3 install awscli --upgrade \
    && pip3 install --upgrade pip \
    && pip3 install aws-cdk-lib \
    && npm install -g aws-cdk
```

```docker-compose.yml
version: '3'

services:
  aws-cdk:
    build: .
    image: aws_cdk_image
    volumes:
      - ./app:/app
    container_name: aws_cdk
    tty: true
```

コマンド
```
docker compose up -d
docker exec -it aws_cdk /bin/bash

mkdir -p /app/sample
cd /app/sample

cdk init --language python
cdk ls
cdk synth -o cloudformation --no-version-reporting --path-metadata false
# cdk synth [app_name] -o cloudformation --no-version-reporting --path-metadata false
```

```app.py
#!/usr/bin/env python3
import os

import aws_cdk as cdk
from aws_cdk import (
    DefaultStackSynthesizer
)

from sample.sample_stack import SampleStack

app = cdk.App()
SampleStack(app, "SampleStack", synthesizer=DefaultStackSynthesizer(
        generate_bootstrap_version_rule=False
    )
)

app.synth()
```

```sample/sample_stack.py
from aws_cdk import (
    Stack,
    aws_s3_deployment as s3deploy,
    aws_s3 as s3,
    RemovalPolicy
)
from constructs import Construct

class SampleStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        s3bucket = s3.Bucket(
            self,
            "MyS3Bucket",
            bucket_name = "cdk-test-bucket-yyyymmdd",
            versioned=False,
            removal_policy=RemovalPolicy.DESTROY
        )
```
Ubuntu: 22.10 から 23.10 にアップグレード

サポート期間が終了した、22.10 のアップグレード方法です。

次のページを参考にしました。
[Upgrading Ubuntu Server 22.10 Kinetic Kudu to 23.10 Mantic](https://unix.stackexchange.com/questions/767795/upgrading-ubuntu-server-22-10-kinetic-kudu-to-23-10-mantic)

22.10 をアップグレードしようとすると、次のメッセージが出ます。

```text
An upgrade from 'kinetic' to 'mantic' is not supported with this tool.
```

そこで、22.10 (kinetic) から、23.04 (lunar) にアップグレードし、それから、
23.10 (mantic) にアップグレードします。

<ol>
<li>/etc/apt/sources.list を修正</li>
    http://jp.archive.* を http://old-releases.* に変更
    
<p></p>

<li>更新</li>

```text
sudo apt update
sudo apt upgrade
sudo apt dist-upgrade
```

<li>再起動</li>
<br />
<li> /etc/apt/sources.list を修正</li>
    http://old-releases.* を http://jp.archive.* に変更
    kinetic　を　lunar　に変更

<p></p>
<li>更新</li>

```text
sudo apt update
sudo apt upgrade
sudo apt dist-upgrade
```

<li>再起動</li>
    23.04 になっていることを確認

```bash
cat /etc/os-release
```

<li> /etc/apt/sources.list を修正</li>
    lunar　を　mantic　に変更

<p></p>
<li>更新</li>

```text
sudo apt update
sudo apt upgrade
sudo apt dist-upgrade
```

<li>再起動</li>
    23.１０ になっていることを確認

```bash
cat /etc/os-release
```

</ol>
Python学習10日目～ファイルの読み書き～

## テキストファイルの書き込み

~~~Python
# with open(ファイル名,'w',encoding='') as 変数:
with open('C:/Codes/Python/test01.txt', 'w', encoding='utf-8') as file:
    file.write('Python\n')  # ファイルオブジェクト.writeで書き込み
    file.write('Hello')
~~~

### 注意点
~~~Python
with open('C:\Codes\Python\test01.txt','w',encoding='utf-8') as file:
    file.write('Python\n') #ファイルオブジェクト.writeで書き込み
    file.write('Hello')
~~~
~~~:実行結果
Traceback (most recent call last):
  File "c:\Codes\Python\write.py", line 1, in <module>
    with open('C:\Codes\Python\test01.txt','w',encoding='utf-8') as file:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      
OSError: [Errno 22] Invalid argument: 'C:\\Codes\\Python\test01.txt'
~~~

\はエスケープ文字として解釈され、'\t'はタブ文字と解釈されてしまっている
解決策として、以下のようにバックスラッシュを2つ重ねるか、正斜線'/'を使用してファイルパスを指定する

## テキストファイルの読み込み

~~~Python
# with open(ファイル名,'r',encoding='') as 変数:
with open('C:/Codes/Python/test01.txt', 'r', encoding='utf-8') as file:
    print(file.read()) #ファイルオブジェクト.readで書き込み
~~~
~~~:実行結果
Python
Hello
~~~

テキストの内容を一行ずつ読み込んで表示させる

~~~Python
with open('C:/Codes/Python/test01.txt','w',encoding='utf-8') as file:
    file.write('Python\n') #ファイルオブジェクト.writeで書き込み
    file.write('Hello\n')
    file.write('GoodBye')

with open('C:/Codes/Python/test01.txt', 'r', encoding='utf-8') as file:
    for count,text in enumerate(file,1):
        print(count,text,end='')
~~~
~~~:実行結果
1 Python
2 Hello
3 GoodBye
~~~

## CSVファイルの読み書き

### 書き込み

~~~Python
import csv
catalog = [('hat','3000'),('shirt','5000'),('shoes','4000')]
with open('C:/Codes/Python/catalog.csv','w',encoding='utf-8',newline='') as file:
    csv.writer(file).writerows(catalog)
~~~

#### writerowとwriterowsの違い
writerowは一行に書き込む、writerowsは複数行に書き込む

### 読み込み
~~~Python
with open('C:/Codes/Python/catalog.csv',encoding='utf-8') as file:
    for x in csv.reader(file):
        print(x)
~~~
~~~:実行結果
['hat', '3000']
['shirt', '5000']
['shoes', '4000']
~~~

## JSONファイルの読み書き

### 書き込み
~~~Python
import json
catalog = [{'name':'hat','price':3000},
           {'name':'shirt','price':5000},
           {'name':'shoes','price':4000}]
with open('C:/Codes/Python/catalog.json','w',encoding='utf-8') as file:
    json.dump(catalog,file,indent=3)
~~~

dump関数はイテラブルの書き込みを行う
引数にindentを指定するとインデントを入れることができる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680269/5f1acd65-1a81-60f9-332b-8bd3be5ecb73.png)

### 読み込み

~~~Python
with open('C:/Codes/Python/catalog.json',encoding='utf-8') as file:
    print(json.load(file))
~~~
~~~:実行結果
[{'name': 'hat', 'price': 3000}, {'name': 'shirt', 'price': 5000}, {'name': 'shoes', 'price': 4000}]
~~~
「リーダブルコード」を読んだ際の個人的メモ

# この記事について
「[リーダブルコード](https://www.oreilly.co.jp/books/9784873115658/)」を読んだ際に、個人的に印象に残ったことを整理してまとめたものです。本書に関しては、多くのまとめ記事があることでしょうし、n番煎じもいいところですが、そういった記事と本記事を読み比べることで何か得られることもあると思い、作成しました。

# コードは理解しやすくなければならない
これが本書を通した「大原則」であり、それを達成するための具体的な手法を本書では解説しています。

以下要点
- 明確な単語を選ぶ。その変数や関数などの処理や値などに基づいて、それを正確に表現するような名前をつける。(場合によっては接尾辞や接頭辞をつける)
<br></br>
 - 汎用的な名前には、それ相応の理由を用意する。いつも使いがちな「temp」は、その用途が本当に一時的なら構わない。
 <br></br>
- 値の範囲指定は、単語で意味を変える。min、max、start、first、begin、endなどをよく理解して使用すること。
<br></br>
- ブール値を持つ変数や関数にはis、can、hasなどの名前を使うとわかりやすい。
<br></br>
- 似たような処理を行うコードは改行などで別々に纏めておく。あるいは一連の処理を一区切りとして、そこまでを改行なしでまとめ、他の一連の処理とは別ブロックにするなど。
<br></br>
- コードからすぐに読み取れる意図はコメントしなくて良い。ただし、その手法を選択した理由があったり、抽象的な操作すぎて何をしているかが明らかでないならコメントをするべき。
<br></br>
- コードは人間の曖昧な思考を、数学的・アルゴリズミックに表現したものである。そのため、プログラムの実際の動きは、一連を通すことで、そのコードの処理したい目的が見えてくる。よって、コメントは時にコードの意図も説明するべきである。
<br></br>
- 制御フローを読みやすくするためには、do/whileを避け、関数から早期に(できるなら)リターンし、if/elseをわかりやすく記述し、ネストを浅くすることなどが有効である。わかりやすくなるなら三項演算子を使っても構わない。
<br></br>
- ロジックが複雑で巨大になってしまったら、それはもっと簡潔に表せるはずだ。
<br></br>
- プログラムの実行を制御する変数、中間結果だけを保持する変数、コードの読みやすさに関係しない変数などは削除する
<br></br>
- 目的を達成するためのコードの中には、目的が複雑であるほど、目的とは無関係なコードが増えてくる。そういったコードは目的の達成には必要だが、そのコードが目的を解決するわけではないので、別な関数などに汎用的に使用できる形で切り分けてしまおう。
<br></br>
- 前述したような無関係なコード(下位問題)は他のプロジェクトでも使えることが多い、そのためにも下位問題は完全に独立した機能として書くことで、多くのプロジェクトの役に立つだろう。
<br></br>
- 誰もが引数が少なくて、事前設定などの面倒なことをする必要のないライブラリが好きだ。だから、キレイじゃないなら自分でラップ関数を作ろう。(ライブラリ使用の際の雑務をこなしてくれる関数など)
<br></br>
- 簡潔で小さなコードを書きたいなら、ライブラリに親しむこと。解決したい問題はライブラリによって提供されているかもしれない。

# 最後に
本書にはここに書いたこと以上にも様々なテクニックが記述されていました。

また、「コードを小さく保つ」や「コーディングよりもUnixツールボックスを使う」などは、以前に読んだ「Unixという考え方」とも似ており、どこでも似たような考えに行き着くのだなと思いました。
WordPressコンテンツ内の画像をsrcsetを使った出し分けに書き換える

#はじめに
WordPressで表示している内容をRatinaに対応するようにしてほしいと依頼された。
ブロックエディタの画像やサムネイルであれば自動で出し分けされるようだが今回はまとめてHTMLで記述されている。
なんとかしてimgファイルを置換すればいいかな。

#解決方法
WPテーマ内のfunction.phpに以下のコードを追加
```function img_to_multi($text){
    //画像タグを抽出
	$pattern = '/<img (.*?)src=[\'"](.*?)(\.png|\.jpg)[\'"](.*?)>/i';
	preg_match_all('<img (.*?)src=[\'"](.*?)(\.png|\.jpg)[\'"](.*?)>', $text,$result);
    
	$target = array();
	$replace = array();
	foreach($result[2] as $i=>$img_base){
		$img = $img_base.$result[3][$i];
        //画像URLからWP上のIDを取得
		$id = attachment_url_to_postid($img);
        //WPにアップロードされた画像であればlarge,mediumの画像を取得して画面幅によって切り替わるようsrcset属性と置換
		if($id){
			$target[] = 'src="'.$img.'"';
			$target[] = "src='".$img."'";
			$medium_img = wp_get_attachment_image_src($id, 'medium');
			$large_img = wp_get_attachment_image_src($id, 'large');
			$replace[] = 'srcset="'.$img.' 1025w, '.$large_img[0].' 1024w, '.$medium_img[0].' 600w" sizes="auto"';
			$replace[] = "srcset='".$img." 1025w, ".$large_img[0]." 1024w, ".$medium_img[0]." 600w' sizes='auto'";
		}
	}
	$text = str_replace($target, $replace, $text);
	return $text;
}
add_filter("the_content", "img_to_multi");
```


#おわりに
がんばったらpictureタグに入れ替えるとかもできたかもしれないがそこまでは出来ていない。
あくまで画面幅にしか対応していないのでどちらかというとAndroid端末対応の方が正しいのかも。
【DynamoDB】LastEvaluatedKeyと友達になりたい

# はじめに
画面にデータを表示させたい！でもDynamoDBは1回のクエリで取得できるデータサイズが1MBと決まっているから
分割してデータを取得しないといけない！
という場面で活躍するLastEvaluatedKeyについて触れる機会があったので簡単にまとめてみたいと思います。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Query.Pagination.html

# LastEvaluatedKeyとは
個人的なイメージですが本のしおりみたいなものです。「ここまで読んだ」というように、
LastEvaluatedKeyは「ここまでデータを取得した」を示す存在です。
そして「ここからデータを取得する」を示す存在としてExclusiveStartKeyというものがあります。
検索時にこのExclusiveStartKeyを使用することで続きからデータを取得してくれて、
かつデータを分割して取得しても順番がおかしいということがなくなります。

# LastEvaluatedKeyの動き(例)

## 条件
・取得したいデータが全部で20件
・1回のクエリ検索で取得するデータが10件(検索の際「Limit」オプションで指定できます)

今回はクエリ検索を2回行います。
1回目のクエリ検索では以下のようなレスポンスを確認できます。

```python
{'Items': [{'hoge': 'text1', 'huga': '12345'}, {'hoge': 'text2', 'huga': '12345'}],
'LastEvaluatedKey': {'id': '12345678', 'created_at': Decimal('0')}}
```

2回目のクエリ検索ではこのLastEvaluatedKeyをExclusiveStartKeyに指定してあげれば続きから検索してくれます。

```python
'LastEvaluatedKey': {'id': '12345678', 'created_at': Decimal('0')}}
```

2回目のクエリ検索では20件に対して11件目～20件目を取得するため、続きのデータがありません。
その際はLastEvaluatedKeyが空で返ってきます。

```python
'LastEvaluatedKey': {}
```


# おわりに
RDSではこのような考えをしたことがなかったので新鮮でした。
重めのデータを扱う際はデータサイズの制限も考慮する必要があるので苦労しました。
また、ページネーションやGSIなどが絡んでくると頭の中がばたばたしました。この時のお話もどこかで記事にできればと思います。
Windows で S3 署名付き URL に、curl をつかってファイルをアップロードする

# はじめに

S3 で署名付き URL を生成して、アップロードを行う際に、Windows からアップロードできるか不明でした。

この記事では、S3 署名付き URL を使って、WIndows からアップロードを行う検証記事です。



# 留意点

この記事では、curl コマンドを利用する前提で記載しています。

Windows のコマンドプロンプト上で curl コマンドが標準で搭載されたのは Windows 10 からです。古い WIndows 7 では追加のインストールをすれば curl が利用できるようです。ただ、そもそもサポートも切れているので利用できないことも考えられます。古めの OS の場合は気を付けましょう。



# Python でアップロード用の署名付き URL を生成する

まず、S3 のアップロード用の署名付き URL を生成します。いろいろ方法はあるのですが、今回は Python のプログラムから生成します。

```python
import boto3

s3_client = boto3.client('s3', region_name='ap-northeast-1')

url = s3_client.generate_presigned_url(
    ClientMethod='put_object',
    Params={'Bucket': 's3-presigned-test01', 'Key': 'this-is-filename.txt'},
    ExpiresIn=3600)

url = url.replace('s3.amazonaws.com', 's3.ap-northeast-1.amazonaws.com')

url = url.replace('&', '^&')
print(url)
```

　　



重要なところをピックアップします。以下の部分は、アップロードの対象の S3 バケットの名前や、ファイル名を指定するものです。ファイル名は、アップロード元のファイル名は関係なしに、ここで指定した `this-is-filename.txt` にリネームされます。

```python
Params={'Bucket': 's3-presigned-test01', 'Key': 'this-is-filename.txt'},
```

　


一点注意点があり、Windows のコマンドプロンプトを利用する際には「& (アンパサンド)」は特別な意味を持つ特殊記号です。2 つ以上のコマンドを連結する際に利用されるものとなっています。S3 の署名付き URL には「&」が含まれているので、Windows 用にエスケープを行っています。

次のコードの部分で、エスケープを行っています。コマンドプロンプト以外の環境では不要なので削除してください。

```
url = url.replace('&', '^&')
```

　

実行例はこんな感じです。

```
$ python3 s3-create-presign-upload.py
https://s3-presigned-test01.s3.ap-northeast-1.amazonaws.com/this-is-filename.txt?AWSAccessKeyId=yoursecret^&Signature=80lSBFAm7BvYoyP%2FT7kRbWlfUn0%3D^&Expires=1706693998
```



# コマンドプロンプトからアップロード

さきほど生成した署名つき URL を使って、コマンドプロンプトからアップロードを行っていきます。コマンドプロンプトを立ち上げます。

![image-20240131154605586.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/3ee9d087-c134-9340-b493-3799fc6318cc.png)



アップロードしたいファイルが格納されているディレクトリに移動します。

```
C:\Users>cd C:\sizai

C:\sizai>dir
 ドライブ C のボリューム ラベルは OSDisk です
 ボリューム シリアル番号は F68D-03E3 です

 C:\sizai のディレクトリ

2024/01/31  15:47    <DIR>          .
2024/01/31  15:47    <DIR>          ..
2024/01/31  15:43                 7 hello-s3-from-windows.txt
               1 個のファイル                   7 バイト
               2 個のディレクトリ  46,067,441,664 バイトの空き領域

C:\sizai>
```



curl コマンドを使ってアップロードを行います。

```
curl -X PUT ^
--upload-file hello-s3-from-windows.txt ^
"https://s3-presigned-test01.s3.ap-northeast-1.amazonaws.com/this-is-filename.txt?AWSAccessKeyId=yoursecret^&Signature=80lSBFAm7BvYoyP%2FT7kRbWlfUn0%3D^&Expires=1706693998"
```



次のように、S3 バケットにファイルがアップロードされている様子を確認できました。

![image-20240131175622115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1002774/1245d0e1-a86b-05ca-c1d1-49b107739496.png)



# 検証を通じてわかったこと

- Windows に curl コマンドが標準搭載されているのは Windows 10 以降
- curl コマンドで指定する URL に「&(アンパサンド)」が含まれており、エスケープに気を付ける必要がある。
【TypeScript】yupで型付けする方法

yupで型を付ける方法をご紹介します。スキーマに型を付けることで、バリデーションが効かないなどのミスを防ぐことができるので、yupを使用している人は試してみてください。

## 環境

yupのバージョンは`1.3.3`で動作確認しています。

## 結論

`ObjectSchema`で作成したスキーマに型を付ける

## 使用例

### 例1

`name`, `age`は必須の場合

```typescript
import * as yup from 'yup';
import type { ObjectSchema } from 'yup';

type User = {
  name: string;
  age: number;
};

const UserSchema: ObjectSchema<User> = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required(),
});
```

### 例2

`name`は必須、`age`は必須でない場合

`age`を省略すると型エラーとなるため、必須でない場合でも`yup.number().defined()`のようにスキーマを定義します。

```typescript
import * as yup from 'yup';
import type { ObjectSchema } from 'yup';

type User = {
  name: string;
  age: number;
};

const UserSchema: ObjectSchema<User> = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().defined(),
});
```

### 例3

`name`, `age`は必須、`address`は`age`が`20`以上で必須の場合

`when`の後（前でも可能）に`shape`でスキーマを定義します。

```typescript
import * as yup from 'yup';
import type { ObjectSchema } from 'yup';

type User = {
  name: string;
  age: number;
  address: {
    prefecture: string;
    city: string;
  };
};

const UserSchema: ObjectSchema<User> = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required(),
  address: yup
    .object()
    .when('age', {
      is: (age: User['age']) => age >= 20,
      then: (schema) =>
        schema.shape({
          prefecture: yup.string().required(),
          city: yup.string().required(),
        }),
    })
    .shape({
      prefecture: yup.string().defined(),
      city: yup.string().defined(),
    }),
});
```

## まとめ

yupに型を付ける方法をご紹介しました。必須でない場合でも`defined`でスキーマを定義しないと型エラーとなるため注意が必要です。


## 最後に

### GoQSystemでは一緒に働いてくれる仲間を募集中です！

ご興味がある方は以下リンクよりご確認ください。

https://goqsystem.recruit.site?qiita=4521293e3918c82d99e0
なんでもあり採用インターンの攻略法、5選！

## なんでもありインターン とは
弊社(日揮パラレルテクノロジーズ・以下JPT)の採用はいささかユニークであり、応募から内定・業務開始まで、フルリモートで完結しています。

そんな中で、第一の関門が「採用インターン」であり、実際に課題を解いて頂いております。そんなインターンを、個人的には「なんでもありインターン」と呼んでいます。（まるで「なんでもありRTA」のように）

ちょうど今年度の採用が一段落しましたので、1担当者として勝手に攻略法を書いてみようと思います。

※ 本記事は2023年度の採用を基にした記事であり、今後内容は変更となる場合があります。ご了承ください。

## JPTの採用インターンの傾向と対策
昨今のJPT採用インターンの課題は課題の難易度が上がってきております。

これは、純粋に応募いただく方が増えたこと、および従来より採用フローがバージョンアップされた事が理由となります。

しかし、以下の攻略法をマスターできると、課題の難易度が一気に変わってきます。

## JPT採用インターンのルール
JPTの採用インターンは、フルリモート・フルフレックスの特性を最大に活かせるよう、以下のルールにて実施しています。

- 作業時間は累計100時間以内
- いつ、どこで、どれだけ作業してもよい
- メンターサービス・生成AI・知り合いに聞くなど、何でもあり

一方、コミュニケーションや提出については、以下のルールにて行っています。
- コミュニケーションは専用のDiscordサーバーで行う  
- 毎日の作業後に日報を提出
- 成果物についてのプレゼンテーションの作成・提出
- 成果物はGitリポジトリにて提出

## なんでもあり採用インターンの攻略法
開発/分析ともに一般論はありますが、まずは以下の点を抑えておくと良いかと思います。

- 安定したPCや作業環境を用意する。  
  ストレス要因はなるべく減らしておきましょう。
- 命の前借りはしない。  
  できれば常に2～3割の余裕をもつ
- まずは設計・計画。  
できればリカバリープランも用意できるとよき
- 出来るだけGitHub Flowを使う。  
同時になぜ現場でGitが必要なのか理解しておくとよき

これらを踏まえた上で、なんでもありインターンのオススメの攻略法を５つご紹介しましょう。

### 1. 完成の定義を決める(100%完成できなくていい)
課題のPDFを開き最初に感じられる事は、「今まで感じたことのない難しさ」だと思います（実際、そのようなフィードバックは多いです）。

一方、「試験のアウトプットは100%完成させるべき」と思われがちですが、昨今のJPTのインターン課題はそもそも100%の完成そのものが難しいです。

ここでのオススメは、**100時間という絶妙に足りない時間の中で、「何をもって完成とするか」 を決める**、です。言い換えれば難易度の調整とでも申しましょうか。

まずは提出する事が大事なので、ムリのない難易度に調整しましょう。

### 2. 完成できなかった部分は、どうしたら完成できたか説明する
100時間という絶妙な時間制限の中では完成が難しいことも少なくありません。

ここでのオススメは、**「どうやったら残りを完成できるのか」を説明**する事です。

エンジニアとして100%完成させたい気持ちは十分わかりますが、現実はまず成果物を完成させ提出する事が大事です。残った課題は、今後の自分に託す気持ちでまとめ、プレゼンテーションにしたためて頂けると良いかと思います。

### 3. 効率よい技術スタックを使う(決められた解法はない)
採用インターンでは毎回新しい課題が出ますが、特定の技術スタックに依存しないよう設計されています。つまり、答えはありません。

ここでのオススメは、**「自身にとって効率のよい技術スタックを使う」**、です。

新しく学習する技術にせよ、既に身に着けた技術にせよ、まずは先を見通せる技術スタックを選ぶと非常にこなしやすくなるかと思います。

併せて、Web系では公開（デプロイ）の経験があると、先の見通しが立てやすくなるのでオススメです。

### 4. わからない事は素直に聞く
実際のインターンの中で、わからない事はたくさん出てきます。課題のバックグラウンドがわからない、設計がわからない、実装方法がわからない。もしかしたら、そもそも課題にバグがあるのかもしれません（その時は教えてください）。

ここでのオススメは、**「素直に聞く」**、です。

もちろん、ある程度自力で試行錯誤し解決に向かう事は大切ですが、それでキャパオーバーになったりタイムロスするのは非常にもったいないです。

繰り返しになりますが、JPTの採用インターンは「なんでもあり」です。友人・知人・ネッ友・支援員・メンター・Youtube・Udemy・Qiita・AIなどなど、使えるリソースは全部使いましょう。

### 5. 自分の状況を正しく説明する
JPTのインターンでは、Discordへの参加やGitのセットアップなどが必要であり、必要な技術サポートをチャットにて行っております。上記の攻略法にも関連しますが、この際「今どういう状況で、どうしたいのか」が明確でないと、お返事が非常に難しくなります。

ここで大切なのが、「自分の状況を理解してもらえるように説明するスキル」、です。 

オススメは、**自身が質問された際に聞きたくなる情報を、事前にリストにまとめる**、です。これをして頂けるだけで、非常にコミュニケーションしやすくなります。

「わからないことがわからない」という方は、まずは料理など一般的な質問に置き換えた上で、ロールプレイングまで出来ると身に着けやすいかと思います。

## おわりに - JPTを目指す冒険者のみなさまへ
実はこの記事は、開発者として必要な一般スキルを書いたものでした。他社様でも十分にご活用いただける攻略法かと思います。よろしければ参考にしてください。

その上で、JPTは「障害者雇用×ITエンジニア×フルリモート・フルフレックス」という、きわめてユニークな会社です。人によっては桃源郷のようにも感じるでしょう（ソースは私です）。

また、「まずは力試ししてみたい！」といった方や「フルリモート・フルフレックスが自分に合うかわからない」といった方へは、「職場体験インターン」を実施しております。

次回のインターンでお会いできることを楽しみにしています。
Javaで商品購入プログラムを作ってみた




```java
public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタ

		System.out.println("注文");
		Scanner scan = new Scanner(System.in);
		String select = scan.next();
		
	Map<String,Integer>list = new HashMap<String,Integer>();
	
	list.put("いちごのケーキ", 100);
	list.put("モンブラン", 200);
	list.put("チョコケーキ", 333);
	list.put("タルト", 234);
	list.put("チーズケーキ", 123);
	
	int total = 0;
	
	switch(select) {
	 case "いちごのケーキ":
	    case "モンブラン":
	    case "チョコケーキ":
	    case "タルト":
	    case "チーズケーキ":
	    	System.out.println("購入個数を選んでください");
	    	int quantity = scan.nextInt();
	    	if(list.containsKey(select)) {
	    		int price =list.get(select);
	    		double subTotal = (price*1.10) * quantity;
	    		total = (int)subTotal;
	    	}else {
	    		System.out.println("その商品は扱っていません");
	    	}
	    	break;
	    	
	
	}
	System.out.println("合計金額は"+total+"円です");
	scan.close();
	

	}

}
```
古いRaspberry Pi OSバージョンのイメージを作成

先日作成したRaspberry Pi Zero W用Raspberry Pi OS(Pythonバージョン3.9)に、Python 3.5環境用whlファイルをインストールするため、Python 3.5を取り込んだけれどもpipが使えなくて古いRaspberry Pi OSにした話です。

#### 使用環境：
- Raspberry Pi Zero W
- Raspberry Pi OS 11(Bullseye)　→ 9(Stretch)に変更


#### 問題点：
- pip install以外のwhlファイルインストール方法が分からない
- Raspberry Pi OS 11上にて、python3.5を使用て「-m pip install hoge」を実行すると「/usr/local/bin.python3.5: No module named pip」のエラーになる

トータル10時間分くらい試行錯誤しましたが、私の力量では解決できませんでした。

#### 解決方法：
- デフォルトでPython3.5をサポートしているRaspberry Pi OS 9(Stretch)に置き換えた

敗北感がすごいですが、自分が扱っているコードがOSのバージョンではなく、Pythonのバージョンに大きく依存していたので、早々にギブアップしました。
Raspberry Pi OS 11上で、古いPythonバージョンを使用したpipを使う必要がある方の力にはなれません。スミマセン。

#### 手順：
1. 古いバージョンのRaspberry Pi OSを入手
2. Raspberry Pi Imagerでイメージメディアを作成
3. 注意事項

---

#### 1. 古いバージョンのRaspberry Pi OSを入手
使用したいRasiberry Pi OSのZIPを下記サイトからダウンロードします。
https://downloads.raspberrypi.org/raspbian/images/

フォルダがリリース日毎に分かれていて探すのが大変な場合は、Rasiberry Pi OSのWikipedia( https://en.wikipedia.org/wiki/Raspberry_Pi_OS )に記載してあるリリース日とバージョンを見ながらおおよそのあたりをつけて探すと少し楽です。


#### 2. Rasiberry Pi Imagerでイメージメディアを作成

① ダウンロードしてきたZIPファイルを解凍
② Raspberry Pi Imagerを起動し、OS選択時に「Use custom」を選択（リストの一番下にあるのでスクロールしてください)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703624/a3a28b4f-c3d6-8d0e-856a-fbaed2d108ea.png" width=80%>

③ ①で解凍したフォルダにある.imgを指定
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703624/e86f2689-5eac-761f-dc71-7ade049613d1.png" width=80%>

④ [Raspberry Piデバイス]と[ストレージ]も選択し[次へ]をクリックしメディアを作成
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703624/6f01101e-9acf-45f1-3f01-2ba7a4897ecd.png" width=80%>

#### 3. 注意事項
使用するRaspberry Pi OSバージョンによっては、ImagerでWifiやSSHの事前設定はできないようです。(私が使用したバージョンはできませんでした。)
事前設定ができない場合は作成したイメージを使用して起動した後、ユーザアカウントのパスワード、Wifi、SSHを設定します。

※ SSHは、画面左上のラズパイマーク→[Raspberry Pi Configuration]を選択後、[Interfaces]タブをクリックすると有効化できます。


---
古いRaspberry Pi OSのイメージメディアを作成する手順は以上です。

ここからはRaspberry Pi OSのPythonとPipで苦労した時のメモです。
最終的には上記の通り「古いOSバージョンを使う」で解決したため、本当にただの苦労話です。

##### やりたかったこと：
Raspberry Pi Zero WのRaspberry Pi OS 11(Bullseye)で、Python3.5用.whlをインストール

##### やったこと：
- Python3.5のインストール → 成功
- python3.5 -m pip install xxxxxxxx.whl → エラー /usr/local/bin.python3.5: No module named pip
- alternativesを使用してpython3.5をpythonに設定(＝ソフトリンク)して pip install xxxxxxxxx.whl → エラー(No module named pip)
- https://bootstrap.pypa.io/get-pip.pyを見る → minimum python versionが3.7であることを知る
- minimum python versionにひっかからないget-pip.pyを持ってくる
- pipのインストールを試みる sudo python3.5 get-pip.py → ERROR: No matching distribution found for pip<21.0
- もう私で対応できる範囲ではないことを悟る
- whlをインストールするだけならば、古いRaspberry Pi OS使えばいいかとあきらめる

これはPython経験が浅すぎることによる挫折なのかもしれません。。。
いつか対応方法が分かるといいなと思います。
地方公共団体情報システムにおける文字コード要件（IPAmj/MJ文字）について整理してみた

# はじめに

こんにちは。すぎもんです。:grinning:

企業はデータ利活用の促進を進め、あらゆる業務において担当者レベルでデータを活用することが増えてきました。世に溢れる様々なサービスを駆使して、いわゆる技術者ではない人たちも「内製化」という名のもとに、データを扱います。

ただこの「データを扱う」というところで、重要なポイントであるものの、意外と深～く理解している人がすくない「文字コード」。日々様々な技術が生まれて消えていく中、古くから変化せずに「文字コード」は存在しています。

今回は、その文字コードに関連した案件の中で、気付いた情報を整理しました。

## 最近出会った案件にて

お客様の中には自治体様も多く、国や行政としてのシステム仕様やデータ要件への対応で、DataMagicをご案内することがあります。

例えば、珍しい漢字の姓名が含まれた名簿データ。いわゆる「異体字」です。

まず、この苗字「髙橋」さんは読めますか？
もちろん「たかはし」さんなのですが、「髙」をよーく見て頂くと、普通の「高」ではなく「髙」なのです。俗に「はしごだか」と呼ばれている「異体字」です。難読苗字だからというわけではなく、この記事の閲覧環境によっては文字化けで読めない文字です。

※文字化けしてしまって読めていない方のために画像を貼っておきます:grinning:
　　　　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/458288/176b7b59-ecf5-d146-be23-b4d02f3389ff.png)


### ■ ｢地方公共団体情報システム｣のデータ要件・連携要件

文字化けの問題は、システム開発においても大きな問題になります。
単純な見た目の問題だけでなく、データを正しくシステムが認識し登録できるかという問題になるからです。（「髙（はしごだか）」で登録した文字をシステムが勝手に「高」へ変換して登録すると、それはもう誤登録です。:scream:）

特に、戸籍や住民基本台帳を管理している地方自治体のシステムでは、
通常使われない珍しい漢字や「異体字」を含む氏名、地名に対応しなければいけないため必須の対応になります。

そういった対応を進めるために、
国・地方行政のIT化やDXの推進を目的としたデジタル庁では、
地方自治体のシステム仕様を整理し、公開しています。

:::note info
デジタル庁： データ要件・連携要件の標準仕様 
https://www.digital.go.jp/policies/local_governments/specification/
:::

この仕様書の文字要件にあたる部分を満たすことで、地方公共団体で管理しているシステムでの問題の発生を防ぐことが可能となります。

最近の案件でそういった要件に対応するために私はDataMagicをお客様にご紹介しています。それは、「DataMagic」が「[地方公共団体情報システム データ要件・連携要件 標準仕様書 【第 3.0 版](https://www.digital.go.jp/assets/contents/node/basic_page/field_ref_resources/4748d2a4-45c7-45de-a55f-dfac4cb04a70/d1ad6560/20231004_policies_local_governments_specification_outline_02.pdf)」の要件に準拠しているからです。
→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→
**要は、「地方公共団体情報システム」の文字要件※の変換に対応している。
　　　　　　　　　　※　文字情報基盤の文字セットであるMJ文字**
→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→

自治体システムの文字要件として、2025年以降は、下記が定義されています。
| データの文字セット | 文字コード |
|:-----------|------------:|
|  JIS X 0213:2012 | JIS X 0221:2020 |

:::note warn
**文字セット「JIS X 0213:2012」とは**
「UTF-8」「UTF-16」「IBM漢字」で表現可能な文字集合です。「JIS X 0213」の2012年改正版で、「JIS X 0213:2004（通称 JIS 2004）」から文字追加はありません。
:::

:::note warn
**文字コード「JIS X 0221:2020」とは**
Unicode（＝UCS：Universal Character Set）と同義です。
「UTF-8」「UTF-16」「UTF-32」のいずれかの文字コードを使えば表現できます。
:::

文字セットや文字コードは、一般的にも、SJISやUnicodeなどご存じの方も多いと思います。でも…文字コード「JIS X 0221:2020」と言われると、なんだか難しそうに見えますよね。ここは「Unicode」と読み替えてもOKです。

IPAmj（MJ文字）への対応には、この「Unicode」と「異体字」の対応が必須なのです。
DataMagicは、この「Unicode」と「異体字」に対応している。つまりは、「IPAmj（MJ文字）に対応している=自治体システムの文字要件を満たす」というわけです。

DataMagicの機能詳細は[こちら](https://www.hulft.com/help/ja-jp/DM-V3/COM-FUN/Content/DM3_JA_FUN/Chap04/ConvVariantChar.htm)

# おわりに
いかがだったでしょうか。戸籍などを扱う地方自治体ならではかつ、文字コード要件に踏み込んだ内容でした。案件対応で知った内容もいくつもありましたが、自治体だけでなく、一般企業でも顧客名簿のシステム登録等で考慮するべき点が見えてくるかと思います。皆さんが珍しい漢字や異体字を扱った際にこの記事を思い出していただければ幸いです。

このブログでは、今後も様々なTopics記事や接続検証等、皆さんの役に立つような記事を投稿していきたいと思います！
ここまで読んでいただきありがとうございました。それでは、また！
Javascriptの条件分岐で思わぬ落とし穴...

## はじめに
UdemyのJavascritpで思ってもいない箇所で躓いたのでまとめます！
知っているつもりで使っていたのですが、実は理解しきれていない箇所が出てきました。

## 問題
const nameArr = ["佐藤","鈴木","吉田"];
という配列に対してjsのmap関数を使って、「吉田」以外の人には「さん」をつけて値を返す
最終目標は
["佐藤さん","鈴木さん","吉田"]
という感じ
しかし今回["吉田","吉田","吉田"]
となってしまった。

## 実際のコード
```js
const nameArr = ["佐藤","鈴木","吉田"];

const nameArr2 = nameArr.map((name) =>{
  if(name = "吉田"){
    return name
  }else{
    return `${name}さん`;
  }
});
console.log(nameArr2);
```
## 解決策
`name = "吉田"`の箇所を`name === "吉田"`に変更
最初の`name = "吉田"`ではmap関数で回ってきた名前に対して「吉田」が代入されていた感じでした。
例えば`"佐藤" = "吉田"`のような感じです。

## 終わりに
これから条件判定する時には = ではなく === を使用するように気をつけます！
=で条件判定できると思っていましたが、=には条件判定以外にも代入するという認識が抜けていました...
Lenostube の YouTube スクリプト アウトライン ジェネレーターでコンテンツ作成に革命を起こしましょう

脚本は、力強く興味深い映画の雰囲気を確立する、ダイナミックなコンテンツ制作の世界の基礎です。 Lenostube の「https://www.lenostube.com/en/youtube-script-outline-generator/」は、プロデューサーにストーリーを改善するためのユニークで効果的なツールを提供するため、革新的なツールです。

魅力的なビデオの作成がかつてないほど簡単になりました。 Lenostube のスクリプト アウトライン ジェネレーターは、作成者が自分のアイデアや考えを簡単に整理できる直感的なインターフェイスを提供することで、執筆プロセスを容易にします。このツールは、コンテンツ制作者がインスピレーションの触媒として機能することで、ストーリーラインをより効果的にアレンジするのに役立ちます。

ビデオ素材の繊細さをナビゲートするスキルは比類のないもので、Lenostube のジェネレーターは傑作の最後の仕上げとなります。このツールを使用すると、経験豊富なコンテンツ プロデューサーであろうと、新進気鋭の YouTuber であろうと、スクリプトが単なる紙の上の言葉ではなく、魅力的なビジュアル ナラティブのガイドとして機能することが保証されます。
LazyListの要素の表示位置を調整する

LazyListがスナップされたときに要素を特定の位置に表示する方法の備忘録です。
Pagerをスワイプしたときみたいな動きをさせたい、ってかんじです。

# デフォルトの挙動

![default-fling.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542937/34171d19-0fb6-4adb-0dbb-faaf88b067ef.gif)

普通のスクロールです。

# 中央に表示する

![align-center.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542937/7f1ad997-2c07-b619-2853-bd0124ae3084.gif)

スクロールが止まったときに要素を中央に表示します。

```kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AlignElementAtCenterInScreen() {
    val state = rememberLazyListState()
    val flingBehavior = rememberSnapFlingBehavior(state)
    LazyRow(
        contentPadding = PaddingValues(16.dp),
        state = state,
        flingBehavior = flingBehavior,
    ) {
        items(10) {
            Box(
                modifier = Modifier
                    .padding(8.dp)
                    .background(Color.LightGray)
                    .size(300.dp, 700.dp),
                contentAlignment = Alignment.Center,
            ) {
                Text(text = "$it")
            }
        }
    }
}
```

SnapFlingBehaviorを設定すると、中央表示してくれます。

# 左端に表示する

![align-left.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542937/0e42f241-f07a-f9da-e156-52c3fa707552.gif)

```kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AlignElementAtStartInRow() {
    val state = rememberLazyListState()
    val positionInLayout: Density.(Int, Int, Int) -> Int = { _, _, _ ->
        // 左端に揃えるには0を返す
        0
    }
    val snapLayout = remember(state) { SnapLayoutInfoProvider(state, positionInLayout) }
    val flingBehavior = rememberSnapFlingBehavior(snapLayout)
    LazyRow(
        contentPadding = PaddingValues(16.dp),
        state = state,
        flingBehavior = flingBehavior,
    ) {
        items(10) {
            Box(
                modifier = Modifier
                    .padding(8.dp)
                    .background(Color.LightGray)
                    .size(300.dp, 700.dp),
                contentAlignment = Alignment.Center,
            ) {
                Text(text = "$it")
            }
        }
    }
}
```

SnapPositionInLayout をいじると、ポジションを調整できます。
`0` で左揃えにしてくれます。

[[中央に表示する](#中央に表示する)]では、内部的にここが `SnapPositionInLayout.CenterToCenter` になっているようです。

# SnapFlingBehavior
https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/snapping/SnapFlingBehavior
[paiza]C言語のストリーム出力をする

原則、paizaでfopen関数は使えない。

ストリーム出力だけなら、次のコードでも出力だけはできる。

＜コード例＞
--------------------------------

#include <stdio.h>
#include <string.h>
#define NAME_LEN 50
#define NINZU_MAX 100

int main( void ){
    FILE *fp;
    int i, j;
    int ninzu = 0;
    char name[ NAME_LEN ][ NINZU_MAX ];
    double height[ NINZU_MAX ];
    double weight[ NINZU_MAX ];
    double hsum = 0;
    double wsum = 0;

    if ( ( fp = fopen ( "abc.txt", "r" ) ) == NULL )
        printf ( "そのファイルをオープンできません。\n" );
    else {
        for ( i = 0; i < NINZU_MAX; i++ ) {
            if ( fscanf ( fp, "%s%lf%lf",
                /* 名前・身長・体重の3項目の読み込みができれば3が返却される */
                name[ ninzu ], &height[ ninzu ], &weight[ ninzu ] ) != 3 )
                break;
            hsum += height[ ninzu ];
            wsum += weight[ ninzu ];
            ninzu++;
        }
        /* 身長の昇順にソート */
        for ( i = 0; i < ninzu - 1; i++ ){
            for ( j = ninzu - 1; j < i ; j-- ){
                if ( height[ j - 1 ] > height[ j ] ){
                    char tn[ NAME_LEN ];
                    double td;
                    strcpy ( tn, name[ j ] );
                    strcpy ( name[ j ], name[ j - 1 ] );
                    strcpy ( name[ j - 1 ], tn );
                    td = height[ i ]; height[ j ] = height[ j - 1 ]; height[ j - 1 ] = td;
                    td = weight[ i ]; weight[ j ] = weight[ j - 1 ]; weight[ j - 1 ] = td;
                }
            }
            for ( i = 0; i < ninzu; i++ )
                printf ( "%-10s %5.1f %5.1f \n", name[ i ], height[ i ], weight[ i ] );
            printf ( "--------------------\n" );
            printf ( "平均      %5.1f %5.1f\n", hsum / ninzu, wsum / ninzu );

            fclose ( fp );
        }
    }
    return 0;
}
被引用数の多い順に選ぶサイトを作成

# はじめに



https://papersearch.onrender.com/

被引用数の多い順に選ぶサイトを作成しました。

# 背景
https://joisino.hatenablog.com/entry/2023/04/10/170519


被引用数の多い順によむジョイジョイジョイさんの論文の選び方の１つです。

# 使い方
- 国際会議とジャーナルを選びます
    - ここではできる限りトップの国際会議とジャーナルを選びました
![Screenshot from 2024-01-31 16-36-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493403/09780698-da7d-aef8-e90e-cbb7f881ad86.png)


- 下のsearchを押します
![Screenshot from 2024-01-31 16-37-08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493403/25547ec0-055e-7f24-fc45-5ceff935d619.png)


- 影響被引用数順に降順で表示されます
![Screenshot from 2024-01-31 16-38-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1493403/31114e16-1240-21a7-7a83-a2a29255203d.png)

# コントリビューション
githubを公開しています。
https://github.com/Buddies-as-you-know/conference_paper_citation

CSSなどの設定はしておらず見にくくなっています。
もっと見やすくしたいやこんな国際会議を追加してほしいなどissue,pullreqをお待ちしています。
IBM i のユーザーやプリンター（OUTQ）毎に印刷出力したページ数を把握したい

本日のお題です。

## IBM i のスプール出力した枚数をカウントしたい
Q.
・プリンター（OUTQ）、ユーザーID、日別に印刷枚数（ページ数）を把握したい。
・現状の設定（運用）では、IBM iからの帳票印刷はWindowsサーバを経由しており、IBM i 上のスプールはWindowsサーバーへ送信されると同時にIBM iから消去されている。（スプールの属性が SAVE(*NO)になっている）

## 対応案1. スプール属性をSAVE(*YES）に変更可能な場合
この場合、IBM i 上に印刷後もスプールが残存しますので、その情報を何らかの方法で集計すればよい事になります。当然ですがスプールの削除も定期的に実施する必要があります。

### スプールのページ数やユーザー数など明細情報を取得する方法　その１

[こちらの記事で紹介した IBM i サービス SYSTOOLS.DELETE_OLD_SPOOLED_FILES プロシージャー](https://qiita.com/gomAnomalocaris/items/ca476e05e7de25f4b6a1)を使用すれば様々な条件で、SQL一発でスプールの出力情報を取得できます。
SQLでPREVIEW(*YES)にすればスプールを削除せずに何度も検索ができます。
検索結果をDB化する、ユーザーへ返答するアプリ画面に組み込む等できると思います。
![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313031313339342f39646562333837312d356563332d366430312d373132642d3665646239366362663964322.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/269a5ecd-9966-706c-0c56-a9e5a0c57f45.png)

この方法の場合、SQLのPREVIEW(*NO)を指定すれば削除もできますので、シンプルだと思います。

### スプールのページ数やユーザー数など明細情報を取得する方法 その2 SPOOLED_FILE_INFO 表関数

IBM i サービスの一つとして[**SPOOLED_FILE_INFO 表関数**](https://www.ibm.com/docs/ja/i/7.5?topic=services-spooled-file-info-table-function)というのもあります。

このSQLでも上記と同様に個々のスプールファイルのページ数、その他属性を取得できます。


## 対応案2. スプール属性をSAVE(*NO)のままにしたい
この場合は、IBM i OSの監査ジャーナル機能でスプール関連のユーザー操作を監査ジャーナルとして出力できます。

参考：SF (スプール・ファイルに対する処置) ジャーナル項目
https://www.ibm.com/docs/ja/i/7.3?topic=entries-sf-action-spooled-file-journal

下図のようなユーザー操作を監査履歴として取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/951865ea-3896-4fdf-5874-c08ee336af4f.png)


### 監査ジャーナルを使う場合の考慮点
・スプールファイルの属性 SAVE(*YES)への変更は不要です。
・監査ジャーナルをかけた場合、一般にはシステム負荷が追加になりますので、システムパフォーマンスに影響ないかの検証が必要です。
・監査ジャーナルが生成される分のストレージ消費量の検証（検討）も必要と思われます。

### 監査ジャーナルの確認のしかた

[次の記事](https://qiita.com/gomAnomalocaris/items/8a2a1f2b63436b0c3917) で監査ジャーナルの設定の仕方、監査レコードの検索の仕方などを説明しています。
Windowsでpinの変更を強制されてしまうのを何とかしたい

## 今回困ったこと
Windows PCを起動し、pinコードを入力すると「組織により、PINを変更するように要求されています」と表示されてしまう（一応生体認証を使うとなぜか強行突破できてしまう模様）
再び同じPINを設定すれば逃れられるかと思ったら、それは拒否されてしまう。鬱陶しい…
今回はこの症状の直し方を解説していきます。

## 原因
原因はずばり、職場や学校のメールアドレスをWindowsに登録してしまったから。
Windowsには、職場のメアドを登録すると、そこの定めたデフォルトの設定でこちらの設定を強制的に上書きしてしまう邪魔な機能があります。
よっしゃ、タネがわかったから即解決！というわけには残念ながらならず、少々厄介な手順を踏む必要があるので、順に解説していきます。

## Step1. メールアドレスの登録解除
原因を読んだ時点で勘のいい方はお気づきでしょうが、職場/学校のメールアドレスの登録を消します。
設定を開き、画面左のタブで「アカウント」を選択し、「メールとアカウント」を開きます。
この画面で職場のアカウントを削除しましょう。

ちなみに、筆者は4年制大学の学部を卒業していますが、大学のメールアドレスの登録を解除していても特に困ることはありませんでした。ただ、すべての学校でそうとは限りませんし、Microsoft Teamsを使う職場などでは削除をしたら問題になることも考えられるので、この操作は自己責任で行うようにしてください。

## Step2. レジストリ（＝設定）をもとに戻す
これで職場の設定を強制上書きされることはなくなりましたが、一度強制変更された設定は放っておいても元に戻ることはないので、自分で修正する必要があります。
この手順は少々長いですが、順番にこなしていきましょう。

①Win + R キーを押して実行ダイアログを開き、regedit と入力してOKをクリックし、レジストリエディタを開きます（普通にタスクバーで「レジストリ」とかで検索してもOK）

②レジストリエディタで次のキーに移動します：HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\PassportForWork\PINComplexity。
（移動は画面左のフォルダのようなアイコンをダブルクリックすることで行えます）
PassportForWork キーが存在しない場合は、Microsoft キーを右クリックし、新規 > キー を選択し、PassportForWork と入力してEnterを押して作成します。
同様に、PassportForWork の下に PINComplexity キーが存在しない場合は、同じ方法で作成します。

③PINComplexity キーの右ペインで、Expiration というDWORDを探します。存在しない場合は、右ペインで右クリックし、新規 > DWORD(32ビット)値 を選択し、Expiration と入力してEnterを押して作成します。
ダブルクリックで値の編集ができるので、「値のデータ」に0を入力します。これでpinを無期限に使用することができます。1~730（単位：日）の内からお好みで選んでいただいてもよいです。

④必ずしもやらなくてもよいですが、同じ要領でHistoryも0に設定しておきます。こうすることで、同じPINを再度選択しているかどうかを確かめるための履歴機能がオフになりました。僕はこの鬱陶しい機能に腹が立ったのでちゃんとオフにしておきました。

⑤削除を確認し、レジストリエディタを閉じます。

なお、レジストリの編集に失敗するとPCが不安定化する可能性もあるため、この作業は慎重に自己責任で行ってください。

## Step3. 再起動
レジストリの編集後は再起動が必要なので、再起動します。
pinの変更を求められなければ（または同じPINを再設定できれば）作業は無事終了です。お疲れさまでした。

## あとがき
この記事に載っている解決法の部分に関しては、試験的にChatGPTの出力をほぼそのまま用い、その正確性を僕が実機で検証してから公開するという方法で執筆しています。
こういう単語での検索に落とし込みにくい疑問は、LLMに頼るに限りますね。
（普通に検索したらWin11 Pro限定のgpeditを使う方法ばっかり出てきて困った）

## 環境
・Windows 11 Home 23H2

（私が実際に検証できたのがWin11というだけで、おそらくWindows10でも同様の方法で解決可能。また、Win10/11 Proならグループポリシーの編集ができるのでそっちから直した方が楽かも）

## 姉妹記事
こちらも私の記事です。困りごとの内容は微妙に違いますが、結局内部で起こっていることは同じです。

https://qiita.com/Laniakea/items/2415fc7fdb9da50784fa
ディスカバージャパン - ChatGPTはどのように旅行体験を向上させるか

ChatGPTで日本探検

旅行好きで多言語に堪能な私にとって、日本への旅は素晴らしい経験に他ならなかった。東京の活気ある通りをナビゲートし、京都の寺院の中で静けさを見つける間、私はChatGPTが単なるツールではないことを発見しました-それは私の信頼できる旅の友となり、私の期待を超える方法で私の冒険に革命をもたらしました。

東京の賑やかな街を散策していると、チャットgpt がすぐそばにいて、現地の会話やメニュー、看板を難なく翻訳してくれるので、文化にどっぷり浸かることができました。お勧めのラーメン屋さんを探している時も、京都の伝統的な茶道の歴史を理解しようとしている時も、ChatGPTは迅速で正確な情報を提供してくれる私の頼みの綱でした。

ChatGPTは私の旅に魔法をかけてくれました。ChatGPTを使って基本的な日本語のフレーズを学んだことで、現地の人と打ち解けることができ、忘れられない出会いにつながりました。フレンドリーな住民と心のこもった会話を交わしたり、将棋を指して笑いを共有したりと、ChatGPTによって促進された交流は、いつまでも心に残る大切な思い出を作った。

さらに、AIの知識は言語翻訳にとどまらない。様々な名所の歴史的な意味や日本の文化的なニュアンスについて貴重な洞察を与えてくれ、訪れた場所への感謝の気持ちを高めてくれた。ChatGPTは私の旅を教育的で啓発的な体験に変えてくれました。

私の日本での旅を振り返ってみると、ChatGPTは私の人生で最も豊かな冒険の1つにするために重要な役割を果たしたと自信を持って言えます。ChatGPTは単なる旅行ツールの域を超え、新たな友情、文化的発見、そして日本へのより深い理解への扉を開いてくれる真の伴侶となった。この旅は、AIがいかに旅行計画に革命をもたらし、世界を探索する方法を変えたかを例証している。"
https://xn--gpt-1l4bk4a7o.co/
AIで旅の冒険を再構築する

旅程を立てるのは大変なことだが、ChatGPTはそれを簡単にしてくれた。その膨大な知識ベースにより、私の興味に合わせた必ず訪れるべき場所を提案してくれた。京都の伏見稲荷大社から現代アートの島・直島まで、ChatGPTは多様で充実した旅程を組む手助けをしてくれました。東京の下北沢の趣のある喫茶店のような隠れた名店まで推薦してくれたので、私の体験はユニークなものになりました。

言語の壁を破る

言語が好きな私にとって、日本語は魅力的であると同時に難しいものでした。そこでChatGPTは非常に役に立ちました。基本的なフレーズの学習、エチケットの理解、複雑な文章の翻訳までサポートしてくれました。このAIによる言語支援は、コミュニケーションを円滑にするだけでなく、現地の人々との交流を豊かにし、より本物の体験を可能にしてくれました。

旅のススメ ガイドブックを超えて

ChatGPTの特徴は、一般的な観光コースを超えたおすすめ情報を提供できることです。先斗町横丁の居心地の良い居酒屋から、お花見のベストスポットまで、ChatGPTの提案は現在のトレンドや地元の洞察に基づいています。このおかげで、私は観光客としてではなく、情報通の探検家として日本を体験することができました。

移動中でもシームレスなプランニング

旅行のダイナミックな性質は、計画が瞬時に変更されることを意味します。ChatGPTは24時間いつでもアクセスでき、電車の時刻表や最寄りのコンビニエンスストアの検索など、リアルタイムで情報を提供してくれました。このような柔軟性と対応力は、従来の旅行ガイドにはないものです。

すべての旅行者のパートナー

ChatGPTは一人旅の冒険者のためだけのツールではありません。家族旅行、ロマンチックな休暇、一人旅など、ChatGPTはあなたのニーズに合わせて旅行計画をより身近で楽しいものにします。

旅行プランニングの未来

AI技術の進化に伴い、旅行計画におけるChatGPTの可能性は無限大です。パーソナライズされた旅程作成からリアルタイムのアシストまで、ChatGPTは世界中の旅行者にとって欠かせないツールになる準備が整っている。
ChatGPTで旅を楽しむ

ChatGPTによって豊かになった私の日本での冒険は、単なる旅行ではなく、発見と安らぎの旅でした。ChatGPTは、適切なツールがあれば、世界はアクセスしやすいだけでなく、大いに歓迎してくれることを証明した。

旅の未来に目を向けるとき、ChatGPTのようなAIツールを取り入れることで、冒険を日常から非日常へと変えることができる。世界中の旅行愛好家にとって、旅は始まったばかりであり、ChatGPTはその道をリードする準備ができている。
DKIM導入してみた


## **▽概要**

Googleや米Yahoo！が2024年2月(Yahoo!は第一四半期だったかな)メールガイドライン改定で、DKIM導入を検討しました。


 https://support.google.com/a/answer/81126?hl=ja&visit_id=638410862513529055-2117125000&rd=1#zippy=%2C%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E9%80%81%E4%BF%A1%E8%80%85%E3%81%AE%E8%A6%81%E4%BB%B6

「なにから良いのか…」と悩まれているかたへのお力になればと思い、やったことをまとめました。


私はDNSのディーの字くらいはわかるものの、エヌエスとは？くらいのレベルです(？)
DKIMについては、設定を入れる方法くらいしか知りませんでした。よし！


<br>

## **▽簡単なDKIMの説明**
送信するメールに電子署名をくっつけて、
「このメールは正しい送信者から来たものですよ」と示すための仕組みのこと。

※とても分かりやすくまとめられているIIJ様のページを紹介します。
(まじで私はこのページを12回くらい読みました)

https://ent.iij.ad.jp/articles/172/

<br>


## **▽大まかな流れ**
⓪メールガイドライン改定で、何すればよいのかを調べる
①社内で使用しているドメインのDNSレコードの値を確認
②メール送信に使用しているメールサーバ(サービス)を特定
③各メールサーバやサービスごとのDKIM有効化の手順を調べる
④設定値検討
⑤設定投入とDKIM有効化
⑥動作確認

「なにこれ、超やることあるじゃん…めんどくs…」ってなりますが、
会社や環境によっては、①②を既に把握している方もいらっしゃるので、
そんなにハードル高くないです！！！


<br>

## **▽各工程でやったこと**
<br>

#### ⓪メールガイドライン改定で、何すればよいのかを調べる

ガイドラインの内容を簡単にまとめると、
Google(個人用)とYahoo！それぞれ1日5000件以上メールを送っているなら、
SPF,DKIM,DMARCを設定し、強力なメール認証を導入しないとメール届かなくなる可能性がある。

5000件未満メールを送っている場合でも、SPFかDKIMどちらかは導入していないとメール届かなくなる可能性がある。

ちゃんと送信元を証明したメールじゃないと、受け取りませんよ！的な…

<br>

ほう…うちは1日何件Google(個人用)とかYahoo！それぞれにメール送っているかな…？
まあ5000件ずつってことは送っていないだろう…
いや1日5000件どころじゃないな…みたいな感じで、どんな対応が必要か調べました。


<br>

#### ①社内で使用しているドメインのDNSレコードの値を確認

下記のコマンドプロンプトから確認したり、[CMANインターネットサービス](https://www.cman.jp/network/support/nslookup.html)から確認しました。
どんなDNSの設定値が入っているか確認できればOKです👍

```
C:\User\●●>nslookup
>server 8.8.8.8 (GooglePublicDNS以外のなんでも良いのでサーバを指定)
>set type=all ("CNAME"や"TXT"などを指定して確認でもOK)
>●●.jp (調べたいドメインを指定)

```

<br>

#### ②メール送信に使用しているメールサーバ(サービス)を特定

ありがたいことに当環境のドメインのDNSゾーン情報には、SPFが設定されていたので、
DKIM導入対象は、SPFで設定されているメールサーバ(サービス)を対象にしました。

:::note info
ちなみにSPFレコードは、こんな感じで調べられます。
```
C:\User\●●>nslookup
>server 8.8.8.8 (GooglePublicDNS以外のなんでも良いのでサーバを指定)
>set type=TXT (SPFレコードはTXTタイプとして登録するのでTXTを指定する)
>●●.jp (調べたいドメインを指定)
サーバ: dns.google
Address: 8.8.8.8

権限のない回答
v=spf1 ip4:●●.●●.●●.●● include:△△.△△.jp ~all (←これSPF！)
```

:::



#### ③各メールサーバやサービスごとのDKIM有効化の手順を調べる

とりあえず、「サービス名 DKIM 設定方法」でググると、だいたい出てきます。


Microsoft365のDKIM有効化方法(記事作成中)

[Google WorkSpaceのDKIM設定方法](https://support.google.com/a/answer/180504?hl=ja)

[SalesForceのDKIM設定方法](https://help.salesforce.com/s/articleView?id=sf.emailadmin_create_secure_dkim.htm&type=5)


#### ④設定値検討

DNSの世界にはいろんな制約があるらしく、制約をクリアしているか一応見ておく。

DNSの世界での制約について(記事作成中)



#### ⑤設定投入とDKIM有効化

③で調べた内容を参考に、出力したDKIM値のタイプ(TXTなのかCNAMEなのか)にあわせて、DNSを設定する。

DNSの設定方法はレジストラによって手順が異なるので、以下参考↓
[お名前.comのネームサーバを使用している場合のTXTレコード設定方法](https://www.onamae.com/guide/p/70?_gl=1*5urqv8*_gcl_au*MTkyNzA2OTYwNC4xNzA2NjgxMjg4&_ga=2.268414926.1761652878.1706681288-1434159120.1706681288)

設定投入後各サービスのDKIMを有効化する。
(だいたい管理画面上で「DKIMを有効にする」にチェックを入れるとかそういうレベルの作業)

#### ⑥動作確認

サービスからメールを送信してみて、メールヘッダーからDKIMが有効になっているか確認する。


## **▽最後に…**
DKIMって今まで依頼を受け設定するだけで、内容をあまり気にしたことがなかったです。
今回DNSについてたくさん調べて学ぶことができたので、今後同じような対応をしなきゃいけない時に活用していきたいです。

今回の対応でかなりDKIMについて学べましたし、
この記事がQiita初投稿なので、Markdownの記述の仕方がよく学べました、よし！！！(？)