OmniAICreator/Qiita-1.07M huggingface dataset:
Googleに学ぶ、コードレビューの方法

## はじめに
皆さん普段、コードレビューを、どのように行っていますか？
意外と、コードレビューの方法って、特に教えてもらってないとか、なんとなくやっているとか、それぞれ自分のスタイルで行っていたりしないでしょうか？

私も、自分スタイルで行っていたのですが、コードレビューにも何か基準やガイドラインがあれば効率的では、と考えて探していたら、Googleが公開している[コードレビュー開発者ガイド](https://fujiharuka.github.io/google-eng-practices-ja/ja/review/)がヒットしました。

本記事では、この[コードレビュー開発者ガイド](https://fujiharuka.github.io/google-eng-practices-ja/ja/review/)の内容を要約して、紹介していきたいと思います。

## コードレビューとは？
コードレビューとは、コード作成者以外の人が、コードを調べるプロセスのことです。
## コードレビューの目的
コードの全体的な品質を、時間をかけて改善することです。
## コードレビューの基準
まず、コードレビューの目的の達成の為に、開発者と、レビュワーは以下のように、プロジェクトを進行させる必要があります。
### 開発者
自分のタスクを進めて、レビュワーにコードを提出し、プロジェクトを前進させないといけません。
そのため、レビュワー側もいちいちコードに難色を示し、コードの変更を取り入れないと、プロジェクトが前進しません。
### レビュワー
提出されたコードの品質を確認するのは、レビュワーの義務です。
難色を示して、コードの変更をなかなか取り入れないのもよくないが、時間的な制約などで、確認レベルを下げて、変更を取り入れてしまうと、品質を悪化させてしまいます。

では、どうすればよいか？

ガイドラインでは、以下のような基準を設定しており、全ガイドラインの最上位の原則としています。
>**一般に、CL が完璧でなくても、その変更がシステムのコードの全体的な健康状態を改善すると確実にわかれば、レビュアーは CL を積極的に承認すべきである。**

ポイントは、「**完璧**」なコードは存在せず、存在するのは、「**より良い**」コードだけということです。
追求すべきは、**継続的なコードの改善**の為、コードが完璧ではなくでも、承認して、プロジェクトを進めるべきです。

但し、さらに改善できそうな点を見つけたら、気軽にコメントを残すべきです。
もし重要ではなく、無視しても構わない指摘の場合、コメントなどをつけて開発者に知らせるとよいです。
※ガイドラインでは、「Nit:」（あら探しや細かい指摘という意味）のようなプレフィックスを付ける例が記載されています。

## コードレビューの観点
目的と基準がわかったので、続いてどの観点で、コードレビューをしていくかです。
以下の観点でコードレビューをしていきます。
### 設計
全体の設計面を確認します。
コードレビューで最も大切なことになります。
### 機能性
開発者の意図通り、動作するか、また、使用するエンドユーザーにとって適切な機能かを確認します。
### 複雑性
コードが必要以上に複雑になっていないか、確認します。
関数や、クラス名など、1行1行複雑になっていないか、確認します。

※「複雑」＝「コードを読んですぐに理解できない」
### テスト
コードの変更に適したユニットテスト、結合テスト、E2E テストを依頼します。
### 命名
関数、クラス名など、あらゆるものに適切な命名がされているか確認します。
### コメント
コメントが記載されている場合、明確でわかりやすいか、確認します。
コードが「**何**」をしているかではなく、「**なぜ**」存在するのかをコメントすると良いです。
### スタイル
スタイルガイドがある場合は、それに従っているか確認します。
### ドキュメンテーション
ビルドや、リリースの方法など変更する場合は、それに関連するドキュメンテーションも更新されているか確認します。
## コードレビューのスピード
続いて、レビュー依頼が来たら、いつレビューするかです。
皆さんも気になるポイントではないでしょうか？

結論から言うと、
**コードレビューの依頼が来たらすぐに着手するのが良いです**

素早くコードレビューを行わないと、以下デメリットがあります。
+ **チーム全体の開発速度が遅くなります。**
レビューをしないと、チームメンバーのタスクが滞ってしまいます。
+ **開発者が、コードレビューのプロセスに不満をもってきます。**
レビュアーのリアクションが遅いのに、修正依頼が多量となると、開発者はストレスを感じてしまいます。
+ **コードの品質の低下に繋がります。**
レビューが遅いと、時間の制約などあり、コードの品質が良くない状態でも提出してしまってよい雰囲気が広がってしまいます。

### レビュアー自身のタスクがある時
レビュアー自身もタスクがあり忙しい時があると思います。
**コードを書くような集中的に取り組むべきタスク**の最中は、中断してまでコードレビューしてはいけません。結果的に、余計なコストが掛かってしまう可能性があります。
とはいえ、最長一営業日以内に返答してください。

### 素早い応答
どうしてもレビュアーの時間がとれないときは、広い観点で見た初期段階のコメントを残したり、他のレビュアーを紹介するも良いです。
レビューが完了していなくても、素早い応答があれば、開発者に余計なストレスを与えないです。

## レビューコメントの書き方
最後にレビューコメントの書き方になります。
以下の観点で、コメントを書くとよさそうです。
### 要約する
コメントは丁寧に、理由を説明してください。
### 礼儀正しさ
礼儀正しく、開発者に敬意を払った書き方がよいです。
必ずコードについてコメントし、開発者本人についてコメントしないように心がけます。
### 理由を説明
レビューコメントに必ず理由を加えてください。
### 指示を与える
コードを修正する責任は、開発者なので、基本的には、コード修正は、開発者に任せるのが良いですが、
一方で直接的な指示や、コードを提案することが有益になる場合があります。
### 説明を受け入れる
コードを理解できない場合は、説明をしてもらったり、コードを書き直してもらうように、依頼しましょう。

## まとめ
いかがでしたでしょうか？
ガイドラインには、開発者はもちろん、レビュアー自身にも配慮された、最適な方法が示されていると思いました。
ガイドラインを基準に、コードレビューしていくと、レビューの方法にも迷わないですし、コードの品質も向上していくと思います。

本記事では、要約して簡単に紹介してますが、
実際のガイドラインはもっと充実した内容となっていますので、興味がある方は、是非ご一読ください。
最後まで読んで頂き、ありがとうございました。

## 参考
+ [コードレビュー開発者ガイド](https://fujiharuka.github.io/google-eng-practices-ja/ja/review/)
HuaweiのSiteKit APIでリバースジオコーディングして知り合いの病院の住所を覗いてみる


Huawei Bandを入手してみたので、HuaweiのAPIを試してみたく...... 調べていましたがHealth系のデータ以外にも色々ありそう、、だったのでSite Kitというものを使ってみました。

https://developer.huawei.com/consumer/en/doc/HMSCore-References/webapi-reverse-geo-0000001050161968


## 事前準備など

スクショがないですが、個人情報入れたりしながら申請して、翌日くらいに許可がおりました。

## 35.7642868,139.8043692

[ヘルスケアIoTLT](https://iotlt.connpass.com/event/300104/)の会場にもなる足立慶友整形外科のスポットをGoogleで調べると`35.7642868,139.8043692`という座標がでました。

> https://www.google.com/maps/place/%E8%B6%B3%E7%AB%8B%E6%85%B6%E5%8F%8B%E6%95%B4%E5%BD%A2%E5%A4%96%E7%A7%91/@35.7642868,139.8043692,21z/data=!3m1!5s0x60188e306dc352a1:0x4c5036dfdfea5843!4m15!1m8!3m7!1s0x60188e306ddabe3b:0xfeb20f5155e3ac79!2z44CSMTIwLTAwMTUg5p2x5Lqs6YO96Laz56uL5Yy66Laz56uL77yR5LiB55uu77yR77yS4oiS77yR77ySIOi2s-eri-ODoeODh-OCo-OCq-ODq-ODouODvOODqw!3b1!8m2!3d35.7641571!4d139.8043359!16s%2Fg%2F11dxmb3r6v!3m5!1s0x60188f84af713b0b:0x35a9b9ca45eb60b9!8m2!3d35.7643062!4d139.8043598!16s%2Fg%2F11fn0ctdfh?entry=ttu


> ![スクリーンショット 2023-12-15 0.21.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/fe2ef44c-c3b0-9bde-67e3-14a6eecb7288.png "スクリーンショット 2023-12-15 0.21.25.png")

URLにある`@35.7642868,139.8043692,21z`の部分が緯度経度のようですね。

## Node.jsで触る

サンプルリクエストからコードを作ってみましたが、割とすんなり動きました。

```js
const APIKEY = `APIキー`

async function postRequest() {
    const url = `https://siteapi.cloud.huawei.com/mapApi/v1/siteService/reverseGeocode?key=${APIKEY}`;
    const body = {
        location: {
            lat: 35.7642868, //緯度
            lng: 139.8043692 //経度
        },
        language: "en",
        radius: 10
    };

    try {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify(body)
        });

        if (!response.ok) {
            throw new Error(`Error: ${response.status}`);
        }

        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('There was an error!', error);
    }
}

postRequest();

```

緯度経度から住所情報に変換ができました。

Googleの情報とかを裏で取ってきているのでしょうか 割と細かく住所が取れてて驚きです。

> ![スクリーンショット 2023-12-15 0.25.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/8ed3ad73-2325-2687-8f64-38e3e1cc4c34.png "スクリーンショット 2023-12-15 0.25.44.png")

## まとめと所感など

HuaweiのAPI、触ってる人が少なすぎて開拓してる感じがすごいです。
他のAPIもあるけどなかなか解読に苦戦してます。

### つまづいたところ

[公式サンプルリクエスト](https://developer.huawei.com/consumer/en/doc/HMSCore-References/webapi-reverse-geo-0000001050161968)がlat,lng（緯度経度）の順番ではなく、lng,lat（経度緯度）の順番になってるのに気づかず逆に設定してたらエラーがでてましたね。。

> ![スクリーンショット 2023-12-15 0.23.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/57637f2d-6dee-ade1-6b5b-ef7035df4377.png "スクリーンショット 2023-12-15 0.23.19.png")
【UE5】BPでBPM！

BPMに合わせてライトを点滅させたいけどGet Game Time Since CreationとかにSinノードそのまま繋いだらなんかズレてたので正弦波とかちょっと調べ直してBPに落とし込んでみた。

結論から言うとこうなるはず(たぶんあってるはず)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102588/a9dfdfc0-2697-a437-4622-848aa65ab8ae.png)

計算をわかりやすくするためにSinの周期を1秒にしたいのでGet Game Time Since Creation(今回はとりあえずこれで)に2πをかける。

角周波数とかは以下を参考に。
https://cognicull.com/ja/uexlhxmj

その状態だと1秒間に1サイクル、つまり1分間に60サイクルなのでBPM計算をしやすくするためにいったん1分間に1サイクルにするために60で割る(beat per minutenなので)。>> BPM=1

それにBPMをかけれたのものをSinノードに入れてやれば指定したBPMで-1～1を繰り返してくれます。
で-1～1のままだと使い勝手が悪いので+1して0～2に、それを2で割って0～1に。
システムの処理方式についての基本

# 1. はじめに
システムの処理方式について、オンライン処理やバッチ処理以外にも、センタカット処理やディレードオンライン処理があることを知りました。そこでそれぞれの処理方式について調べた内容を記事にします。

# 2. 処理方式の分類
## 2.1 オンライン処理
リアルタイムでデータを処理する方式です。ユーザーの入力や要求に対して即座に応答します。インタラクティブで、迅速な処理が求められる場合に使用されます。

例: インタラクティブなウェブアプリケーション
ウェブショッピングサイトやソーシャルメディアプラットフォームなど、ユーザーがリアルタイムでデータを送信し、即座に結果を得ることができるアプリケーション。
## 2.2 バッチ処理
一定のサイクルに従いバッチ単位でデータを処理する方式です。大量のデータを一括で処理し、通常は事前に準備されたプログラムに基づいて処理が行われます。処理のタイミングがリアルタイムではなく、時間がかかることがあります。

例: 月次給与計算
企業が月末に全従業員の給与を計算するプロセス。従業員の給与データが蓄積され、一括で処理されて結果が生成される。

## 2.3 センタカット処理
センタカット処理は、データの中央部分だけを取り出して処理する方法です。大規模なデータセットから必要な一部だけを選択的に処理することができます。全体のデータを処理するよりも効率的であることがあります。

例: ビッグデータ解析
大量のビッグデータセットから特定の期間や特定の領域だけを取り出して解析する。例えば、特定の月のデータや特定の地域のデータのみを処理する。

## 2.4 ディレードオンライン処理
ディレードオンライン処理は、システムの負荷が高まった場合に、一時的に処理の品質を低下させてでもシステムの応答性を維持する方法です。通常、優先度の低いタスクや品質を下げても許容できるタスクに対してこの手法が採用されます。高負荷時においても一定のサービスを提供し続けることが可能ですが、その代わりに一部の機能や品質が低下することがあります。

例: オンラインゲームの画質調整
大勢のプレイヤーが同時に接続する場合、サーバーの負荷を軽減するために一時的にグラフィックの品質を下げる。これにより、全体の応答性が向上し、プレイヤーエクスペリエンスを維持できる。

# 3. おわりに
システムの処理方式について整理することができました。理解のお役に立てたら幸いです。
[TableauミニTips]レイアウトコンテナのスキマが埋まらない！！への対処法（パラメータによるViz切り替え時）

# はじめに

みなさんこんにちは。
 [Tableau Tips ＊ Tabjo Advent Calendar 2023](https://adventar.org/calendars/8683)のDay16として、ちょっとしたTipsを投稿させていただきます。

# パラメータ＋フィルタによるシート切り替え時に発生するスキマ

Tableau Desktopで古くからあるテクニックで、「レイアウトコンテナ内のシートを、パラメータ＋フィルタで切り替える」というものがあります。
※このテクニック自体の説明は、とても分かりやすい記事を上げて頂いている方がいるためその記事を紹介させていただくこととし、本記事では割愛します。

https://qiita.com/sho_szk_/items/9b8e2a8cb5f4faf1a3fe

↓こんなん

![パラメータ切り替え.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/30fe67bd-2037-eb97-567d-0f1798704056.gif)


最近は、Dynamic Zone Visibility（動的ゾーン表示）機能の実装によって使うケースも減ってはきましたが、動的ゾーン表示には「Viz の構造とは関係がない定数値」を用いなければならないという制限もあるので、まだまだパラメータ＋フィルタでの切り替えが登場する機会はあるだろうな、と思っています。

さて、このテクニックを使ってシートを切り替えているとき、シートの内容によっては、 **片方のシートを非表示にしても、微妙にスキマが残る** ことがあります。
（わかりやすくするため各シートとレイアウトコンテナに枠線をつけました）

こっちは大丈夫なのに、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/d8dffa27-b94c-32b4-8388-86f5dd2fd29a.png)

こっちを選ぶと下に（配置によっては上に）微妙にスキマが残る

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/2005eb78-6fc5-7277-cbd8-a0acfeb899e3.png)

きっちりレイアウトを合わせたいときにこれが起こってしまうと、「なんで消えてくれないんだよ〜」と悲しくなってしまいます。
あまりに悲しかったので、なんでこれが起きるのか調べてみた、というのが本記事の趣旨です。

いきなり結論ですが、これがどのような場合に起きるかというと、

非表示になるのが **行・列シェルフに何もディメンションが設定されていないシートである**
ときでした。

例のツリーマップとか、ドーナツチャート（min(0)とかは置くけどディメンションではない）とかが該当します。

では、このようなVizをレイアウトコンテナにおいてパラメータで切り替えたときにスキマが絶対に残ってしまうのかというと、、、防ぐ方法がありました。

# 解決法

解決法は力技ですが、

**なんでもいいので行か列シェルフにピルを置いて非表示にする**

です。

手順を見ていきましょう。



まず、**ダミーとなるディメンションを計算フィールドで作成**します。

<img width="400"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/dce183bd-5cbb-45fd-1652-631537f6a208.png" >

例では”Dummy"という文字列にしていますが、なんでもいいです。不連続のディメンションに設定すれば数字でもOKです。

そして、**作成したダミー計算フィールドを行or列にぶち込み**ます。

<img width="500"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/3b8f333e-e251-ed0a-3ea4-d0f5a39a98a0.png" >

もちろん、計算フィールドを作成せず直接行or列シェルフをダブルクリックしてフィールドを作成してもOKです（アドホック計算）。

この状態でダッシュボードに戻り、パラメータを切り替えてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/79d3c4b7-3987-5f4e-9f4e-e1c497ab4884.png)

**スキマが消えてくれました！！！**


とはいえ、さすがにツリーマップを表示した時に「スキマ埋め用ダミー」とか出ていると、これはスキマよりカッコ悪すぎるので、こちらをなんとかしましょう。

ツリーマップのシートに戻り、設定したダミーのフィールドを選択して、「ヘッダーの表示」をクリックして、増えてしまった列のフィールドラベルおよびヘッダーを非表示にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/9e28002d-d3c6-bfd3-6b54-49a3a0420f6f.png)

また、よく見るとツリーマップの上下に余計な罫線ができてしまっているので、**「書式設定」から罫線を削除**し綺麗にします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/7547d0a2-629d-e570-7c7b-5f8cfcc11ada.png)

これでツリーマップの見た目が元通りになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/9ebf4bb0-f3b4-02f3-2f42-f91f866478c5.png)

もちろん、切り替えてもスキマが出ないようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1945012/79d3c4b7-3987-5f4e-9f4e-e1c497ab4884.png)

めでたしめでたし。

# おわりに

というわけで、「行・列シェルフに何もディメンションが指定されていないシートが、パラメータで非表示にしてもスキマとなってしまう」の解消法でした。
とても細かいネタですが、いつかどこかで、誰かの役に立てば幸いです。
不用意にサーバーでアプリを起動してハングアップさせてしまった話

# はじめに
この記事は、本番環境などでやらかしちゃった人 Advent Calendar 2023の18日目です。

https://qiita.com/advent-calendar/2023/yarakashi

もう何年も前のことなのでもうそろそろ時効だろうと思い、誰かの学びになればとここに供養します。

# やらかしちゃった出来事
## ある日の保守作業

保守管理しているWebシステムの調査のためにOracle Databaseが動いているWindows Serverにリモートデスクトップ接続していました。

調査の過程でデータベースインスタンスにアクセスして状態を確認する必要が生じました。

普段は自分のクライアントPCにインストールしたデータベースクライアントツールでつないで確認していたのですが、その時は横着してサーバーにインストールされていた[SQL Developer](https://www.oracle.com/jp/database/sqldeveloper/)（OracleのGUIデータベースクライアントアプリ）を起動してしまいました。

## 固まる画面
じんわり起動していくSQL Developer。が、途中でプログレスバーが止まります。

「あれ？なんかすごい時間かかるな…。」
嫌な予感がしマウスを動かすもカーソルが動かない。キーボードも効かない。
このあたりで冷や汗が出てきます。

近くの同僚に聞いてみます。

同僚「OSフリーズしちゃってるね…。」
私「SQL Developer起動しただけなんだけど…。」
同僚「このサーバー**メモリ4GB**しか積んでないからSQL Developer動かすのもキツイと思う…。」

なるほど、[Oracle DBのサーバー最小要件](https://docs.oracle.com/cd/E82638_01/ntdbi/oracle-database-minimum-hardware-requirements.html#GUID-BF7AD499-451F-4EAE-803F-219F0628A879)がメモリ2GBとはいえ、GUIがリッチな分メモリを食うWindows Serverでかなり攻めたサイジングです。

少ないリソースで何とか動いてたのを、重めのアプリを立ち上げたせいで、とどめを刺してハングアップさせてしまったのでした。

## 鳴り響く電話

利用者からシステムエラーが出て動かないと電話がかかってきます。

もうハングってしまったものはどうしようもありません。
サーバーのあるデータセンターに連絡して本体を再起動してもらいました。
クラウドではなくオンプレだったので自分ですぐ再起動もできないのがさらにつらいですね。

再起動後、無事システム復旧しました。
データベースファイルにダメージがなかったのは不幸中の幸いでした。

# 学びと反省

この件での学びと反省は、**サーバーのリソース状況を碌に調べもせず、重たい（可能性のある）アプリだの処理だのをカジュアルに動かさないこと**です。

本番サーバーともなればリソースに余裕なく常に自分の仕事を淡々とさばいている可能性があります。

**普段と違うオペレーションをしない、するのであればその操作の影響や負荷、操作時のサーバーのリソースの状況を確認したうえで細心の注意を払って実施しましょう。**

また、**極力サーバーで作業せずクライアントPCや他の場所のリソースを使って作業を済ませられないか**検討してみましょう。

# おわりに

令和になり、クラウドがサーバーインフラのメインになって、今時そんな貧弱なサーバー使わないでしょと思うかもしれませんが、寧ろクラウドの方がサーバーのサイズを簡単に変えられるため、コスト削減のためにギリギリを攻めている可能性があります（オートスケールなどクラウドならではの対策はありますが）。

他にも例えばAWSならEC2のT系インスタンスでCPUクレジットが枯渇したときのように、OSのリソースモニターには表れない制約でサーバーが激重になることもあります。

https://aws.amazon.com/jp/blogs/startup/burstable-performance-instances/

本番サーバーでの作業はいつだって気を付けましょう。
React + PHPでreCAPTCHAv3を使いつつメールを送信するサンプル

## 事前準備
- reCAPTCHAのサイト登録をしておく → https://www.google.com/recaptcha/admin
- 発行されたサイトキーとシークレットキーをメモ

## フロント
- `react-google-recaptcha-v3`を入れて、`GoogleReCaptchaProvider`で囲っておく
- サイトキーは環境変数から読み込む
```tsx
<GoogleReCaptchaProvider
  reCaptchaKey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY!} // ← サイトキー
