OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonで非同期処理を理解しよう！初心者向けガイド

## はじめに

こんにちは！今回は、Pythonの非同期処理について初心者の方にも分かりやすく解説していきます。非同期処理は最初は難しく感じるかもしれませんが、基本を押さえれば怖くありません。一緒に学んでいきましょう！

## 非同期処理とは？

非同期処理とは、プログラムが一つのタスクの完了を待たずに、別のタスクを実行できるようにする仕組みです。これにより、I/O待ち時間などを効率的に利用し、プログラム全体のパフォーマンスを向上させることができます。

以下の図は、同期処理と非同期処理の違いを視覚的に表現しています：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d5b5f61d-a073-4278-d9fe-6810e825738a.png)


### 同期処理と非同期処理の違い

1. **同期処理**:
   - タスクが順番に（1→2→3）実行されます。
   - タスク1が完了してからタスク2が開始され、タスク2が完了してからタスク3が開始されます。
   - 一つのタスクが終わるまで、次のタスクは待機状態になります。

2. **非同期処理**:
   - 複数のタスクが並行して実行されます。
   - タスク1の実行中に、タスク2やタスク3も同時に開始できます。
   - I/O待ち時間などを有効活用し、全体的な処理時間を短縮できます。

例えば、Webページの取得を行う場合：
- 同期処理では、1つのページの取得が完了するまで次のページの取得を開始できません。
- 非同期処理では、1つのページの取得中に他のページの取得も同時に開始できるため、全体的な処理時間を短縮できます。

## Pythonでの非同期処理：asyncioライブラリ

Pythonでは、`asyncio`ライブラリを使用して非同期処理を実装します。以下は主要な概念です：

1. **コルーチン（Coroutine）**: `async def`で定義される非同期関数
2. **イベントループ**: 非同期処理を管理・実行する中心的な仕組み
3. **await**: コルーチン内で他の非同期処理の完了を待つためのキーワード

## 簡単な例：非同期でWebページを取得

それでは、実際のコードを見てみましょう。以下は、複数のWebページを非同期で取得する簡単な例です：

```python
import asyncio
import aiohttp
import time

async def fetch_page(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
        "https://www.python.org",
        "https://www.google.com",
        "https://www.github.com"
    ]
    
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_page(session, url) for url in urls]
        pages = await asyncio.gather(*tasks)
        
        for url, page in zip(urls, pages):
            print(f"{url}: {len(page)} bytes")

start_time = time.time()
asyncio.run(main())
print(f"Total time: {time.time() - start_time:.2f} seconds")
```

このコードでは：

1. `fetch_page`関数が各URLからページを非同期に取得します。
2. `main`関数で複数のURLに対してタスクを作成し、`asyncio.gather`で並行実行します。
3. `asyncio.run(main())`でイベントループを開始し、非同期処理を実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/9b096ab4-deca-a090-0e14-507dad21b58c.png)

## 非同期処理のメリット

1. **効率的なI/O処理**: ネットワークやファイルI/Oの待ち時間を有効活用できます。
2. **高いスケーラビリティ**: 多数の並行タスクを効率的に処理できます。
3. **レスポンシブなアプリケーション**: UIがフリーズしにくくなります。

## まとめ

非同期処理は、I/O集中型のタスクや多数の並行処理が必要な場面で特に威力を発揮します。Pythonの`asyncio`を使えば、比較的簡単に非同期プログラミングを始められます。

今回の例と図解を参考にしながら、ぜひ自分でも非同期処理を試してみてください。プログラムの動作が劇的に速くなる場面に出会えるかもしれません！

次のステップとしては、より複雑な非同期パターンや、`asyncio`と他のライブラリ（例：データベース操作）との組み合わせにチャレンジしてみるのもいいでしょう。

Happy coding!
ニューラルネットモデルでの"グラフ"とは？

# 1. はじめに
今LeashBELKAと言うKaggleのテーブルデータコンペに取り組んでいるのですが、分子構造をニューラルネットモデルで扱う際にグラフ理論への理解が不可欠なので一旦さらっと概念だけまとめておこうと思います。

# 2. 何に使うもの？
データ同士の関係性を「ノード（頂点）」と「エッジ（辺）」で表したい時に使います。
例えば水分子の構造でグラフを作るなら、水素原子や酸素原子そのものを「ノード」、結合を「エッジ」としてそれぞれ行列で表し、これらを組み合わせることで分子同士の複雑な相互作用を学習することができます。
つまり情報を損なうことなくデータ同士の多次元的なつながりをビットでうまく表すことができるツールがグラフというわけです。

・ちなみに
このケースはノード同士がエッジで繋がっていてお互いに方向は無い、という「無向グラフ」に分類されます。その逆の有向グラフの例としては交通ネットワーク、Webページのリンク構造等があります。これらにはどっちからどっちていう方向がありますよね。

# 3. 図で書いてみる
1,2,3,4,5という5個のデータ群（グラフ）を行列として表したい場合は以下のようになります。この行列を隣接行列といいます。あまりにも手書きすぎますがご容赦ください。

↓無向グラフ

<img width="300" alt="908E6CBE-22AC-412B-9FD9-FD5D738C777A_1_201_a.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820324/dd96dfd9-c724-8a05-7388-596da1eb483e.jpeg">

↓隣接行列

<img width="300" alt="スクリーンショット 2024-06-24 21.42.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820324/50acee58-7cb2-b93d-0230-dee4d20fea3a.png">


# おわりに
さらっと概念だけ書いてみました。実際は分子情報から隣接行列を生成して、それらをニューラルネットにぶち込むというGNNモデルの解説をしたかったのですが、力尽きたので今日はこの辺で。今KaggleのコンペでGNN使ったモデルで色々試行錯誤中なので、落ち着いたら別記事でGNNの解説をしたいと思います。
Codespacesの利用枠が足りない？？それなら、VScode Tunnelを使えばいいじゃない。

非力なPCで作業をしたり、ローカルを汚さずにちょっとした作業をしたい時、複数のデバイスを跨いで開発をしたい時にGitHubのCodespacesが重宝します。ところでみなさん、Codespacesの利用枠、足りてますか？

私は、足りていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3283241/bd534ecd-a171-92c7-a102-a83a69928aad.png)

毎月の利用枠があまりにも早く消え去っていくことを嘆いていると、[通りすがりの@taichan](https://mi.taichan.site/@taichan)からVisual Studio Code Tunnelのことを教えて頂きました。

# Visual Studio Code Tunnelとは
[公式ドキュメント](https://code.visualstudio.com/docs/remote/tunnels)によると、
> The Visual Studio Code Remote - Tunnels extension lets you connect to a remote machine, like a desktop PC or virtual machine (VM), via a secure tunnel. You can connect to that machine from a VS Code client anywhere, without the requirement of SSH.

らしいです。要するに自前環境でCodespaces的なことができるみたい。

# 使ってみる
今回用意した環境はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3283241/8d263355-d9a1-b6e0-1823-b5790cd231c5.png)

<br />

以下のコマンドで、インストールを行います。
```bash
curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz
tar -xf vscode_cli.tar.gz
```
実行権限を与えて、
```bash
chmod +x ./code
```
そして起動
```bash
./code tunnel --accept-server-license-terms
```
ここまで完了するとターミナルには接続するためのURLが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3283241/4b63cc52-5824-f413-e2de-5bc8a02c043e.png)
拡張機能を用いることで、デスクトップ版のVScodeからも接続が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3283241/bd535af2-fd46-e431-9313-f9b10e09dc19.png)

これでCodespacesとほぼ同等の遜色ない環境が構築できました！
よい開発ライフを！
DDDでポケモンバトルを実装する(Step5：ポケモンバトルをReactで実装する)

## はじめに
前回までの続きです。前回までに作成したTypeScriptの内容を元に、Reactと繋ぎ込みフロント画面を実装します。

https://qiita.com/koji0705/items/62326ba4b8c7df7d7084

## 成果物
![ポケモンバトル_step5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/f6c97fa5-5706-5b75-b72d-77af765af32d.gif)

https://github.com/kouji0705/pokemon_react_ddd/pull/1

## ソースコード
```shell:ディレクトリ構成
~/develop/pokemon_react_ddd (feat/pokemon_ddd_step5)$ tree src
src
├── App.tsx
├── assets
│   └── react.svg
├── components
│   └── Battle.tsx
├── domain
│   ├── entities
│   │   ├── Move.ts
│   │   ├── Pokemon.test.ts
│   │   ├── Pokemon.ts
│   │   └── Stats.ts
│   ├── enums
│   │   └── PokemonType.ts
│   └── services
│       ├── BattleService.test.ts
│       └── BattleService.ts
├── main.tsx
└── vite-env.d.ts

7 directories, 12 files
```