>
  {props.children}
</GoogleReCaptchaProvider>
```
- reCAPTCHAを使いたいところで ↓ の処理を行う

```typescript:Component.tsx
const { executeRecaptcha } = useGoogleReCaptcha();
...
const onClick = () => {
    if (executeRecaptcha == null) return;

    const serverEndpoint = '/api/mail';
    const token = await executeRecaptcha('任意の名前');
    const postData = await fetch(serverEndpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          'g-recaptcha-response': token,
          mailTo: 'mailTo@test.com',
          body: '本文',
        }),
    });
}
```
※ `GoogleReCaptchaProvider`よりも下の階層のコンポーネント内でないと、`executeRecaptcha`がundefinedになり失敗する

## バックエンド
- 環境変数に`RECAPTCHA_SECRET`を入れておく
- .htaccess使うなら↓
```bash:.htaccess
SetEnv RECAPTCHA_SECRET 'reCaptcha v3 secret'
```

- APIを実装する
```php:/api/mail.php
<?php
$secretKey = getenv("RECAPTCHA_SECRET"); // ← シークレットキー

$json_data = file_get_contents("php://input");
$decoded_data = json_decode($json_data);
$token = $decoded_data  ->  {"g-recaptcha-response"};

// reCAPTCHA
$url = "https://www.google.com/recaptcha/api/siteverify";