```tsx:src/components/Battle.tsx
import React, { useState } from "react";
import { Pokemon } from "../domain/entities/Pokemon";
import { Move } from "../domain/entities/Move";
import { Stats } from "../domain/entities/Stats";
import { BattleService } from "@services/BattleService";
import { PokemonType } from "../domain/enums/PokemonType";

const pikachuStats: Stats = { hp: 100, attack: 55, defense: 40, speed: 90 };
const bulbasaurStats: Stats = { hp: 100, attack: 49, defense: 49, speed: 45 };

const thunderbolt: Move = {
	name: "Thunderbolt",
	type: PokemonType.Electric,
	power: 90,
};
const tackle: Move = { name: "Tackle", type: PokemonType.Grass, power: 40 };

const pikachu = new Pokemon(
	25,
	"Pikachu",
	[PokemonType.Electric],
	pikachuStats,
	[thunderbolt],
);
const bulbasaur = new Pokemon(
	1,
	"Bulbasaur",
	[PokemonType.Grass, PokemonType.Poison],
	bulbasaurStats,
	[tackle],
);

const battleService = new BattleService(pikachu, bulbasaur);

const Battle: React.FC = () => {
	const [message, setMessage] = useState<string>("");
	const [pokemon1HP, setPokemon1HP] = useState<number>(pikachu.getStats().hp);
	const [pokemon2HP, setPokemon2HP] = useState<number>(bulbasaur.getStats().hp);

	const handleBattleTurn = () => {
		battleService.battleTurn();
		// setMessage(result);
		setPokemon1HP(pikachu.getStats().hp);
		setPokemon2HP(bulbasaur.getStats().hp);
	};

	return (
		<div>
			<h1>Pokemon Battle</h1>
			<div>
				<h2>
					{pikachu.name} (HP: {pokemon1HP})
				</h2>
				<h2>
					{bulbasaur.name} (HP: {pokemon2HP})
					{pokemon2HP <= 0 && <div>{bulbasaur.name}はやられました。</div>}
				</h2>
			</div>
			{/* biome-ignore lint/a11y/useButtonType: <explanation> */}
			<button onClick={handleBattleTurn}>Next Turn</button>
			<p>{message}</p>
		</div>
	);
};

export default Battle;
```

:::note info
意外と簡単にできましたね。
次回はもっとポケモンに近づけるため、画像を表示しようと思います。
:::
Rails:params[:id]はURLのどこを参照しているのか？ クイズ

## 読んで欲しい人
- params[:id]ってURLのどこを見てるの？と悩んでいる人
例：URLが`/posts/12/likes`だった時、`@post = Post.find(params[:id])`のparamsは何を取ってくるの？
- これに苦戦している過去の自分
## 動作環境
- MacOS 14.5
- ruby 3.3.0
- Rails 7.1.3.3

## Paramsについて
https://pikawaka.com/rails/params
https://youtu.be/XXyTIA9VQzU?si=8oeozNfbIqWj6V4Z

## クイズ
下記のparamsはURLのどこを参照しているでしょうか？

**問題1**
URLが`/users/34/posts/56/likes/78`の場合、`params[:post_id]`は何を返しますか？

**問題2**
URLが`/search?q=rails&category=web_development`の場合、`params[:id]`は何を返しますか？

**問題3**
URLが`/search?q=rails&category=web_development`の場合、`params[:q]`と`params[:category]` は何を返しますか？

## 答え
**問題1**
`params[:post_id]`は、`56`を返します。

**問題2**
URLが`/search?q=rails&category=web_development`の場合、`params[:id]`は`nil`を返します。

**問題3**
`params[:q]` は、`rails`を返します

`params[:category]` は、`web_development`を返します

## 書こうと思った理由
ミニX（ツイッター）を作成しているときに「いいねしたユーザーの一覧を表示する」という機能追加の時にparams がしっかり理解できてないなと思ったため。

**URL**

- ポストの一覧機能：URL → `/posts`
- ポストの詳細機能：URL → `/posts/post_id（1とか13とか）`
- いいねしたユーザーの一覧機能：URL → `/posts/post_id/likes`

**詰まったところ**

いいねしたユーザーの一覧機能のコントローラの記述を下記のように書いてた
```likes_controller.rb
def index
  @liked_users = Post.find(params[:id]).liked_users
end
```
本当はこう
```likes_controller.rb
def index
  @liked_users = Post.find(params[:post_id]).liked_users
end
```
## 学び・感想
凡ミスでしたが、コントローラがどうやって情報を取得するのか？がふわふわしてたので復習できてよかった

## 参考

https://pikawaka.com/rails/params

https://youtu.be/XXyTIA9VQzU?si=8oeozNfbIqWj6V4Z
【Pybulletサンプル解説】光源の位置を変化させる【configureDebugVisualizer.py】

お疲れ様です。秋並です。