$data = array(
    "secret" => "$secretKey",
    "response" => "$token",
);
$data = http_build_query($data);
$options = array(
    'http' => array(
        'method' => 'POST',
        'header' => 'Content-type: application/x-www-form-urlencoded',
        'content' => $data
    )
);

$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);

if(!json_decode($response, true)["success"]) {
    // 検証失敗
    echo json_encode(["result" => "failed"]);
    exit();
}

// 検証成功
mb_language("Japanese");
mb_internal_encoding("UTF-8");
date_default_timezone_set('Asia/Tokyo');

// メール全般設定
$mailTo = $decoded_data -> mailTo;
$subject = "【お問い合わせ】フォームよりお問い合わせがありました";
$header = "From: no-reply@test.com"."\r\n"
."Return-Path: no-reply@test.com";

// フォーム項目を取得
$message = $decoded_data -> body;

// 送信
mb_send_mail($mailTo, $subject, $message, $header);

// 結果出力
echo json_encode(["result" => "success"]);
```

※ 迷惑メール判定を受けないようDNSのSPFレコードも設定しておく
※ APIと同じサーバー内のメールサーバーから送る想定になっているので、別サーバーを使用する際はSMTPで送るように変える
(A´･ω･)_🍜~~~~🍥🍥🍥機械学習学習はじめました🍥🍥🍥（そもそも機械学習とは？）

<h3>機械学習（machine learning）学習はじめました。(A´･ω･)_🍜

Pythonを勉強する中で機械学習やDeepLearning の需要を認識し、昨日から本格的に始めました。（※随時追記＆精度を上げます）

なお、本Qiitaは以下の
<h3>「機械学習のための関数解析入門ヒルベルト空間とカーネル法　瀬戸道夫、伊吹竜也、畑中健志　共著」

https://www.amazon.co.jp/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E9%96%A2%E6%95%B0%E8%A7%A3%E6%9E%90%E5%85%A5%E9%96%80-%E3%83%92%E3%83%AB%E3%83%99%E3%83%AB%E3%83%88%E7%A9%BA%E9%96%93%E3%81%A8%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E6%B3%95-%E7%80%AC%E6%88%B8-%E9%81%93%E7%94%9F/dp/4753601714/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=LX7UD36KHU6Z&keywords=%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E9%96%A2%E6%95%B0%E8%A7%A3%E6%9E%90%E5%85%A5%E9%96%80%E3%83%92%E3%83%AB%E3%83%99%E3%83%AB%E3%83%88%E7%A9%BA%E9%96%93%E3%81%A8%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E6%B3%95&qid=1702520201&sprefix=%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E9%96%A2%E6%95%B0%E8%A7%A3%E6%9E%90%E5%85%A5%E9%96%80%E3%83%92%E3%83%AB%E3%83%99%E3%83%AB%E3%83%88%E7%A9%BA%E9%96%93%E3%81%A8%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E6%B3%95%2Caps%2C415&sr=8-1

を個人的に解釈したものの備忘録です。（※大学以降で習う数学あるあるですが、答えは先生方の頭の中にしかないため、違う解釈をしている可能性があります。ご了承下さい。徐々に精度を上げます。※数学の学習は「イメージの共有」に近いです。）

<h2>機械学習（machine learning）における「学習」とは何か？（´｡｀)

ここでは、ざっくり言うと

「**ある２つの計測データの組の関係を近似的に推定すること**」、要は「**関係を調べる**」

ことを指します。（※数式で詳しく書くと次みたいなかんじ）

https://drive.google.com/drive/folders/1DeNcDckvhDKcQha9aAGnuyVuL7JViwMX

<h2>一口に「機械学習」と言っても、どうもいろいろアプローチがあるらしい(´д｀)

AIの機械学習はどの問題を扱うかで、何を「学習」ととらえるかが変わるらしいです（逆も然り）

**①回帰問題（大量のデータからなんか共通する特徴を抽出）** 

⇒たぶん微分方程式の解の定性や存在

②**分類問題（大量のデータのうち、特徴となる部分の分布の境界を捕縛する）** 

⇒境界の関数の各点での符号等

____

なお、本の書き方によると、**どうもこれ以外の捉え方＝「～問題」もあるっぽい** です。上記の本ではこの２つをメインに扱っています。あと、これはあくまで予想なのですが、カーネル法というのが、今後でてきて内積がやたらとでてきていたので、たぶん「 **リースの表現定理** 」というやつを多用する気がします

<h2>なんかフーリエ級数とか今後でてきそう匂いがしたので、自分の卒論を参考資料にUPしておきます。

これまで社会人になってから一度も使うことなかったので、皆さんで成仏させてやってください(っ∀`)ﾉ゛（※閲覧権限を付与してあります）

https://drive.google.com/drive/folders/1hY-GzTwDZge4atoaEwyUIbLKWQjHv0lE

（がっつり**フーリエ級数の話**です。※$\varepsilon-\delta$ 論法とか不備あるかもですが、一応担当教授にみてもらったやつなので、留数計算とかは何回も確かめ記憶があるので、たぶん合ってます。「**リーマン・ルベーグの補助定理**」とかいう**全然「補助」じゃないやつ**がキーとなります。
なお、個人情報はAcrobatで抹消しました）

※これは以下の参考文献『**Fourier Series (Georgi P. Tolstov 著, Richard A. Silverman 英訳**)』の第1～3 章の読解＆要約＆（数学ではあるあるの）行間を当時の力で埋めたものです。
※以下のAmazonのものより版が古く、なんかもっと暗い色の表紙だった気がします。

https://www.amazon.co.jp/Fourier-Georgi-P-Tolstov/dp/161427715X

※一応、リンク切れてるとあれなんでGIthubにも追加しました（上記の資料と同じです）

https://github.com/thinking-weed/analysis-statics-memo

あと大学で数学を少し学んだ経験則をまとめました。もしよければ、参考に

https://qiita.com/thinking-weed/items/a841534b0cfd7ec96194

<h1>参考資料一覧（※全部の内容を自分も理解できているわけではないです。）

①統計検定1級公式問題集　実務教育出版　
　※過去問のページ　https://www.toukei-kentei.jp/prepare/kakomon/
②青チャートI＋A、Ⅱ＋B　数研出版
③理工系の微分積分学　吹田信之、新保経彦　共著　学術図書出版書
④線形代数入門　齋藤正彦　著　東京大学出版会
⑤定本　解析概論　高木貞治　著　岩波書店　
⑥数学シリーズ　数理統計学　改訂版　稲垣萱生　著　裳華房
⑦数学シリーズ　集合と位相　内田伏一　著　裳華房
⑧one point 推定と検定　鷲尾泰俊　著　共立出版
⑨電磁場とベクトル解析　深谷賢治　著　岩波書店
⑩基礎数学８〔新版〕複素解析　高橋礼司　著　東京大学出版会（**留数計算という超面倒くさい積分の計算を学ぶのにかなり重宝します**。数学書にしては、珍しいタイプのかなり丁寧な本です。）
⑪岩波　数学入門辞典　
青本和彦・上野健爾・加藤和也・神保道夫・砂田利一・高橋陽一郎・深谷賢治・俣野　博・室田一雄　共著（日本数学界の重鎮たちによる数学用語の辞書。大概何でも載ってるが、あくまでヒントしかくれない(-_-;)）
【IT企業への転職を考えている方へ】塾⇒IT企業に転職して驚いた3つのこと

この記事は[Ateam Finergy Inc. × Ateam Wellness Inc. Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ateam-finergy-wellness)の19日目の記事です:seedling:

# :raising_hand_tone1:自己紹介
2023年から株式会社エイチームフィナジーで
コンテンツマーケティング（SEO）を担当しております
「ぱせり」と申します:seedling:

前職では地方の個別指導塾でスタッフを2年経験し、
同社のサービスサイト運営・管理を担当しておりました。
塾のサービスサイト運営からIT企業のコンテンツマーケティングという 
**「異業界×異業種」に転職した人** です！

# :bulb:IT企業へ転職して驚いた3つのこと
元々、SEOをがっつりやっていたわけでもなければ、
IT企業に属していたわけでもなかったので、
入社してからは驚くことばかりで、
今も刺激的で楽しく学びながら毎日働いています。

そんな中でも特に驚いたこと3つについてご紹介できればと思います！
今後転職を考えている方の参考になれば幸いです。

:::note info
**【IT企業へ転職して驚いたこと】**
1\. 検索力の速さ
2\. エンジニア・デザイナーと直接話せる
3\. チャットが活発
:::

## 1. 検索力の速さ
今の会社に転職してきて一番最初に驚いたのが検索力の速さです。

ついさっき一言、二言前に話していた
「この用語ってどういうこと？:thinking:」と出た疑問に対して、
誰かが会話しながら用語について検索をして
「こういうことです:sunglasses:」とすぐに疑問を解決するのです。

これが会議や普段の雑談で何度も行われ、

「なんやこの検索力……！！！」
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627639/b69bbf93-878c-babb-bf87-62d0e0597066.png">
と、そのスピード感に圧倒されていました。

:::note info
誰かが調べて説明してくれるのを待つのではなく、
自分ですぐにググって、情報を精査し、自分の中で咀嚼し、
周りにアウトプットする。
:::

社会人にとっては当たり前の行動かもしれませんが、
そのスピード感が前職とは段違いでした。

この光景を見てから、私は常にGoogleが手放せなくなりました。
（私生活でもすぐに調べる癖がついてきてます）

## 2.エンジニア・デザイナーと直接話せる
2つ目は、実際にシステムやUIを構築しているエンジニア・デザイナーの方々と会話ができるという点です。

前職では、サイトの構築を外注しており、
外注先の担当者の方としか直接やり取りができませんでした。

「どんな方が担当しているのか分からん…」

　　　<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627639/8b4cc16c-9405-584e-a19a-3d6f95c3a8f8.png">

といったような状況で、
担当者の方と「高度な伝言ゲーム」を行っている感覚でした。

:::note info
それがいまは実際にサイトを作っている人たちがすぐそばにいて、直接話をすることができる！
:::

これはインハウスIT企業ならではの「驚き」かつ「大きなメリット」だなと思いました。

また、「これってできるのかな…？」みたいなアイデアも
エンジニア・デザイナーの方々に直接確認して
できるかどうかの判断がすぐにつきやすいのもメリットだと思います。

## 3. チャットが活発
3つ目は、社内での情報共有が活発な点で、
弊社ではSlackを用いて様々な情報が飛び交っています。

SEOについて、何か最新の情報が出たら
すぐにチーム内のチャットで誰かが発信してくれます。

また、競合や自社の動きも何かあればチャットで共有があります。

「なぜこれらの動きがこんなにも活発なのかな…？」

　　　<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3627639/03c95e64-e833-6ccc-33d0-9a1003eeb888.png">

と考えたときに思い浮かんだことが、

:::note info
必ず誰かが発信した情報には何かしらの反応がある
:::

ということです。

情報が発信されたら、周りの人間は読んで終わりではなく
それに対してのコメントや絵文字でリアクションを誰かしら返しています。

発信した情報に対して反応があるから、
発信した人もされた人も「次も発信しよう！:sunglasses:」という
好循環が生まれているのだろうなと感じました。

# :bulb:まとめ
転職前は、異業界×異業種ということもあり、正直かなりビビっていました。

「ネガティブな驚きが多かったらどうしよう」
「なじめるかな、楽しめるかな」
「自分がこの会社で活躍できるかな」

上記のような不安ばかり抱えていましたが、
転職してからは、記事に書いた3つのことだけでなく
ポジティブな驚きがたくさんありましたし、
未経験の私にやさしくサポートしてくださる環境があり、
今も楽しく働けています。

これからもともに働くメンバーに良い刺激をもらいながら
日々頑張っていきたいと思います:muscle_tone1:

最後までお読みいただきありがとうございました！
zshで環境変数RBENV_VERSION, NODENV_VERSIONの値を補完する

# やりたいこと

```console
$ RBENV_VERSION=<Tab>
```

でインストールされているrubyのバージョン（2.5.7, 3.0.5, ...）を補完したい。nodeでも同様。

# 実装

下記ファイルを`$fpath`のいずれかのディレクトリに配置する。$fpathは`print -l $fpath`で調べられる。

`_rbenv_versions`
```zsh
#compdef -value-,RBENV_VERSION,-default-

local -a _versions
_versions=( ${(@f)"$(rbenv versions | sed -e 's@^..@@' -e 's@ .*@@')"} )
_describe -V -t versions Versions _versions
```

`_nodenv_versions`
```zsh
#compdef -value-,NODENV_VERSION,-default-

local -a _versions
_versions=( ${(@f)"$(nodenv versions | sed -e 's@^..@@' -e 's@ .*@@')"} )
_describe -V -t versions Versions _versions
```

~/.zcompdumpを削除してzsh再起動する。

<details><summary>#compdefの説明</summary>

>For the -value- context, the form is ‘-value-,name,command’, where name is the name of the parameter on the left hand side of the assignment. In the case of elements of an associative array, for example ‘assoc=(key <TAB>’, name is expanded to ‘name-key’. In certain special contexts, such as completing after ‘make CFLAGS=’, the command part gives the name of the command, here make; otherwise it is empty.
>
>It is not necessary to define fully specific completions as the functions provided will try to generate completions by progressively replacing the elements with ‘-default-’. For example, when completing after ‘foo=<TAB>’, _value will try the names ‘-value-,foo,’ (note the empty command part), ‘-value-,foo,-default-’ and‘-value-,-default-,-default-’, in that order, until it finds a function to handle the context.

https://zsh.sourceforge.io/Doc/Release/Completion-System.html
</details>


## 実装2

~/.zshrcに１行で
```zsh
compdef -e '_versions=(${(f)"$(nodenv versions | sed -e "s@^..@@" -e "s@ .*@@")"}); _describe -V -t versions Versions _versions' '-value-,NODENV_VERSION,-default-'
```

と書いても同じ。

# 環境変数名も補完
なお~/.zshrcに
```zsh
zstyle ':completion:*' fake-parameters {NODENV_VERSION,RBENV_VERSION}:string
```
と書いておくと、環境変数NODENV_VERSION, RBENV_VERSIONが定義されていないときでも
```console
$ RBENV_V<Tab>
```
のように環境変数名を補完できるようになるので便利。
これからC#を学ぶために

# はじめに

これからC#を学ぶために、活用したい情報をまとめました。
本を買って学ぶのもいいですが、新しい機能のキャッチアップは難しいので
効率よく勉強していきたいですね。

# Microsoft  C# プログラミングの学習

初心者から上級者まで、チュートリアルを通してわかりやすく学べます。コンテンツのボリュームがすごい。

https://learn.microsoft.com/ja-jp/dotnet/csharp/

# paiza.io

ブラウザ上でプログラムを書いて実行できます。
C#以外の言語にも対応していますので、環境構築なしで気軽に使えるのが良いです。

https://paiza.io/projects/frLnCyG9IiN0CTJAW5XR8A

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/9b4383cc-06de-b433-40e2-e922fc3b1901.png)