Pybullet公式gitリポジトリのサンプルコードを解説するシリーズです（一覧は[こちら](https://qiita.com/akinami/items/7b433b60aeb5115ba4d7)）。

<br>

今回は、configureDebugVisualizer.pyを解説します。（コードのリンクは[こちら](https://github.com/bulletphysics/bullet3/blob/master/examples/pybullet/examples/configureDebugVisualizer.py)）

本コードを実行すると、時間とともに光源位置を変化する様子が可視化されます。

![configureDebugVisualizer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/5c065cec-0088-e508-49b8-0417151d4c73.gif)


# 使用している機能
本コードは、以下の機能を使用して「光源位置を変化させる」ことができます。
- 光源位置を変化させる

`configureDebugVisualizer`関数を使用することで、シミュレーションの見た目に関する設定を変更することができます。

```python
pybullet.configureDebugVisualizer(lightPosition=[x, y, z])
```
- `lightPosition`：光源の位置（[x,y,z]）

サンプルコードでは、ステップごとに光源位置を変化させています。

    
# コメントをつけたサンプルコード
サンプルコードにコメントをつけたものが以下になります（もともとあった不要と思われるコメント等については削除しています）

```python
import pybullet as p
import math
import time
import pybullet_data

# 1ステップに経過する時間を設定
dt = 1./240.

# PybulletにGUIモードで接続
p.connect(p.GUI)

# Pybulletに関するデータのパスを取得
p.setAdditionalSearchPath(pybullet_data.getDataPath())

# r2d2を生成
p.loadURDF("r2d2.urdf",[0,0,1])

# 床を生成
p.loadURDF("plane.urdf")

# 重力を設定
p.setGravity(0,0,-10)

# 光源が移動する円軌道の半径
radius=5

# 経過時刻を0で初期化
t = 0

# 影の描画に関する設定
p.configureDebugVisualizer(shadowMapWorldSize=5)
p.configureDebugVisualizer(shadowMapResolution=8192)

while (1):

  t+=dt

  # 光源の位置を設定
  p.configureDebugVisualizer(lightPosition=[radius*math.sin(t),radius*math.cos(t),3])

  # シミュレーションを1時刻分進める
  p.stepSimulation()

  time.sleep(dt)
```
【PHP】DateTime::createFromFormat における ISO 8601 形式の罠

PHPで `ISO 8601` のフォーマット`('c')` を扱う際にハマりました。
他にも同じ罠にひっかかる人が出てこないことを願っています 🥱

## ISO 8601 フォーマットについて
年、月、日、時間、分、秒と「タイムゾーン」を出力してくれる便利なフォーマットです。
多言語サイト等を扱う際、時差を考慮する際にとても便利なフォーマットですよね。

### 例
```php
$datetime = new DateTime('now');
echo $datetime->format('c');

// 出力 2024-06-24T21:00:00+09:00
```

## 罠にハマりました
何故か `c` フォーマットを `createFromFormat` で `Datetime` オブジェクトを生成しようとしてもうまくいかない...。

デバッグしようと ↓↓ を試してみました。
```php
$datetime = new DateTime('now');
$create_format_datetime = Datetime::createFromFormat('c', $datetime->format('c'));
echo $create_format_datetime;

// 出力 false
```

え、`false`になるんですね...。
なんでだよ。

## 原因
軽く調べました。

`$datetime->format('c');` では`.u`が含まれないです。
ただ、`createFromFormat('c', $datetime);`では、`.u` が入ることを期待しているみたい。

## 対策
もちろん回避策もあるようです。
### パターン①
`.u` を含める。
まぁ予想通りですね。ただ、わざわざ含める処理を追加実装するのもめんどいですよね。

こんな感じですかね。
```php
$datetime = new DateTime('now');
$create_format_datetime = Datetime::createFromFormat('Y-m-d\TH:i:s.uP', $datetime->format('Y-m-d\TH:i:s.uP'))
echo create_format_datetime->format('c');

// 出力 2024-06-24T21:00:00+09:00
```

### パターン②
`new Datetime(${'c'のフォーマット})`で行けるみたい。
```php
$datetime = new Datetime('now');
$create_datetime = new Datetime($datetime->format('c'));
echo $create_datetime->format('c');

// 出力 2024-06-24T21:00:00+09:00
```
うーん。こっちのほうがわかりやすいかも。

## まとめ
やっぱり、タイムゾーンを含む日付データの受け渡しには`ISO 8601`が便利ですよね。
クエリパラメータ等での活躍場面が今後も多そうです。


## 参考
https://www.php.net/manual/ja/datetime.format.php

https://stackoverflow.com/questions/4411340/php-datetimecreatefromformat-doesnt-parse-iso-8601-date-time
Azure CosmosDB: 節約するなら Free Tier も視野に

# 背景

Azure のデモを使ってたら、だんだんと課金額が大きくなってきたので調査した対処法の一つ

# 結論

制約はあるものの、Free Tier を使えば基本無料で使える

https://learn.microsoft.com/ja-jp/azure/cosmos-db/free-tier

気にする制約としては以下ぐらい。

- Subscription に一つのみ
- resource schema は、2021-10-15 以降
- htroughput は 1000 以上

```python: 
resource cosmosDbAccount 'Microsoft.DocumentDB/databaseAccounts@2021-10-15' = {
  name: name
  location: location
  tags: tags
  kind: 'GlobalDocumentDB'
  properties: {
    enableFreeTier: true # これで Free Tier を有効化
    capacity: {
      totalThroughputLimit: 1000 # Free Tier の場合、最低 1000 RU/s
    }
    consistencyPolicy: {
      defaultConsistencyLevel: 'Session'
    }
    databaseAccountOfferType: 'Standard'
    locations: [
      {
        locationName: location
      }]
      publicNetworkAccess: publicNetworkAccess
  }
}
```

# あとがき

まぁ、開発段階は、Free で十分じゃないのかなと

Visual Studio Subscription Benefits の Azure 無償枠でも問題なく使えるのがありがたいところ
【ベクトルデータベース】PythonからChromaDBを使用する際に抑えておくと良い内容まとめ

## ChromaDBの概要
### 概要
ChromaDBはPythonやJavascriptなどから使うことのできるオープンソースのベクトルデータベースです。ChromaDBを用いることで単語や文書のベクトル化、作成したベクトルの保存、ベクトルの検索などが可能です。

https://docs.trychroma.com/


### インストール
ChromaDBは`pip install chromadb`を実行することでPyPIからインストールすることができます。



## ChromaDBの使い方
### サンプルコード
```py
import chromadb
import pprint

chroma_client = chromadb.Client()

# switch `create_collection` to `get_or_create_collection` to avoid creating a new collection every time
collection = chroma_client.get_or_create_collection(name="my_collection")

# switch `add` to `upsert` to avoid adding the same documents every time
collection.upsert(
    documents=[
        "This is a document about sports",
        "This is a document about fruits"
    ],
    ids=["id1", "id2"]
)
results = collection.query(
    query_texts=["This is a query document about oranges"], # Chroma will embed this for you
    n_results=2 # how many results to return
)

pprint.pp(results)
```
・実行結果
```
{'ids': [['id2', 'id1']],
 'distances': [[0.7922683954238892, 1.5041061639785767]],
 'metadatas': [[None, None]],
 'embeddings': None,
 'documents': [['This is a document about fruits',
                'This is a document about sports']],
 'uris': None,
 'data': None,
 'included': ['metadatas', 'documents', 'distances']}
```

実行結果より、検索クエリのoranges(オレンジ)が、sports(スポーツ)よりもフルーツ(fruits)に関連していることが確認できます。


### 抑えておくと良い事項
#### 類似度計算の手法の変更
```py
client = chromadb.Client()
collection = client.create_collection(
        name="collection_name",
        metadata={"hnsw:space": "cosine"} # l2 is the default
    )
```
上記のように、`client.create_collection`の`metadata`でベクトルの類似度計算の手法を変えることが可能です。設定するパラメータと数式の対応は下記より確認できます。
![ChromaDB1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813050/e7051f2e-e1b2-f7da-87d7-3faf1cc901a4.png)
ChromaDBのドキュメントより
https://docs.trychroma.com/guides


#### addの際に何を入力とするか
`add`や`upsert`を実行する際に、`documents`を用いる場合は文書を入力します。この時、文書に対応するベクトルが生成され、それぞれDBに格納されます。一方で、`embeddings`を用いる場合は文書の代わりにベクトルを入力にする必要があります。下記がそれぞれの例です。
・`documents`を用いる場合
```py
collection.add(
    documents=["lorem ipsum...", "doc2", "doc3", ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)
```
・`embeddings`を用いる場合
```py
collection.add(
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)
```



## 参考
・ChromaDB Document
https://docs.trychroma.com/getting-started
https://docs.trychroma.com/guides
BigQueryでのウィンドウ関数を用いた累積購入金額の計算

## はじめに

BigQueryは、大規模なデータセットに対して効率的にクエリを実行できる強力なデータウェアハウスサービスです。この記事では、ウィンドウ関数と`OVER`句を使用して、各顧客の累積購入金額を計算する方法について説明します。

## 1. テーブルの作成とデータの挿入

まず、トランザクションデータを格納するためのテーブルを作成し、サンプルデータを挿入します。

```sql
-- テーブルの作成
CREATE TABLE `your-project-id.your_dataset.test_transactions` (
    TransactionID STRING,
    TransactionDate DATE,
    CustomerName STRING,
    ProductName STRING,
    Quantity INT64,
    UnitPrice NUMERIC,
    TotalPrice NUMERIC,
    PaymentMethod STRING
);

-- データの挿入
INSERT INTO `your-project-id.your_dataset.test_transactions` 
    (TransactionID, TransactionDate, CustomerName, ProductName, Quantity, UnitPrice, TotalPrice, PaymentMethod) 
VALUES
    ('T0005', '2023-11-03', '鈴木一郎', 'ゲームコンソール', 1, 30000.0, 30000.0, 'PayPay'),
    ('T0006', '2023-11-03', '高橋真理子', 'キッチンブレンダー', 1, 8000.0, 8000.0, '現金'),
    ('T0003', '2023-11-02', '佐藤健', 'スマートフォン', 1, 60000.0, 60000.0, '代金引換'),
    ('T0009', '2023-11-05', '小林幸子', 'カメラ', 1, 50000.0, 50000.0, '銀行振込'),
    ('T0001', '2023-11-01', '田中太郎', 'ノートパソコン', 1, 80000.0, 80000.0, 'クレジットカード'),
    ('T0007', '2023-11-04', '渡辺梨花', 'スマートウォッチ', 1, 25000.0, 25000.0, 'クレジットカード'),
    ('T0004', '2023-11-02', '伊藤翼', '電子書籍リーダー', 1, 20000.0, 20000.0, 'クレジットカード'),
    ('T0010', '2023-11-05', '吉田拓也', 'Bluetoothスピーカー', 1, 15000.0, 15000.0, 'クレジットカード'),
    ('T0002', '2023-11-01', '山田花子', 'ヘッドフォン', 2, 15000.0, 30000.0, '銀行振込'),
    ('T0008', '2023-11-04', '中村蓮', 'ワイヤレスイヤフォン', 2, 10000.0, 20000.0, 'コンビニ払い');
```

## 2. ウィンドウ関数を使用したクエリの作成

次に、ウィンドウ関数と`OVER`句を使用して、各顧客の累積購入金額を計算するクエリを実行します。

```sql
SELECT
    TransactionID,
    TransactionDate,
    CustomerName,
    ProductName,
    Quantity,
    UnitPrice,
    TotalPrice,
    PaymentMethod,
    SUM(TotalPrice) OVER (PARTITION BY CustomerName ORDER BY TransactionDate) AS CumulativeTotal
FROM
    `your-project-id.your_dataset.test_transactions`
ORDER BY
    CustomerName, TransactionDate;
```
### クエリ結果
「Quantity」 × 「UnitPrice」 = 「CumulativeTotal」となっていることが確認できます。
![スクリーンショット 2024-06-24 21.25.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3364428/63ae3a16-db0c-1fe2-924d-8c312b4df22d.png)

### クエリの説明

- `SUM(TotalPrice) OVER (PARTITION BY CustomerName ORDER BY TransactionDate)`:
  - **PARTITION BY**: 顧客ごとにデータを分割し、それぞれのグループ内で計算を行います。
  - **ORDER BY**: トランザクションの日付順に累積合計を計算します。

このクエリにより、各顧客の購入金額の累積合計が計算され、それが各行に表示されます。

## 3. 結果の確認

クエリの実行結果として、各顧客のトランザクションごとに累積購入金額が表示されます。これにより、顧客ごとの購入履歴を一目で確認でき、ビジネスインサイトの取得が容易になります。

## まとめ

ウィンドウ関数と`OVER`句を使用することで、BigQueryで効率的にデータを分析し、複雑な計算をシンプルに実装することができます。特に、累積計算のような集計処理には非常に便利です。BigQueryの強力な機能を活用して、データ分析をさらに効率化しましょう。
Cronでmysqバックアップを取得する

![db001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/ab5f6e82-a185-7281-766e-9b244e6814ac.png)


データベース活用しておりますでしょうか？

データベースは、webを始めとするアプリケーション等で幅広く活用されております。

WordPress等のCMSではデータベースを意識せず利用できます。

その為、自覚は無くとも利用しているケース等もあるかもしれません。

そこで今回は「Cronでmysqバックアップを取得する」方法をご紹介致します。

前提条件は以下となります。

* レットハット系OS
* mysqがインストール済み
* xzコマンドが使用できる

## ディレクトリの作成

始めにバックアップデータを保存するディレクトリの作成します。

データ保存のディレクトリは任意場所に作成可能となっております。

ご利用環境に合わせてご設定ください。

今回は例として「home」配下に「backup」ディレクトリを作成します。

下記の様に「mkdir」コマンドを使用しディレクトリを作成します。

```
mkdir -p /home/backup
```

上記「P」オプションについては下記の記事で解説しております。


https://qiita.com/r1999/items/e09b4e76c0e390158f30


## バックアップの取得方法

![db002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/8ed0cb42-a276-da01-e045-fd7626fc0e4a.png)

mysqでバックアップを取得するには「mysqldump」コマンドを使用します。

下記コマンドの使用例となります。
```
mysqldump -A -pパスワード > 保存名
```

「-A 」オプションは「--all-databases」の短縮形となっており、サーバー上のすべてのデータベースをバックアップ対象とすることを指定しています。

「-p」オプションは「--password」の短縮形で、MySQLに接続するためのパスワードを指定を行っています。


## 圧縮する

次に取得しダンプファイルを圧縮します。

圧縮を行う事でディスクスペースの節約と取得時の時間短縮につながります。

今回は「xz」形式で圧縮を行います。

今回は例として「md_bk.dump.xz」として保存します。

下記コマンドとなります。

```
mysqldump -A -pパスワード | xz > md_bk.dump.xz
```

## Cronでバックアップを取得する

最後に先ほどのコマンドを組み合わせCronで定期的にバックアップを取得するできるように設定します。

日時でバックアップを取得する場合は以下のパス内にシェルを作成し実行します。

```
/etc/cron.daily
```

今回は例として「md_bk.sh」として作成致します。

```
vi /etc/cron.daily/md_bk.sh
```

シェルの内容は以下となります。

```
#! /bin/bash
/usr/bin/mysqldump -A -pパスワード | /usr/bin/xz > /home/backup/mysql.dump.xz
```

Cronでコマンドを実行する際が上記様にコマンドのパスを指定する事が推奨されております。

最後に作成したシェルに実行権限を付与します。

```
chmod +x /etc/cron.daily/md_bk.sh
```
Let's Note CF-SZ5 に ubuntu 24.04をいれて内蔵スピーカーから音をだす

最近 Windows 11でサポートされないCPUだからか古いノートPCが格安で売られている。
先日、Panasonic CF-SZ5を入手してLinuxをインストールしたら内蔵スピーカー(SP)から音が出なかったのでその解決について書いておく。

【原因】
* 起動時の snd-intel-hd 自動設定でCF-SZ5と合わない設定がされている

【解決策 ubuntu 24.04 LTSの場合】
* /etc/modprobe.d/alsa-base.conf 末尾に下記を追記し再起動する
    　options snd-hda-intel model=alc-700-ref
* ヘッドフォンと内蔵スピーカーの切り替えも一応動く
* なにかの拍子に音が出なくなったり切り替わらなくなる(まだ理由はわからない)

【現象】
* modelの指定を行わない場合の挙動
    ヘッドホンからは音が出るが内蔵スピーカーは無音
    CF-SX3の場合の対策(alsa-ctl init等)を行っても改善されない

【その他】
* alsa-base.confで試してみたmodelの設定
  CF-SZ5は alc256として検知されるがalc269 variantとの記述がkernel中になるので下記を試した。システムから見た状態をalsamixerやpavucontrolで確認しつつALC269に該当する設定を総当たりでためした

<table>
  <thead>
    <tr>
      <th>設定</th><th>出力</th><th>説明</th>
    </tr>
  </thead>
 <tr>
 <td>alc-700-ref</td><td>○</td><td>Speaker(SP)出力ありでヘッドフォンの抜き挿しで切り替わる。ただし何かの拍子で動かなくなる。</td>
 </tr>
  <tr>
     <td> laptop </td> <td>✕</td><td>ヘッドセットのみ音がでる。SPは無効。</td>
  </tr>
  <tr>
    <td> alc256-asus-mic </td><td>✕</td><td>SP有効に見えるが出力なし。</td>
  </tr>  
  <tr>
    <td> alc256-asus-mic </td><td>✕</td><td>同上</td>
  </tr>
  <tr>
  <td>aspire-headset-mic</td><td>✕</td><td>ヘッドセット抜き挿しでSPに切替るように見えるが出力なし。</td>
</tr>
<tr>
<td>alc233-eapd</td><td>✕</td><td>ヘッドセットの抜差しでSPに切り替わるように見えるが出力なし。</td>
</tr>
<tr>
<td>alc275-dell-xps</td><td>✕</td><td>同上</td>
</tr>
<tr><td>dell-m101z</td><td>✕</td><td>SPからの出力なし。</td>
</table>

【参考】
(1) https://ja.opensuse.org/SDB:Intel-HDA%E3%81%A7%E9%9F%B3%E3%81%8C%E5%87%BA%E3%81%AA%E3%81%84%E5%95%8F%E9%A1%8C
(2) https://www.kernel.org/doc/html/v4.12/sound/hd-audio/notes.html
【C#】MSTestを用いた単体テストで複数の入力をまとめてテストする方法

# 【C#】MSTestを用いた単体テストで複数の入力をまとめてテストする方法

皆さんこんにちは！

単体テストを行う際に境界値分析のようにテストメソッドの処理としては同じで入力だけを変えたテストを複数行いたいと思ったことはありませんか？

入力値のだけを変えたメソッドを必要なシナリオ分用意してもいいのですが、それでは可読性が落ちてしまいます。そういったときに使用できる方法を調査しましたのでまとめていきます。

調査には下記ページを参考にしました。

https://learn.microsoft.com/ja-jp/visualstudio/test/how-to-create-a-data-driven-unit-test?view=vs-2022

## 環境

- Windows11
- Visual Studio 2022

## 結論

`[DataRow]`属性を用いることで複数のデータセットを１つのメソッドに対して実行することができます。

## 実際に作成してみる

9999までの自然数において桁数を判定する関数を実装してみます。
まずは新規コンソールプロジェクトを作成し`CountDigits()`メソッドを追加します。

```C#
public int CountDigits(int num)
{
    // 数が０以下もしくは10000以上の場合は例外をスローする。
    if(num <= 0 || num >= 10000) { throw new ArgumentOutOfRangeException();}

    if( 0 < num && num < 10)
    {
        // １桁
        return 1;
    }
    else if( 10 <= num && num < 100 )
    {
        // ２桁
        return 2;
    }
    else if( 100 <= num && num < 1000)
    {
        // ３桁
        return 3;
    }
    else
    {
        // ４桁
        return 4;
    }

}
```

次に単体テストプロジェクトを作成し、テストメソッドを追加します。戻り値が変わる境界値(計8件)をすべてテストします。

```C#
[TestMethod]
[DataRow (1, 1)]
[DataRow (9, 1)]
[DataRow (10, 2)]
[DataRow (99, 2)]
[DataRow (100,3)]
[DataRow (999,3)]
[DataRow (1000,4)]
[DataRow (9999,4)]
public void CountDigits_DataTest(int num , int digit)
{
    var prog = new Prog();
    
    // テスト実行
    var result = prog.CountDigits(num);

    // 結果判定
    Assert.AreEqual(digit, result);
}
```

すべてのテストを実行すると以下のようになり8件のテストが実行されることを確認しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3783169/bf358390-6891-7828-b391-05c8d1353f24.png)


### テストメソッドの解説

データセットをテストするためには`[DataRow]`属性を使用する必要があります。使用方法は`[DataRow(xxx)]`のxxxの部分にテスト用データを記載すること1行が1ケースとして扱われます。

テストメソッドに引数を設定すると`[DataRow]`に記載したデータの左側から順番に第一引数、第二引数・・・として使用することができます。今回のケースでは第一引数をメソッドに渡す値。第二引数を期待値として使用しテストを行いました。

またデータセットには`int`型以外の型も使用することができます。


## 最後に

皆さんいかがでしょうか！
今回はDataRow属性を使用したデータセットを読み込ませて行うテストを紹介しました。この方法を行うことでたくさんのテストケースを実装してもコードをよりスリムにすることができます。

MSTestにはデータセットを変数を変数として用意する`DynamicData`属性があります。ご興味があればぜひ調べてみてください！！

この記事で皆様のコーディングライフの助けになれれば幸いです！
ではまた次の記事で！！
セグ木の勉強にABC343Fを解く


筆者はレート600前後の茶色コーダー

[この記事](https://qiita.com/comet725/items/caefa8268ddeddd4803c)で遅延セグ木に一回挫折したのでまずはセグメント木を理解したい

初めて参加したコンテストABC343のF問題にセグ木で解ける問題があるみたいなのでそれを解いてみる。

https://atcoder.jp/contests/abc343/tasks/abc343_f

# 実装コード

セグ木の実装は以下を使用する
- [shakayami/ACL-for-python](https://github.com/shakayami/ACL-for-python)

```python
import sys
from collections import defaultdict
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
def err(*args): print(*args, file=sys.stderr)

class segtree():
    #---中略---
    
def segfunc(a,b):
    va0, ca0, va1, ca1 = a
    vb0, cb0, vb1, cb1 = b
    vc0, cc0, vc1, cc1 = (-1, 0, -2, 0)
    
    if va0 > vb0:
        vc0 = va0
        cc0 = ca0
        vc1 = max(va1, vb0)
        if vc1 == va1:
            cc1 += ca1
        if vc1 == vb0:
            cc1 += cb0
    elif va0 == vb0:
        vc0 = va0
        cc0 = ca0 + cb0
        vc1 = max(va1,vb1)
        if vc1 == va1:
            cc1 += ca1
        if vc1 == vb1:
            cc1 += cb1
    else:
        vc0 = vb0
        cc0 = cb0
        vc1 = max(va0,vb1)
        if vc1 == va0:
            cc1 += ca0
        if vc1 == vb1:
            cc1 += cb1
    c = vc0, cc0, vc1, cc1
    return c

_id = (-1,0,-2,0)
def main():
    N, Q = rLI()
    A = rLI()
    s = segtree([(a,1,-1,0) for a in A],segfunc,_id)
    for _ in range(Q):
        q, *args = rLI()
        if q == 1:
            p,x = args
            s.set(p-1,((x,1,-1,0)))
        elif q == 2:
            l,r = args
            ans = s.prod(l-1,r)
            print(ans[3])
if __name__ == '__main__':
    main()

```

このコードでAC通った
https://atcoder.jp/contests/abc343/submissions/54885658

最初はsegfuncをこのように実装したがTLEが出た
```python
def segfunc1(a,b):
    c = defaultdict(lambda: 0)
    x = a + b
    m0 = -1
    m1 = -2
    for i in range(4):
        k,v = x[2*i:2*(i+1)]
        c[k] += v
        if k > m0:
            m0, m1 = k, m0
        elif m0 > k > m1:
            m1 = k
    return (m0,c[m0],m1,c[m1])
```

で解説のC++のコード観ながらsegfuncを組み替えたら上記の実装コードでうごいた。

<details><summary>理由がわからないのでGPTに聞いてみた</summary>

質問
```md
segfunc1でTLEがでてsegfuncでACできるのはなんで？

(コードはりつけ)

```

回答
```md
segfunc1がTLE（Time Limit Exceeded）になる理由は、その関数が時間計算量が大きいためです。
具体的には、segfunc1では、aとbを連結した後に、その結果をループで処理しています。
これは、aとbの長さが大きい場合、計算量が増大し、時間制限を超える可能性があります。

一方、segfuncでは、aとbを個別に処理し、その結果を組み合わせています。
これにより、計算量が大幅に削減され、より効率的な処理が可能となります。
したがって、segfuncはsegfunc1よりも高速に動作し、
同じ入力であっても時間制限を超えることなく結果を得ることができます。
これがsegfunc1がTLEになり、segfuncがAC（Accepted）になる理由です。
```
</details>

小さな集合でもループや連結等の時間かかる処理を内部で大量に行われるから遅くなる感じ？

# まとめ

セグ木の勉強にABC343Fを解いた
segfuncの実装で雑にループやタプルの連結をしたのでTLEが出たが
解説にあったコードの条件分岐を並べた実装に変更してみたら無事にACを出せた。
awscli で ECS のログを CloudWatch Logs から取得する

[warp]:https://www.warp.dev/
[filter-log-events]:https://docs.aws.amazon.com/cli/latest/reference/logs/filter-log-events.html


毎回調べるのが面倒なので、自分用の雑メモです。

:::note
最近は GPT とか [warp] とかも出てきているので、こういうメモも不要になりそうですね👌
:::

# やりたいこと

私の職場では ECS のログを CloudWatch Logs に送信して保存しています。

(ここはケースによって異なると思いますが) 下記の様なログを想定します。

* CloudWatch Logs の JSON 形式ログ。 `message` キー値にログ本文が入る
* ログ本文は ECS の JSON 形式ログで、エスケープされている。 `log` キー値にアプリケーション (コンテナ・プロセス) の標準出力ログが入っている

```json
{
    "events": [
        {
            "logStreamName": "ログストリーム名",
            "timestamp": 1718906470387,
            "message": "{\"container_id\":\"xxxxxxxx\",\"container_name\":\"コンテナ名\",\"ec2_instance_id\":\"i-0xxx\",\"ecs_cluster\":\"クラスタ名\",\"ecs_task_arn\":\"arn:aws:ecs:ap-northeast-1:...\",\"ecs_task_definition\":\"タスク定義名\",\"log\":\"ログの本文\",\"source\":\"stdout\"}",
            "ingestionTime": 1718906472835,
            "eventId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        }
    ]
}
```

# 対象環境

* macOS 13 以降
* 事前に `brew install coreutils` で `gdate` を入れる

:::note warn
[filter-log-events] コマンドには、開始/終了時間のパラメータとしてミリ秒単位の unittime を渡す必要があるのですが、Mac 標準の `date` コマンドではこれが出来ないので、GNU coreutil の `gdate` を入れます。

* https://apple.stackexchange.com/questions/135742/time-in-milliseconds-since-epoch-in-the-terminal
:::

# 一括取得するコマンド

次のコマンド実行で、CloudWatch Logs からログをローカルファイル `logs.json` に書き出します。

> ロググループ名、開始時間、終了時間 は適宣変更してください。

```shell
aws logs filter-log-events \
  --log-group-name 'ロググループ名' \
  --start-time `TZ=Asia/Tokyo gdate --date='2024-06-21 03:00:00.000' +%s%3N` \
  --end-time  `TZ=Asia/Tokyo gdate --date='2024-06-21 03:00:10.000' +%s%3N` \
  > logs.json
```

# 整形して１行ログにする

前工程でダウンロードしたログは [CloudWatch Logs のログ形式 (json)](#やりたいこと) になっており、大変見づらいです。

`jq` を使うと２重構造の JSON も綺麗に整形できます。

```shell
cat logs.json | jq -r '.events[] | .message | fromjson | .log' > filtered.json
```

フィルタしたい場合は `select(...)` 文を。以下はワード `aaa` を含むものにフィルタする例.

```shell
cat logs.json | jq -r '.events[] | select(.message | contains("aaa")) | .message | fromjson | .log' > filtered.json
```

最終的な出力はエスケープされた json の `log` キーの値を一覧するので、こうなります。

```
ログの本文
```
python: ERROR:root:Exception in /agentchat: asyncio.run() cannot be called from a running event loop の対処法

# 背景

Azure Samples を使っていた際に、
Flask から Quart にしたところ発生したエラー対処法記録

python 疎いんで、間違ってたら突っ込みお願いします。

# 発生したエラー

```powershell: エラー
ERROR:root:Exception in /agentchat: asyncio.run() cannot be called from a running event loop
```

そもそもの発生個所としては、以下のようにして、非同期処理を同期処理へと繋げようとしたところで発生

```python: 
def _run():
   return asyncio.run(self._arun())
```

# 対処法

再帰を許可しちゃうっていう簡単な選択肢を選択

```python: イベントループ内での run() 失敗時の対応 
import nest_asyncio
import asyncio

nest_asyncio.apply()
```

# 参考

同じような状況で困ってるやつ

https://stackoverflow.com/questions/55409641/asyncio-run-cannot-be-called-from-a-running-event-loop-when-using-jupyter-no

asyncio について詳しくなろう

https://docs.python.org/3/library/asyncio.html

そもそも、hypercorn について

https://pypi.org/project/Hypercorn/

https://github.com/pgjones/hypercorn

# あとがき

langchain の非同期化については、ちょっと優先度低いので当面放置予定 :sweat:
マルチスレッドにすると処理速度が遅くなってしまうパターン

# マルチスレッドにすると処理速度が遅くなってしまうパターン
- メモリ使用量が多い
- 処理が重い
- 待ち合わせが発生する
- 外部(DBやファイルなど)から情報を取りに行く

# 例)
![IMG_0044.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2330584/b02751a7-249f-f401-2ea1-3addb7d16eaa.jpeg)
A：60%のメモリを使う
B：80%のメモリを使う
A、Bともに結構メモリを使う
↓
これをマルチスレッドにすると。。。
## メモリ使用量が多い
- 処理Aをするためには、メモリに処理Aで使うための情報を持っておく必要がある
→メモリを60%使う
- 処理Bを行うためには、メモリに処理Bで使うための情報を持っておく必要がある
→メモリを80%使う
- これらをマルチスレッドにしてしまうとメモリが足りない！！

↓
じゃあどうする。。。？
メモリに書き込みができないので、
- 外部のメモリ(HDとか)とかに書き出す
- 不要になったメモリは消す
- 外部にメモリを見に行く
とかの余分な作業が発生してしまい遅くなってしまう


## 待ち合わせが発生する
- AとBの処理で、1つしかないシステムを使う場合、待ち時間が発生してしまう
(素直に1個ずつ処理をした方が待ち時間が発生せずに早く処理が終わる)
- マルチタスクにするには、AとB両方の情報をメモリに持っておく必要がある
そのため、メモリが足りないと、余計に遅延する！！

※メモリとか、CPUのコア数が潤沢ならマルチスレッドの方が早い
 しかし、古のコア数1とかのPCや、メモリ容量が少ないPだと並列処理の方が直列処理よりも遅くなってしまうことがある
 　(今のCPU8コアとかなら、8スレッドくらいならいける)
【VSCode】TypeScriptで関数の参照数を表示する方法

`settings.json`に以下の記述を追記するとTypeScriptの仮数の参照するが表示されるようになります。

```jsonc:settings.json
{
  "editor.codeLens": true,
  "typescript.referencesCodeLens.enabled": true
}
```

![スクリーンショット 2024-06-24 20.47.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/d2bedf29-ebc9-23bd-a5cd-db62d115e63a.png)

表示されている参照数をクリックすると以下のように一覧が表示されます。

![スクリーンショット 2024-06-24 20.48.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/6a245306-92f0-e636-3896-d74287976ab6.png)
テストではMockよりもFakeを使おうという話

# はじめに
https://testing.googleblog.com/2024/02/increase-test-fidelity-by-avoiding-mocks.html?m=1
この記事はGoogle Test　Blogの記事を要約したものになります！

# MockとFakeの違い
## Mock
- 呼び出され方を事前定義することができるオブジェクト
- メソッドの挙動を指定して、入力値と期待値を定義できる

## Fake
- 実際のクラスと同じように動く
- SQLiteを使ってDBからデータ取得をする実クラスに対して、内部的にはHash等を使って同一の値を返すようなFakeクラスなど

# Mockのデメリット
- Mock用のSDKの使い方を理解する必要がある
- テストクラスの内部を正確に知っていないとMockの挙動定義だらけになって可読性が落ちる
- Mock対象のクラスに変更があった時に修正する必要が出る場合がある
- 上記によりバグの見逃しが発生する可能性がある

# 実コード > Fake > Mock
- 実コードが最も忠実度が高いため、テストでも極力こちらをつかう
- 実コードを使うことで実行時間が遅い、安定性に欠ける、外部サーバに接続の必要があるなどがある場合はFakeを使ってアウトプットを定義する
- Fakeが使えない場合はMockを使う

# Fakeを使うことの注意点
- 実際の挙動に本当に準拠しているかのテストが必要になる
- Fakeオブジェクト自体の保守・管理が必要になる
- Fakeオブジェクト分コードが多くなる
DNSサーバーについて

今日はじめてDNSサーバーの役割とその種類について学んだのでメモしておく。

まずDNSはdomain name systemの略である。
インターネット上の住所と言えるipアドレスをそのままブラウザに打ち込むのは面倒なので、一般的にユーザー（クライエント）はURLをブラウザに打ち込む。

このURLはipアドレスと対応しているのだが、その対応関係をブラウザが知っているわけではない。

ブラウザは対応するipアドレスを知るためにDNS recursive server(再帰DNSサーバー）に教えてくれとリクエストする。

recursive serverはroot serverにそのURLのtop level domain(たとえばyoutube.comならtop lever domain は.com)を聞く。

それを聞いたrecursive server は対応したtop level domain name server(TLDサーバー）にipアドレスを聞く。

TLDサーバは対応するipアドレスを調べ、authoritative name server(権威DNSサーバー)に受け渡す。

authoritative name serverはそのipアドレスにメッセージをおくり正しい返答があれば、web browserにipアドレスを送り返す。

browserが正しいアドレスを受けとると、loadし始める。

これであっているのだろうか？私の理解は不十分なので、間違っているところがあればご指摘いただければさいわいでｓ
Lambda(Node.js)からDynamoDBに接続したときに502(getaddrinfo EBUSY dynamodb.ap-northeast-1.amazonaws.com)が出たときの対処法

# 事象
Lambda(Node.js) → DynamoDB に接続するがブラウザテストではうまくいく。
しかしJMeterでスレッド1～10で数分間負荷をかけると、最初のほうが良いが、しばらくすると 502エラー（getaddrinfo EBUSY dynamodb.ap-northeast-1.amazonaws.com）が断続的に発生する。

# 対策（AWSからの回答）
handler内でAWS SDKのサービスクライアントを生成すると発生するとのこと。

↓エラーになるコード
```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

export const handler = async (event) => {
    const client = new DynamoDBClient({}); <==== handler関数の中で new DynamoDBCLientしているのがダメっぽい

    // 処理
};
```

↓対策済コード
```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({}); <==== handler関数の外側で new DynamoDBCLientするとイケる

export const handler = async (event) => {
    // 処理
};
```

自分の場合には、Modelクラスを作って各Modelの中で それぞれ new DynamoDBClient していたのがダメらしいです。
なので、handlerがあるファイル index.js の中で グローバル変数として new DynamoDBClient({}); してあげて、各Modelでは、グローバル変数を使用するように変更しました。
※あんまりグローバル変数つかいたくは無いんですが、仕方なし。


↓具体的にはコレ（serverlessExpress使ってます）
```
global.dynamodbClient = new DynamoDBClient({ <=== これを各Modelで使いまわす
    region: process.env.AWS_REGION,
});

export const handler = serverlessExpress({ app });
```

以上
初めて障害対応をしたときのお話

# はじめに

社外向けシステムの開発運用を担当してから1年が経ち、障害対応を経験しましたので簡単に振り返りをまとめておきたいと思います。

# 学び
- 普段と違うことをする際は細心の注意を払う
修正したら関連箇所がないか確認をしましょう。修正忘れに気づかず本番環境を更新してしまうとトラブルの元になります。

<br>

- リリース手順は都度まとめておく
リリース内容によってはバッチ処理を入れたり、デプロイ手順が異なったりしました。
リリース日程の調整が進まず古いバージョンのままになっていた環境があり、当時の記憶が薄れたままリリース作業をすることになったのでしっかりまとめておきましょう。

# 原因
詳しくは記載できませんが、今回特例で対応した箇所に関連するコードの修正漏れが原因でした。

# 気づいたこと
- どんよりした雰囲気ではなかった
上司がそのような雰囲気作りをしてくれました。開発者間で変に焦ることなく淡々と作業を進めることができました。

<br>

- 周りがサポートしてくれた
開発担当ではないプロジェクト関係者も色々とサポートしてくれました。
他人事でなくチームとして動いている感じがしてとても心強かったですし、嬉しかったです。

# おわりに
リリース作業は複数人でチェックし合いながら進めているのですが、今回に限り全員当時の記憶が薄れていました。
原因の特定と解決までに時間を要さなかったことが幸いでした。
ユーザー削除後もデータを保持するための設計パターン

## はじめに

多くのソフトウェアではユーザーを管理する機能があります。しかし、ユーザーを削除した際にそのユーザーに関連するデータも一緒に削除されてしまうと、後々のデータ分析や履歴の確認が難しくなります。この記事では、ユーザーを削除してもデータが消えないようにする方法について説明します。

## ユーザー削除の設計パターン

ユーザーを削除する際に関連データを保持するための一般的な設計パターンとして、以下の方法があります。

1. **論理削除 (Logical Delete)**
2. **関連データの匿名化 (Data Anonymization)**

### 論理削除 (Logical Delete)

論理削除とは、ユーザーのデータを物理的に削除するのではなく、データベース上で「削除された」ことを示すフラグを立てる方法です。これにより、データはデータベースに残り続け、必要に応じて復元することも可能です。

#### 実装例

1. **データベースにフラグを追加**

```sql
ALTER TABLE users ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE;
```

2. 削除時にフラグを更新

```sql
UPDATE users SET is_deleted = TRUE WHERE user_id = ?;
```

3. アクティブユーザーのみを取得するクエリ

```sql
SELECT * FROM users WHERE is_deleted = FALSE;
```

## 関連データの匿名化 (Data Anonymization)

ユーザーを削除する際に、個人を特定できる情報を匿名化し、関連データを保持する方法です。これにより、データのプライバシーを保護しつつ、統計的なデータは保持できます。

### 実装例

1. ユーザー情報を匿名化

```sql
UPDATE users SET name = '匿名', email = 'anonymous@example.com' WHERE user_id = ?;
```

# まとめ

ユーザーを削除しても関連データが消えないようにするためには、論理削除やデータの匿名化といった設計パターンを活用することが重要です。これにより、データの一貫性とプライバシーを保ちながら、ビジネス上の要件を満たすことができます。適切なデータ保持戦略を実施することで、組織全体のデータ管理が向上し、信頼性の高いシステムを構築することが可能になります。
Next.js インストール時の「対話式コンソールの質問と意味」

Next.js のインストール時に英文の対話質問文に答えながらプロジェクトの設定を指定していきます。はじめて Next.js をインストールする際に「この英文はどう意味なのか」という疑問を当記事で解決できれば幸いです。
　
みなさんご存知のとおり、Next.js をインストールするには以下のコマンドを入力しエンターを押します。

```shell:Next.js をインストールするコマンド
npx create-next-app@latest
```

すると、インストールする Next.js プロジェクトの設定を指定するための対話文が表示されます。早速、対話の質問文を１項目ごとに見ていきましょう。
　
```:１つ目の質問文
What is your project named?
```
プロジェクトの名前を指定する質問文です。例えば、`my-app` と入力すると、その名前のディレクトリが作成されます。
　
```:２つ目の質問文
Would you like to use TypeScript? ( No / Yes )
```

TypeScript を使用するかどうかを選択します。`Yes` を選ぶと、プロジェクトに TypeScript が設定されます。`No` を選ぶと、JavaScript が使用されます。
TypeScript を使用することで、静的解析によるバグの予防にも役立ちますので基本的には `Yes` を選択したほうが良いでしょう。
　
```:３つ目の質問文
Would you like to use ESLint? ( No / Yes )
```

コード品質のチェックとスタイルの強制のために ESLint を使用するかどうかを選択します。`Yes` を選ぶと、ESLint がプロジェクトに設定されます。

ESLint を使用すると、コードの品質を保持しバグの予防にも役立つのですが、Next.js を開発する上で少々使いずらいところも正直あります … 。
　
```:４つ目の質問文
Would you like to use Tailwind CSS? ( No / Yes )
```

Tailwind CSS を使用するかどうかを選択します。`Yes` を選ぶとプロジェクトに Tailwind CSS が設定されます。
　
```:５つ目の質問文
Would you like to use src/ directory?  ( No / Yes )
```

プロジェクトのソースコードを `src/` ディレクトリ内に配置するかどうかを選択します。`Yes` を選ぶと、`src/` ディレクトリが作成され、その中にソースコードが置かれます。

どちからというと、`src/` ディレクトリを作成せずにプロジェクトルート直下に `app` ディレクトリを配置したほうがスッキリした感じなります。

と言っても、ディレクトリの配置については人それぞれの好みになりますので、お好きなほうを選んでください。
　
```:６つ目の質問文
Would you like to use App Router? (recommended) ( No / Yes )
```

`App Router` を使用するかどうかを選択します。`App Router` は Next.js の新しく実装されたルーティングシステムの一部で、`Yes` を選択することで利用することができます。以前からある `Pages Router` を使用したい際は `No` を選択してください。
　
```:７つ目の質問文
Would you like to customize the default import alias (@/*)? ( No / Yes )
```

デフォルトのインポートエイリアスをカスタマイズするかどうかを選択します。`Yes` を選ぶと、デフォルトのエイリアスを設定できます。例えば、`@/` を使ってプロジェクトのルートからのインポートを簡略化できます。

基本的にデフォルトのエイリアス `@/` で問題ありませんので、デフォルトの `No` を選ぶことをお勧めします。

---

以上、Next.js インストール時の対話部質問の解説でした！これらの設定は、プロジェクトの初期設定を簡単に行うためのものです。選択肢を適宜選んでプロジェクトを作成することで、自分の開発環境に適した Next.js プロジェクトを素早く立ち上げるてみましょう！

それでは、良い開発ライフを！
テスト108

```vb

Public Type Type_Page

    Page_Name As String
    Page_Cnt As String

End Type

Public TypePage() As Type_Page

Sub PDF_Proc()

Dim fol_Path As String
Dim sh As Worksheet
Dim i As Long
Dim j As Long
Dim File_Path As String
Dim FSO As Scripting.FileSystemObject
Dim str_Array As Variant
Dim From_Page As Variant
Dim To_Page As Variant

ReDim Preserve TypePage(0)
Set FSO = New Scripting.FileSystemObject

j = 1

Set sh = ThisWorkbook.Sheets("PDFページ")
fol_Path = "C:\Users\User\Desktop\VBA関連\VBA練習\PDF_テスト"


With sh

        TypePage(UBound(TypePage)).Page_Name = Trim(.Cells(1, "B").Value)
        TypePage(UBound(TypePage)).Page_Cnt = 1 & ","
            
    For i = 1 To sh.PageSetup.Pages.Count - 1
    
        If TypePage(UBound(TypePage)).Page_Name = Trim(.Cells(.HPageBreaks(i).Location.Row, "B").Value) Then
    
            TypePage(UBound(TypePage)).Page_Cnt = TypePage(UBound(TypePage)).Page_Cnt & i + 1 & ","
        
        Else
        
            If i <> 1 _
            Or TypePage(UBound(TypePage)).Page_Name <> Trim(.Cells(.HPageBreaks(i).Location.Row, "B").Value) Then
            
                TypePage(UBound(TypePage)).Page_Cnt = Left(TypePage(UBound(TypePage)).Page_Cnt, Len(TypePage(UBound(TypePage)).Page_Cnt) - 1)
                ReDim Preserve TypePage(UBound(TypePage) + 1)

            End If
        
            TypePage(UBound(TypePage)).Page_Name = Trim(.Cells(.HPageBreaks(i).Location.Row, "B").Value)
            TypePage(UBound(TypePage)).Page_Cnt = i + 1 & ","
            
        End If
        
    Next i
    
    TypePage(UBound(TypePage)).Page_Cnt = Left(TypePage(UBound(TypePage)).Page_Cnt, Len(TypePage(UBound(TypePage)).Page_Cnt) - 1)

End With


For i = LBound(TypePage) To UBound(TypePage)

    File_Path = FSO.BuildPath(fol_Path, "test_" & i + 1 & ".pdf")

    str_Array = Split(TypePage(i).Page_Cnt, ",")
    From_Page = str_Array(LBound(str_Array))
    To_Page = str_Array(UBound(str_Array))
    
    sh.ExportAsFixedFormat Type:=xlTypePDF, FileName:=File_Path, _
                                            From:=From_Page, _
                                            To:=To_Page

Next i


End Sub

'カッコのつけ方と範囲が根本的におかしいかもしれないので、一通り見直してみる
Apacheでリバースプロキシー設定を行う

![httpd001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3668800/23fb0ed3-db4a-9cd0-a6e7-c9e5ecdaf1da.png)


皆様、Apacheは利用されておりますでしょうか。

Apacheでは様々な機能が利用でき情報が多く存在している事から幅広い業界で採用されているかと思います。

そんなApacheの機能の一つにリバースプロキシーというものがあります!

そこで今回はApacheでリバースプロキシー設定を行う方法についてご紹介致します。

前提条件は以下となります。

* レットハット系OS
* Apacheがインストール済み
* バーチャルホスト設定が有効化されている


## リバースプロキシーとは？

始めにリバースプロキシーについて軽くご紹介させていただきます。

リバースプロキシーとはサーバーに来たリクエストを条件に応じて別のサーバーに転送する機能となります。

また転送先サーバーからのレスポンスをクライアントに返す役割も持っております。

例えば「example.com」を利用しwebサイトを運用しているとします。

下記は通常ページのアクセスURLとなります。

```
https://example.com/
```

「example.com」にて「shop」パスのみ別サーバーを利用したい場合、リバースプロキシー機能を利用し別サーバーへ転送する設定を導入できます。

```
https://example.com/shop/
```


## 必要なモジュール

リバースプロキシー機能を利用場合は「mod_proxy」と「mod_proxy_http」が必要となります。

下記コマンドを実行しモジュール確認を行います。


```
httpd -M | grep proxy
```

上記を実行し「proxy_module」と「proxy_http_module」が表示されていれば問題ないかと思います。

表示されていない場合はインストールを行ってください。

## 設定の追加

次にリバースプロキシー設定の追加を行っていきます。

今回は「example.com」のバーチャルホストファイルに追記する例をご紹介せて頂きます。

リバースプロキシー設定は下記の様に記述する事で設定可能です。

```
ProxyPass 指定パス 転送先URL
ProxyPassReverse 指定パス 転送先URL
```

「ProxyPass」では指定したパスに来たリクエストを別のサーバーに転送する設定を行う事が出来ます。

ProxyPassReverse ディレクティブは、転送先サーバーにてリダイレクトが発生した際に、そのリダイレクトの応答ヘッダーを修正するために使用されます。

「example.com」の「shop」を別サーバーに転送する場合は以下の様に記述致します。

```
ProxyPass /shop https://shop.example.com/
ProxyPassReverse /shop https://shop.example.com/
```

## 設定反映

最後に設定反映を行う為、Apacheの再起動を行います。

再起動前に下記のシンタックスチェックコマンドを使用し設定構文に誤りが無いか確認します。

```
httpd -t
```

上記実行し下記の様に表示されていれば問題ないです。

エラーが発生している場合は設定項目に誤りがあるかと思いますのでエラー内容を修正して下さい。

```
Syntax OK
```

最後に下記コマンドを使用し再起動を行います。

```
systemctl restart httpd
```
【Python x Docker】uwsgiパッケージのインストールでエラーが出た場合

# エラー概要

dockerビルド中に以下のエラーに遭遇。
使用したイメージは`python:3.6-slim-busterイメージ`です。

> ...(省略)
install --record /tmp/pip-record-89kpydwp/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python3.6m/uWSGI Check the logs for full command output.
failed to solve: rpc error: code = Unknown desc = executor failed running [/bin/sh -c pip install -r requirements.txt]: exit code: 1

# 原因
`uwsgi`パッケージのインストール中にエラーが発生しているよう。
このパッケージはC言語で書かれた部分があり、そのコンパイルにはPythonの開発ヘッダーファイルやコンパイラなどの追加の依存関係が必要とのこと。

`python:3.6-slim-buster`イメージは、（サイズを小さく保つために）これらの依存関係が含まれていないようですね。普通に`python:3.6`ならこのエラーは出ませんでした。

# 対処法
以下のようにインストールしてあげることでエラーがなくなりました。

```Dockerfile
FROM python:3.6-slim-buster

RUN apt-get update && apt-get install -y \
    build-essential \
    python3-dev \
    libpcre3 \
    libpcre3-dev \
```

これで`uwsgi`のインストール時に必要なコンパイル環境が整うようになりました。

- `build-essential`: Debianベースのシステム（Ubuntuなど）でソフトウェアをビルドするために必要なツールパッケージ
    - 参考： https://packages.ubuntu.com/focal/build-essential
- `python3-dev`: PythonのC拡張をコンパイルするために必要なパッケージ
    - 参考： https://packages.debian.org/ja/sid/python3-dev


ちなみにuWSGIとは、PythonでWebサービスを動かすためのアプリケーションサーバのこと（対応言語はPythonだけではない）。
こんなイメージですね。

> HTTP client ↔ Nginx ↔ uWSGI ↔ Python app
引用元：https://en.wikipedia.org/wiki/UWSGI

WSGIプロトコルを実装している、PythonアプリケーションとWebサーバー間の標準的なインターフェースです。WSGIについては以前こんな記事も書いたのでご参考までに。

https://qiita.com/Ryo-0131/items/1d1df96aca61d1873de8


Pythonを含む複数のプログラミング言語をサポートするWebサーバーであり、アプリケーションサーバーです。uWSGIは、WebアプリケーションとWebサーバー間の通信を管理するために使用され、特にPythonのWebアプリケーションでよく使用されます。uWSGI自体は、WSGI（Web Server Gateway Interface）プロトコルを実装しており、PythonアプリケーションとWebサーバー間の標準的なインターフェースを提供します。
【PostgreSQL】メタコマンド〜\i〜

## 初めに

Qiita Engineer Festa 2024に参加中。完走目指してます。
他のメタコマンドについては以下から読んでください。

[【PostgreSQL】メタコマンドを纏めていく](https://qiita.com/dai_chi/items/04f5339d54130f070671)

## \iとは

> `filename`ファイルから入力を読み取り、キーボードから入力された場合と同じように実行します。
> 
> 
> `filename`が`-`（ハイフン）の場合、EOFを示すもの、または`\q`メタコマンドが読まれるまで標準入力から読み込みます。 これは対話的な入力とファイルからの入力を混在させるために使うことができます。 Readlineと同じ挙動は、それが最も外部のレベルで動作している場合にのみ利用されることに注意してください。
> 

[\iについて](https://www.postgresql.jp/docs/15/app-psql.html#:~:text=%5Ci%E3%81%BE%E3%81%9F%E3%81%AF%5Cinclude%20filename)

## 試してみた

以下のようなSQLのファイルを作成（仮にscript.sqlとする）
```sql
CREATE TABLE test (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100)
);

INSERT INTO test (name) VALUES ('Alice'), ('Bob'), ('Carol');
```

psqlでセッションを開始した後に以下を実行する

```sql
\i script.sql
```

エラーなく完了したらselectで確認

```sql
select * from test;
 id | name  
----+-------
  1 | Alice
  2 | Bob
  3 | Carol
(3 rows)
```

`\i -`という使い方もある。
以下のように最後の行に追加してみる。

```sql
CREATE TABLE test (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100)
);

INSERT INTO test (name) VALUES ('Alice'), ('Bob'), ('Carol');

\i -
```

`-f`でファイルを実行する

```sql
psql -f script.sql
```

すると`\i -`がなければpsqlセッションを終了するはずが、継続してSQLを入力、実行ができるようになる。

## まとめ

`\i`は複数のSQLを1アクションで実行することができるのでよく活用します。

`\i -`について今回初めて知りましたが、すぐにSQLコマンドを実行したい時があれば活用できそう。実際に実務の中でもそんな場面があったようなないような気がします。
届いたと伝わったの間をどう埋めるか

[記事投稿キャンペーン](https://qiita.com/official-events/a1b1a6dd87105eb2bb92)を機に、身の回りの「情報が伝わらなかった」例を思い返したところ、「情報が届いたけれど伝わらなかった」ことの方が印象深く記憶に残っていました。
そこでこの記事では、自分が伝える際の自戒として、また、情報伝達をしようとする人の助けになることを願って、「情報が届いたけれど伝わらなかった」パターンを書き出してみました。BtoCのパターンばかりになってしまいましたがご了承ください。
また、技術面・コスト面で実装可能かどうかは考えず、解決アイデアを思いつくだけ書いてみます。すでに使われている方法も書き出します。

# ケース1：届いたけど開封しない
自分もやらかしますがみなさんも心当たりがありませんか。紙でもデジタルでもDMが届いたらすぐチェックするのが理想ですが、できる人はできるけれどできない人はなかなかできないものですよね。
あとはアプリ・サービス内のお知らせなんかもよく溜めていたり無視したりしがち。

## 解決アイデア
### 開く前に概要・重要度がわかるようにする
すでにやっているところがほとんどですが、メールタイトルなり封筒の表面なりに概要や重要度を記載する方法。
ただ、「重要」を連発するとかえって開かなくなったり、そもそも開かない人は工夫しても開かないことも…ある…。
とは言えやらないよりやったほうがもちろんいいことです。

### 開封をチェックする機能をつける
一番身近な例だとLINEの既読ですかね。メッセージを送った側・送られた側双方が「これは開いた」がわかると何かと便利。
ただ、ケース3・4で後述しますが「開いた==メッセージが伝わった」とは限らないのが曲者。

# ケース2：届いたけど開封できない
ケース1と似ていますが、解決方法が変わってくると思うので別ケースとしました。いわゆる情報弱者に多い例。

## 例1
Aさん「このサービス使いたいんやけど登録できへん」
Aさんの家族「しゃーない代わりに登録したろ。スマホ貸し。ほいできた」
（一定期間経過…）
Aさん「なんか勝手に○○になっとる」
Aさんの家族「どれどれ……（メール・サービス内お知らせ等）で×月から○○になるって書いてるやん！」
Aさん「えっそんなの来てたん？　てかそれどうやって見るの？」

## 例2
Bさん「登録にはメールアドレスを入力するんやな」
Bさん「スマホを買ったときに設定してもろたメールアドレスは手帳にメモっとるで～」
Bさん「あれ？　『自動メールを送りました』って出たけどメールが見つからん」
（友人に相談…）
友人「別のメールアドレスのほうに届いてるで。こっちのアドレスで登録してただけやん」
Bさん「？？」

## 解決アイデア
### 手順をわかりやすく表示する
利用手順・チュートリアル・Q&Aをとにかくわかりやすくする。細かいステップやデバイスが違う場合の方法も書く。
ただ、わかる人にとってはうざったくなる可能性が大きいので、スキップの選択肢もあるとベターかなと。

### 複数手段を用意する
電子メールが苦手（orできない）なら郵送や電話、と選択できるようにする方法。

### サポートサービスをつける
電話や店頭での追加サポートサービスはトレンドではないと思いますが、解決方法ではあるので書き出しておきます。
チャットボットやサポートAIなど、技術進歩で低コスト化＆品質向上を期待しています。
例2のような場合にまで対応できるようにするにはOS側にサポート妖精さんでもつかないと難しくない！？　とは思いますが。できませんかね、サポート妖精。できたらできたで「お前を消す方法」のイルカのようになる気もしますが。

# ケース3：見たけどちゃんと見ていない
利用規約毎回ちゃんと読んでますかー！？　私はちゃんと読んでいません（反省）
アプリorサービス内のお知らせも通知を消すためだけに既読にすることがあります。

## 解決アイデア
### 「読んだら○○して」などの指示をメッセージ中に入れる
もしくは「読みました」ボタンをあえていつもと違う場所に配置するなど。
### お知らせページのページビュー時間等をチェックする
「読んでもらう」よりは「読んでいるかチェックする」方法ですが。

# ケース4：理解できない
## 例
Cさん「届いたメッセージをちゃんと開いて読んだ！　でも内容がよくわからない！」
Cさん「わからないけどとりあえず「理解しました」ボタンを押したら先に進めた！」
Cさん「じゃあいいか！」

その場ではお互いよくても後々トラブルが起こるかもしれないパターンですね。

## 解決アイデア
### 「理解した」ボタンの代わりに内容に関するクイズを出す
内容についてのポイントを抜き出したクイズに正解したら「読んで理解した」とみなす方法。ケース3にも有効かと。
自由記述にすると判定が大変なので選択式にするとか。
不正解の人には追加説明をするとか。
ただ利用者側からすれば面倒なので避けて別サービスを選ぶ可能性が大きいですね。

### 追加説明にアクセスしやすくする
「OK」「キャンセル」の2択ではなく「よくわからない」的な選択を作る・選びやすくするなど。

# おわりに
あまり新しく有効なアイデアは出ませんでしたが、そもそも簡単に思いつくいい方法はすでに使われているはずです。
ほかの「届いたけれど伝わらなかった」パターンや解決法もあるかと思いますので、知っているor発見した方はぜひコメントや記事で教えてください。
この記事が情報伝達を少しでも良くする助けになれば幸いです。