# .++C++; // 未確認飛行 C

C#によるプログラミング入門
プログラムを作るうえでの考え方やアルゴリズムなど、わかりやすくまとまっていて良くお世話になっています。

https://ufcpp.net/study/csharp/

# DOBON.NET

逆引きリファレンスとして活用しています。
サンプルコードがすぐに使えて便利です。

https://dobon.net/
データフェッチング: 2つの一般的なアプローチの比較

## はじめに
データの取得は、どのプロジェクトにおいても不可欠な重要な技術です。データの取得方法を理解し、効果的に利用することは、ユーザーエクスペリエンスを最適化するだけでなく、アプリケーションの構築と管理の方針を形成するのにも役立ちます。本記事では、HTTPリクエストのデータ取得において`try-catch block` と `destructuring request`を使用してデータ取得処理という2つの一般的なアプローチを比較してみましょう。

## try-catch block の使用
try-catch block は、伝統的なアプローチで、データ取得操作中に発生したエラーを捕捉するために使用されます。各HTTPリクエストは`try`ブロックで包まれ、発生したエラーは`catch`ブロックで処理されます。これにより、エラーを処理し、適切なアクションを実行する強力な手段が提供されます。


```javascript
try {
  const response = await useFetch('example_api_end_point')
  const data = response.data
  return data
} catch (error) {
  console.error('Error when feching data:', error)
  throw error
}
```

## destructuring request の使用:
`destructuring request` は、HTTPリクエストの結果からデータを抽出するための簡潔なアプローチです。包括的な try-catch ブロックの代わりに、直接 data と error の値を抽出するために destructuring 構文を使用します。これにより、コードが簡潔で読みやすくなります。
```javascript
const { data, error } = await axios.get('example_api_end_point')
if (error.value) {
  console.error('Error when feching data:', error)
  throw error
}
```

## 比較

`try-catch block` と `destructuring request` は、リクエストの処理において共通の手順に従います。具体的には、両者ともデータの取得が成功した場合とエラーが発生した場合の両方に対するデータ処理メカニズムが備わっています。しかし、それらは使用方法においてもいくつか違いがあります。

`destructuring request`の使用では、エラーの処理はそのAPIからデータを取得する部分だけに限定されます。一方、`try-catch`では、`try`ブロック全体が対象となり、そのブロック内のどこかでエラーが発生した場合、エラーは投げられます。つまり、フェッチ部分が正常に機能していても、周辺のコードからエラーが発生した場合、エラーも投げられます。

```javascript
try {
  /*
    Let's say this section has an error
  */
  const response = await useFetch('example_api_end_point')
  const data = response.data
  return data
} catch (error) {
  console.error('The error occurred inside the try block:', error)
  throw error
}

→ result：error
```


Destructuring requestは柔軟性が高いです。dataやerrorだけでなく、より多くのプロパティを代入できます。例えば、Nuxt3のuseFetchを使って以下のようにできます。

```javascript
const { data, pending, error, refresh } = await useFetch(`example_api_end_point`)
/* 
    Handling go here
    pending: a boolean indicating whether the data is still being fetched.
    refresh/execute: used to refresh the data returned by the handler function.
*/
```

## 使い方

### 1. Destructuring requestの使用時:
1. **シンプルな要件**: 基本的なケースだけを扱い、dataとerrorを抽出するだけで十分な場合。

2. **コードが短い**: シンプルなケースでは、destructuring requestを使用することでコードが短くなり、読みやすくなります。

### 2. try-catchの使用時:
1. **複雑な要件**: 複数のエラータイプを扱うか、エラーが発生した際に複雑なロジックを追加する必要がある場合。try-catchを使用すると柔軟にコードを拡張できます。

2. **エラー処理の調整**: エラー処理の挙動を細かく制御したい場合。エラータイプに基づいて特定のアクションを実行するなど。

3. **エラーに関する明示性**: エラーに関する詳細情報が必要な場合。トレースバックやカスタムエラーメッセージなど。

### 3. 組み合わせる場合:
両方のアプローチの利点を活かすために、両方の手法を組み合わせることができます。例えば、destructuring requestを使用して基本的なケースを処理し、より複雑な状況に対処するために周りにtry-catchを追加することができます。これにより、シンプルなケースではコードが短く保たれ、複雑な状況に対処する柔軟性も保たれます。

## おわりに
以上が、destructuring request と try-catch の使い方に関する基本的な情報です。プロジェクトや開発チームの要件によって、どちらのアプローチが適しているかを選択することが重要です。シンプルなケースでは destructuring request を使用してコードを短くし、複雑な状況では try-catch を活用して柔軟なエラーハンドリングを行うことで、効果的にデータの取得を管理できるでしょう。
【React】クリックに関するイベント【JavaScript】

クリックに関するイベントは次のとおり。

| イベント | 説明 |
| ---- | ---- |
| mousedown | 要素上でマウスが押下された |
| mouseup | 要素上でマウスが離された |
| click | 要素上でマウスが押下され、離された |

イベントの発生順は次のとおり。

`mousedown` → `mouseup` → `click`

`blur`との関係は次のとおり。

`mousedown` → `blur` → `mouseup` → `click`
Ubuntu Proが使えないときの対処法

## はじめに
　Ubuntu Proを使うためにトークンを登録しようとしたときに、「コマンドがありません」と表示されて困ったことはないでしょうか？（ほとんどの人はないと思いますが。）私みたいな変態?のためにその対処法を記事にしておきます。

## インストール
　`apt`コマンドを用いてパッケージの更新をしてから、Ubuntu Proのパッケージをインストールします。
```shell
sudo apt -y update&&sudo apt -y upgrade&&sudo apt -y autoremove
sudo apt -y install ubuntu-advantage-pro
```

## おわりに
　これだけで解決するぐらいなら、コンテナのイメージ内などにも最初からインストールしておいてほしいですね。これからもニッチな記事を執筆していきたいと思います。
【Ruby Gold3.1詰まったシリーズ】ヒアドキュメント確認してみた

# はじめに
今回はヒアドキュメントに関する内容です。ヒアドキュメントの識別子としてEOFがあり、EOFの前につける文字に応じてEOFの挙動が変わることは理解していました。しかし、具体的にどのように変わるのかが自分の中で整理できていなかったため、調査しまとめてみます。

# EOFの種類
ruby gold試験に+EOF、-EOF、~EOFが出題されていたのですが、+EOFは存在しなさそうなので、EOF、-EOF、~EOFの動きを確認していきます。（試験では存在しないものも出題されるのでひっかけ問題に要注意です...）

# 確認してみた
環境はruby2.5.3です。
まず、EOFを利用した下記コードを実行しました。
~~~ruby:EOF
s = <<EOF
    Hello,
    Ruby
  EOF
puts s
~~~
~~~:結果
irb(main):001:0> s = <<EOF
irb(main):002:0"     Hello,
irb(main):003:0"     Ruby
irb(main):004:0"   EOF
irb(main):005:0" puts s
irb(main):006:0" 
irb(main):007:0" 
irb(main):008:0" 
irb(main):009:0" 
~~~
すると、処理が実行されませんでした。
調査してみると、どうやら2個目のEOFをインデントすることはできないみたいです。
そのため、2個目のEOFをインデントしないバージョンで実行しました。
~~~ruby:EOF（インデントしない）
s = <<EOF
    Hello,
    Ruby
EOF
puts s
~~~

~~~:結果
irb(main):001:0> s = <<EOF
irb(main):002:0"     Hello,
irb(main):003:0"     Ruby
irb(main):004:0" EOF
=> "    Hello,\n    Ruby\n"
irb(main):005:0> puts s
    Hello,
    Ruby
=> nil
~~~
すると、今度はsにはテキストが格納され、putsでテキストを表示できています。
sに格納されたテキストには空白が含まれています。

次に、~EOFを利用した下記コードを実行しました。
~~~ruby:~EOF
s = <<~EOF
    Hello,
    Ruby
  EOF
puts s
~~~
~~~:結果
irb(main):001:0> s = <<~EOF
irb(main):002:0"     Hello,
irb(main):003:0"     Ruby
irb(main):004:0"   EOF
=> "Hello,\nRuby\n"
irb(main):005:0> puts s
Hello,
Ruby
=> nil
~~~
すると、sに格納されたテキストには空白が含まれていません。

最後に、-EOFを利用した下記コードを実行しました。
~~~ruby:-EOF
s = <<-EOF
    Hello,
    Ruby
  EOF
puts s
~~~
~~~:結果
irb(main):001:0> s = <<-EOF
irb(main):002:0"     Hello,
irb(main):003:0"     Ruby
irb(main):004:0"   EOF
=> "    Hello,\n    Ruby\n"
irb(main):005:0> puts s
    Hello,
    Ruby
=> nil
~~~
すると、sに格納されたテキストには空白が含まれています。

# おわりに
確認した結果、EOFと-EOFともに空白を含めてテキストを格納できることと、~EOFのみ空白を含めずにテキストを格納できることが分かりました。EOFに関しては、2個目のEOFをインデントすることはできないことも分かりました。今回比較しながら何が違うのか整理し納得できたので非常に良かったです！もし誤っている箇所がありましたらご指摘いただけると助かります！

# 参考

https://www.amazon.co.jp/gp/product/B0756VF9Y3/ref=ppx_yo_dt_b_d_asin_title_o00?ie=UTF8&psc=1

https://qiita.com/heysan/items/82bdfb7af4325f05e0e5
UnsafeAccessorでprivateメソッドをテストしよう

# はじめに

この記事はC# Advent Calendar 2023の14日目の記事です。
13日目の記事は @sh1ch による[C# TimeProvider の利用について (.NET8)](https://qiita.com/sh1ch/items/936945705d0c7a4ab655)でした。

# 注意

この記事中にはprivateメソッドをUnitTestすることの是非は取り扱いません。
・・・でもUnitTestしたくなりますよね？

# UnsafeAccessorとは

[UnsafeAccessor](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.unsafeaccessorattribute?view=net-8.0)とは.NET8.0で登場した属性で、今までリフレクションを使用しないと呼び出せなかったprivateメソッドなどにアクセスできるようになるものです。

# 今まで

今までpublicにしたくないがUnitTestをしたいというメソッドをどうしていたかというと[InternalsVisibleToAttribute](https://learn.microsoft.com/ja-jp/dotnet/api/system.runtime.compilerservices.internalsvisibletoattribute?view=netcore-3.1)を使う方法が主流だったのではないかと思います。

具体的に言えばcsprojに以下のような定義を追加していました。

```xml
<ItemGroup>
    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
        <_Parameter1>UnitTestProjectName</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>
```

更に昔には`AssemblyInfo.cs`というファイルを用意し、以下の定義を使っていました。

```cs
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("UnitTestProjectName")]
```

そしてテストしたいメソッドは以下のように定義していました。

```cs
public class TestTarget
{
    // UnitTestから呼ぶためにinternalで定義
    internal int SomeMethod() => 1;
}
```

そしてUnitTestのコードは以下のようになるかと思います。

```cs
public class TestTargetUnitTest
{
    [Fact]
    public void SomeMethod()
    {
        var target = new TestTarget();
        Assert.Equal(1, target.SomeMethod()); // internalへはアクセス可能
    }
}
```

これの何が問題かというと、公開したくないメソッドなのに[internal](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/internal)になっているせいで同じアセンブリ内からアクセスされてしまうという点です。
また`InternalsVisibleTo`属性の都合上、UnitTestプロジェクトの名前を参照される方のプロジェクトに追加する必要があるという問題もあります。

# これから

.NET8.0で`UnsafeAccessor`を使うと以下のように記述できます。

```cs
public class TestTarget
{
    private int SomeMethod() => 1;
}
```

そう。普通にprivateで定義するだけです。
そしてUnitTest側では以下のように記載します。

```cs
public class TestTargetUnitTest
{
    [UnsafeAccessor(UnsafeAccessorKind.Method, Name = "SomeMethod")]
    static extern int SomeMethod(TestTarget testTarget);

    [Fact]
    public void SomeMethodTest()
    {
        var target = new TestTarget();
        Assert.Equal(1, SomeMethod(target));
    }
}
```

このように前述した問題点がすべて解消しています。
`UnsafeAccessor`自体は.NET8.0の機能ですが、ご覧いただいた通り呼び出し側(UnitTest側)でしか使用されないため**テスト対象のプロジェクトの`TargetFramework`を変更する必要はない**という点もうれしいですね。
Unity用にnetstandard2.1を使用しているようなクラスライブラリにも使用できるのがありがたいのではないでしょうか。

# PrivateProxy

この機能を使ったライブラリとして @neuecc さんの[PrivateProxy](https://github.com/Cysharp/PrivateProxy)というものがあります。
こちらについてはご自身の解説記事も[こちら](https://neue.cc/2023/09/21_privateproxy.html)にありますので合わせてご参照ください。

# まとめ

これによりテストしたいがために`internal`にしていたメソッドを`private`にすることができ、より安全なクラス設計ができます。
ただしご想像の通り悪用するといろんなことができてしまいますので乱用はほどほどに。。。
【Django】staticファイルの404エラーはDEBUGをTrueにすれば良い

# 概要
Djangoをローカル環境で起動したら、なぜかJSなどの静的ファイルが読み込まれない404エラーに...。
おかしいなぁ本番環境では正常に動いているプログラムなのに...と思ったら、思わぬところに原因がありました。

# 原因は`DEBUG`設定
原因は、Djangoの`DEBUG`設定にありました。
`settings.py`で明示的に`DEBUG=True`と追記したら無事に読み込まれるようになりました。

調べてみると多くの場所で既にこの現象の対応策が書かれていました。
stackoverflowでもそんなコメントが盛りだくさん。

> On localhost, make sure to define DEBUG = True on settings.py
引用元：[Django Static files 404](https://stackoverflow.com/questions/12809416/django-static-files-404)

また、以下の記事にはこのように記載がありました。

> DEBUG=True と設定すると、本来Webサーバーを経由してstaticやmediaのファイルを読み込むところをdjangoアプリ内のstaticとmediaを見に行くように設定することができます。
引用元：[【Django】debug=False設定時staticとmediaのファイルが読み込めない事象の解決](https://rurukblog.com/post/Django-debug-false/)

# なぜDEBUG設定でSTATICが読めるようになるのか？
`DEBUG = False`の状態では、Djangoは静的ファイルを提供するための役割を持っていません。

じゃあ、本番環境では`False`にしているけどどうしているの？といえば、静的ファイルを提供するのは通常、Webサーバー（NginxやApacheなど）の役割です。
Django自体が「静的ファイルの提供」という責任を持たない設計思想なのです。Webサーバに任せた方が効率的で高速だから、でしょう。

では、開発環境では一体誰が静的ファイルを提供してくれているのか？
というと、Django内蔵の開発用Webサーバー（runserver）です。

「え、`python manage.py runserver 0.0.0.0:8000`みたいなコマンド使ってないんだけど！？」っというケースもあるかもですが、`DEBUG=True`に設定すると、このrunserverが使用されるようになります。
そして、このサーバによって静的ファイルが提供されるようになるのです。
逆を言えば、Webサーバを使わない開発環境下では、runserverを使わないと静的ファイルが読み込まれません。
（nginxのdockerを利用してconfで設定するという方法もありますので、これは別途また記事にしたいと思います。あとは静的ファイル提供用のPythonライブラリもあるみたいです）

ただし、言わずもがな、デバッグ内容が本番でも見れると、詳細なシステム情報を取得されてしまう可能性があるので本番では使いません。[Django公式ドキュメント](https://docs.djangoproject.com/en/3.0/howto/static-files/)にもrunserverが自動的に動いてくれていることが書いてありますし、もちろん、開発環境でのみの利用が推奨されています。

> During development, if you use django.contrib.staticfiles, this will be done automatically by runserver when DEBUG is set to True (see django.contrib.staticfiles.views.serve()).

> This method is grossly inefficient and probably insecure, so it is unsuitable for production.

## runserverはどんな流れで処理しているのか？
処理の流れとしては以下になっていると考えられます。

①runserverが`STATIC_URL`(`settings.py`で行う`/static/`とかのこと)のリクエストをクライアントから受け取る
②Djangoはそれを確認し、静的ファイルとみなす
③runserverは設定ファイル（`settings.py`）で定義された`STATIC_ROOT`のパスを確認し、静的ファイルのルートディレクトリとして使用
④リクエストされた静的ファイルが`STATIC_ROOT`内に見つかった場合、runserverはそのファイルをクライアント（ブラウザなど）に返す

`static`ファイルをnginxから見せるような手間がいらなくなるので、ローカル環境で行う場合はrunserverを使った方が良いケースが多いかと思います。
wsl2上のDockerコンテナにssh接続してCursorエディタを使う

# はじめに
WindowsでCursorエディタを使いたいけど「Dev Containers」が使えないのでDockerコンテナ上で開発できないし、けどどうにかして使いたいなぁということで、色々と調べた結果、最終的にsshで接続してCursorからDockerコンテナ内に接続できたので、記念に初記事を投稿します。
（2024/01/09 追記）
[バージョン：0.22.0 から「Dev Containers」に対応したようです！ ](https://changelog.cursor.sh/? "更新履歴")
試してみましたが、無事にコンテナの中に入れることを確認しました。
以降、Cursorでコンテナの中に入るためだけにssh接続する環境を作る必要は無さそうです。

# wsl2上のDockerコンテナにssh接続する環境を作成
### 1. **ssh公開鍵の作成(WSL)**  
```ssh-keygen -t ed25519 -f .ssh/(公開鍵名)```

### 2. **Dockerコンテナの作成(WSL)**  
```Dockerfile
FROM ubuntu:20.04

USER root

# SSHサーバーをインストールする
RUN apt-get update && apt-get install -y \
    nano \
    openssh-server \
    && rm -rf /var/lib/apt/lists/*

# SSHでrootログインを許可する
RUN mkdir -p /run/sshd
RUN echo 'root:password' | chpasswd

# 公開鍵をコンテナにコピーする
COPY .ssh/ssh_test.pub /root/.ssh/authorized_keys

# 公開鍵のパーミッションを設定する
RUN chown root:root /root/.ssh/authorized_keys && \
    chmod 600 /root/.ssh/authorized_keys

# SSH設定を更新する
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

# SSHサーバーを起動する
CMD ["/usr/sbin/sshd", "-D"]
```

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

services:
  develop:
    build:
      context: .
    container_name: SSH_TEST
    tty: true  # コンテナ維持
    restart: always  # PC起動時に自動でコンテナ起動
    ports:
      - "2224:22"  # ホストの2224番ポートをコンテナの22番ポートにマッピング
```

### 3. 公開鍵をローカルPCへ持ってくる
```C:\\Users\\(ユーザー名)/.ssh/```

### 4. configファイルの設定（存在しない場合は新規作成）  
```C:\\Users\\(ユーザー名)/.ssh/config```
```
Host ssh_test
  HostName localhost  ※DockerコンテナのIPアドレス（localhostでOK）
  Port 2224      ※Dockerコンテナのポート番号
  User root    ※Dockerコンテナのユーザー名
  IdentityFile ~/.ssh/ssh_test  ※転送した公開鍵のパス
```

### 5. **configファイルと公開鍵の権限変更**（権限が広すぎるというエラーを回避）
```
icacls config /reset
icacls config /inheritance:r
icacls config /grant:r "$($env:username):(R)"

icacls.exe .\ssh_test /inheritance:r
icacls.exe .\ssh_test /grant:r "$($env:USERNAME):(R)"
```

# Cursorエディタから接続
1. Cursorエディタのコマンドパレットで```「Remote-SSH：Connect to Host」```を選択し、configファイルに追加した接続先を選択
1. パスワードを求められるのでssh公開鍵作成時に設定したパスワードを入力
1. 接続に成功するとこんな感じでコンテナ内に入れます↓

![スクリーンショット 2023-12-16 083725.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1213832/02b4c8c1-7c65-0f6b-eac4-c9d5da3dc123.png)

# おわりに
私の環境では上記の方法でCursorでDockerコンテナ内に接続することができました。
どなたかのお役にたったら幸いです。

最後まで御覧いただきありがとうございました。
開発者ツールのログExport

# 記事概要
画面開発をしている際に、開発者ツールを使って相手方に確認をして貰いたいときがよく出てくる。相手方に開発者ツールを起動して貰い、「コンソール」・「ネットワーク」タブの操作ログをExportして送付頂くためのブラウザの操作手順。

# 環境
対象ブラウザ=>「Microsoft Edge」

# コンソールのExport手順

1. 操作対象画面の表示
1. 開発者ツールの起動
    ➀ F12を押して、開発者ツールを起動する。
1. コンソールタブの選択
    ➀ 表示された開発者ツールから、コンソールを選択する。
    （選択肢にない場合「＋」からコンソールを選択する。）
    ➁ ログの保持に✓を入れる。
1. 画面操作
    ➀ コンソールログに残したい画面操作をする。
（ここで不具合調査している対象の操作等を行う。）
1. 操作ログのExport
    ➀ 画面操作終了後、開発者ツールのコンソールログが出力されている箇所で右クリックをして、「名前を付けて保存」を選択し、操作ログをExportする。

# ネットワークのExport手順
1. 操作対象画面の表示
1. 開発者ツールの起動
    ➀ F12を押して、開発者ツールを起動する。
1. ネットワークタブの選択
    ➀ 表示された開発者ツールから、ネットワークを選択する。
（選択肢にない場合「＋」からネットワークを選択する。）
    ➁ ログの保持に✓を入れる
1. 画面操作
    ➀ ネットワークログに残したい画面操作をする。（ここで不具合調査している対象の操作等を行う。）
1. 操作ログのExport
    ➀ 画面操作終了後、開発者ツールのネットワークログが出力されている箇所で右クリックをして、「コンテンツを含むすべてをHARとして保存する」を選択し、操作ログをExportする。
【Rails・Next.js】devise-token-authによるログイン認証 Part2

# はじめに
今回の記事は【Rails・Next.js】devise-token-authによるログイン認証 Part2です。
Part1はこちらです。

https://qiita.com/ke_sukesakuma/items/c512af1e9b8c1eeb38e5


# deviseの日本語化

devise関連ファイルを日本語化していきます。
今回は認証URLを含んだメールの日本語化を目的としています。

```ruby
rails g devise:i18n:locale ja
```

```ruby:config/application.rb
module Myapp
  class Application < Rails::Application
    #省略
    config.i18n.default_locale = :ja
  end
end
```

# 認証メールの設定
今回は`letter-opener-web`を用いて、メール認証をしていきますがその際に送信元のドメインを定義する必要があります。

```ruby:config/environments/development.rb
Rails.application.configure do
  
  config.action_mailer.default_options = { from: "no-replay@example.com" }
  config.action_mailer.default_url_options = { host: "localhost:3000" }
end
```
## letter_opener_web の導入

```ruby:Gemfile
 gem "letter_opener_web"
```

letter_opener_web 用のルーティングを追加します。

```ruby:config/routes.rb
Rails.application.routes.draw do
 mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.development?

end
```
追加の設定を`config/environments/development.rb`に追加します。

```ruby:config/environments/development.rb
Rails.application.configure do
  
  config.action_mailer.default_options = { from: "no-replay@example.com" }
  config.action_mailer.default_url_options = { host: "localhost:3000" }
  config.action_mailer.delivery_method = :letter_opener_web #追加
end
```
サーバーを再起動したあと、 `http://localhost:3000/letter_opener` にアクセスしてください。

この画面が表示されていれば設定完了です。

![スクリーンショット 2023-12-14 20.54.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517817/ed13a163-a35f-aaa8-a38f-7ccdc9d59d10.png)


# Thunder Clientの導入
https://marketplace.cursorapi.com/items?itemName=rangav.vscode-thunder-client

Partの最後に`HTTPクライアントツール`である`Thunder Client`を導入します。
`VSCode`の拡張機能のため、インストールは問題なくできるかと思います。

![スクリーンショット 2023-12-14 20.58.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517817/fb3af67d-6f41-957a-5467-b77138c9e3e2.png)

サイドバーより`New Request`を押下して作成することができます。
任意のドメインを入力することでリクエストの疎通を確認することができます。
![スクリーンショット 2023-12-14 21.01.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517817/e3b611a4-49d1-7087-9633-19c993ed8a33.png)


各タブで確認することができます。

`Query`、`Headers`、`Body`あたりがよく使われます。

![スクリーンショット 2023-12-14 21.01.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517817/41cc9bfc-cce3-a931-e2e5-dd58a0afdc99.png)


![スクリーンショット 2023-12-14 21.01.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517817/62b6103b-0a5c-cee8-842f-3b7d5c696c2b.png)
Node-REDのライブラリまとめ

# はじめに

Node-REDには便利なライブラリがいっぱいあります。
以下のページで検索することが可能です。
個人的に興味のあるものについてまとめます。

https://flows.nodered.org/

# OpenAIのGPT-4 Vision API

画像を入力して、プロンプトに沿った回答をしてくれます。

https://flows.nodered.org/node/node-red-contrib-simple-gpt-vision

# OpenAI(ChatGPT)

https://flows.nodered.org/node/node-red-contrib-openai

# LINE

LINE Messaging APIやLINE Notifyが利用できます。

https://flows.nodered.org/node/node-red-contrib-line-messaging-api

# SpreadSheet

https://flows.nodered.org/node/node-red-contrib-google-sheets

https://flows.nodered.org/node/node-red-contrib-spreadsheet-in

# influxdb

https://flows.nodered.org/node/node-red-contrib-influxdb

# 通信 : OPC UA

https://flows.nodered.org/node/node-red-contrib-opcua

# 通信 : MC Protocol

https://flows.nodered.org/node/node-red-contrib-mcprotocol

# 参考ページ

https://qiita.com/kazutxt/items/8c72448eefcba56dabef
自分用YKKK

```

output = {
  "都市": 0,
  "娯楽": 0,
  "活発": 0,
  "食事": 10,
  "お金": 9
}

change = {
    "都市":"自然",
    "娯楽":"学び",
    "活発":"のんびり"
}


# 各カテゴリに特定の条件に基づいて偏差を計算する関数
def calculate_custom_deviation(scores):
    center = 5
    deviations = {}
    for category, score in scores.items():
        if category in ['食事', 'お金']:
            # '食事' と 'お金' の場合、5からの正の偏差を計算する
            deviations[category] = max(0, score - center)
        elif category == "都市":
            # 他のカテゴリーの場合、5からの絶対偏差を計算する
            if score - center > 0:
                deviations[category] = abs(score - center)
            else:
                deviations[change[category]] = abs(score - center)
        elif category == "娯楽":
            if score - center > 0:
                deviations[category] = abs(score - center)
            else:
                deviations[change[category]] = abs(score - center)
        elif category == "活発":
            if score - center > 0:
                deviations[category] = abs(score - center)
            else:
                deviations[change[category]] = abs(score - center)
        else:
            # 他のカテゴリーの場合、5からの絶対偏差を計算する
            deviations[category] = abs(score - center)
    # 値の大きい上位２つを抽出
    top2_deviations = dict(sorted(deviations.items(), key=lambda item: item[1], reverse=True)[:2])
    return top2_deviations


# すべてのカテゴリーに対するカスタム偏差を計算する
custom_deviations = calculate_custom_deviation(output)
print(custom_deviations)

```
Vim小技: 今書いているVimスクリプトを試す

※小ネタです。備忘録として。

Vimプラグインなどを作成している場合に、今開いているスクリプトを読み込んで試したい場合は、以下を実行すれば良い:
```
:source %
```
`source` は `so` という短縮形でも良い。公式ヘルプに記載あり：

https://vim-jp.org/vimdoc-ja/usr_41.html#41.1

`%` は Ex コマンド用の特殊文字でファイル名に展開される。

https://vim-jp.org/vimdoc-ja/cmdline.html#cmdline-special
第二世代　bq2040　i2c通信

「aibo ers-11x/210で遊ぶ Advent Calendar 2023」の19日目です。

ERS-210のバッテリーについて、Loïc POLLIERさんのレポジトリbattmonを参考に、一から勉強したメモをまとめています。
https://github.com/lpollier/battmon/tree/master

ERS-210のバッテリーの保護回路のGas gage ICから端子2へSBMC, 端子3へSMBDが接続されています。SBMC（クロック）とSBMD（データ）はSMBus通信という2線式インターフェースですが、ほとんどがI2Cと同様に扱うことができるとのことです。

今回はバッテリーのBQ2040とaibo本体の間のSMBus通信を、ESP32を使って監視してみました。スニファー（監視者）というらしいです。

:::note alert
警告
この記事について作者はいかなる責任も負いません。
バッテリーの分解、改造は非常に危険な行為です。
:::

:::note
業務外の個人的な検討活動です。
:::

#### 引き出し線の作製
バッテリーと本体の間でSMBus線をプローブできるように、プラグを延長しました。
その引き出し線の途中で端子2と3をプルアップしてESP32へ接続しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103446/24a3f6db-15a5-28a2-0bc5-b3e7f3e2e4ae.png)


#### "I2C sniffer on ESP32"
WhitehawkTailorさんのレポジトリーI2C-snifferを使わせていただきました。ありがとうございます。
https://github.com/WhitehawkTailor/I2C-sniffer

レポジトリにあるmain.cppの名前を変更して拡張子をinoとし、ArduinoIDEからESP32へアップロードしました。

#### 実行結果
- aibo本体にバッテリーを接続した直後の信号
```
SCL up: 1677 SDA up: 62 SDA down: 72 false start: 1653

1111s
S0001011W+00100001+1S0001011R+00000111+01000101+01010010+01000001+00110010+00110000+00110001+01000010-s
S0001011W+00010110+1S0001011R+11000000+00000010-s
S0001011W+00001000+1S0001011R+01101010+00001011-s
S0001011W+00001001+1S0001011R+00110000+
Start delay    
```
最初の1111sは分かりませんでした。
次のS0001011Wと00100001はaibo本体からの書き込みで、0x0BがBQ2040のデバイス番号、0x21がBQ2040へのデバイス名呼び出し命令であり、続けてaibo本体からは読み出しで、S0001011R+00000111+01000101+01010010+01000001+00110010+00110000+00110001+01000010-s
で、0x0B（BQ2040のデバイス番号）、0x07（デバイス名の長さ）, 0x45 (E), 0x52(R), 0x41(A), 0x32(2), 0x30(0), 0x31(1), 0x42(B)、つまりERA-201Bを回答しているようです。

BQ2040のデバイス名や命令は仕様書に書いてありましたので、対応を読んでいると楽しめます。
https://www.ti.com/lit/ds/symlink/bq2040.pdf?ts=1698077054205

デバイス名などはEEPROMの中身を呼び出しているようなので、EEPROMの内容と対比すると良さそうです。

#### 警告をもう一度
今回のAdvent Calenderでバッテリーセルの交換について考察しますが、非常に危険ですので知識と教養を深める目的で調べたことをまとめました。
十分な安全対策を講じなければ、実行することはお勧めできません。
重要なことなので、もう一度書きます。

:::note alert
警告
この記事について作者はいかなる責任も負いません。
バッテリーの分解、改造は非常に危険な行為です。
:::
アクセシブルなEPUBを作るには

## はじめに

ACCESS Advent Calendar 14日目です。

社名が「アクセス」なので、EPUB のアクセシビリティについての話をしようと思います。

今年5月、EPUB 3.3がW3C勧告(Recommendation)となりました。正式な仕様となる上でさまざまな変更点があるのですが、そのうちの一つがアクセシビリティ対応です。

EPUB 3.3では、アクセシビリティへの対応は「推奨」となっています。これは「必須」とするとこれまでのEPUBが仕様違反となってしまうためではあるものの、重要視されていることには変わりありません。

では、アクセシブルなEPUBとは何か？　簡単にではありますが考えてみたいと思います。

## 定義

EPUB 3.3のアクセシビリティはに関する要件は、同時に勧告となったEPUB Accesibility 1.1で定義されています。また同時に、　EPUBに埋め込むべきメタデータについても定められています。

https://www.w3.org/TR/epub-a11y-11/

## 要件

### WCAG に準拠したコンテンツの作成

コンテンツはウェブコンテンツにおけるアクセシビリティのガイドラインであるWCAG(Web Content Accessibility Guidlines)への適合が求められています。WCAG2.0のレベルAへの適合が必須条件であり、最新版（現在はWCAG2.2）のレベルAAへの適合が強く推奨されています。EPUBはHTMLやSVGなどWebの技術で構成されているため、これを参照しよう、ということですね。

### EPUBとしての要件

EPUB固有の要件としては、ページナビゲーション（目次）やテキストとオーディオの同期再生があげられます。これらは必ずしもなくても出版物やコンテンツとしては成立しますが、含める場合はEPUB仕様に沿った記述が求められます。

### メタデータに記載

出版物としての情報を定義するOPFファイルに、どのアクセシビリティに対応しているかなどを表すためのメタデータを記載する必要があります。これによって、ユーザはアクセシビリティに対応している電子書籍を探すことができ、ま
ビューアもメタデータを読み取ることで、ユーザにアクセシビリティの機能を提供できるようになります。

## 未来へ

今年9月、芥川賞を受賞された市川さんは、読書バリアフリーの普及を訴えました。

https://www.sankei.com/article/20230730-MGK6PWWD3RLVHMHZ6EKQHBJ4XI/

紙という物理的なくびきから解き放たれた電子書籍は、紙の本では難しかった文字サイズの変換や分かち書きといった様々な方法を提供できるはずです。

また、フィーチャーフォンがケータイ小説を生み出し、スマートフォンがウェブトゥーンと呼ばれる縦スクロール漫画を生み出したように、本というコンテンツは媒体によって形を変えています。逆に言えば、ユーザが求める形の新しい本もまた、EPUBというフォーマットの中で実現できると私は考えています。

一方、それを実現するためには、コンテンツだけでなくビューア（リーディングシステム）の対応も必要です。今回はリーディングシステムに対する仕様も制定されましたが、どのビューアがどういった機能に対応しているかは分かりにくいのが現状です。

https://www.w3.org/TR/epub-rs-33/

卵が先が鶏が先か、という話にもなりますが、物理本の再現が重視されてきたEPUBビューアはまだまだやれることがある、と考える次第です。

## 参考資料

https://www.jepa.or.jp/jepa_cms/wp-content/uploads/2023/07/EPUB-3.3_and_EPUB-A11Y-1.1_2023-07-12.pdf
Windows10をアップデートしたらペイントが使えなくなった

# 事象
昨日、夜間にWindows10が勝手にアップデートされていました。
今日も今日とてペイントを使おうとしたらこんな表示が。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287660/32307b4e-dbc9-0513-62b9-96213cbfe5ac.png)

「新しいドキュメントを作成できません」

それは困ります。

# 暫定的対処
以下のパスから32ビット版のmspaint.exeを開く。

`C:\WINDOWS\SysWOW64\mspaint.exe`

# 恒久的対処

なんとしてでも64ビット版を動かしたい！という場合は、コマンドプロンプトあるいはPowerShellを管理者権限で開き、次のコマンドを実行。

`dism /Online /add-Capability /CapabilityName:Microsoft.Windows.MSPaint~~~~0.0.1.0`

実行結果として、次のようなメッセージが出ると成功。

```
展開イメージのサービスと管理ツール
バージョン: 10.0.19041.3636

イメージのバージョン: 10.0.19045.3803

[==========================100.0%==========================]
操作は正常に完了しました。
```

日常が戻ります。

# 原因
というのも、今までは以下のパスにあるペイントをショートカットで起動していました。
`C:\WINDOWS\System32\mspaint.exe`

~~今回のアップデートでなぜか使えなくなった模様。~~

今回のアップデートでなぜ消えたのかは不明ですが、
`C:\WINDOWS\System32\ja-jp\mspaint.exe.mui`
がシステムフォルダから消えていたのが原因でした。

上記dismコマンドを実行することにより、`mspaint.exe.mui`がインストールされ無事64ビット版も使えるようになります。


(追記)
Microsoft Storeでペイントのシステム要件を見ると、こうなっていた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287660/8a86b738-14e6-6e26-241c-0cf21818db1c.png)

https://apps.microsoft.com/detail/9PCFS5B6T72H?hl=ja-jp&gl=JP

どうやら64ビット版の運用がWindows10で終了したらしい?
ということで、32ビット版なら現状のシステムで使えるようです。

# 参考
https://twitter.com/JurakuSoftware/status/1735256848926945458
https://www.winhelponline.com/blog/unable-to-create-new-document-wordpad-mspaint/