OmniAICreator/Qiita-1.07M huggingface dataset:
【AWS】invalid JSON formatエラーの解決方法。imagedefinitions.jsonとimageDetail.jsonの違いとは

# 概要

`buildspec.yaml`の記述の仕方が問題でAWS CodeDeployにて以下のエラーになりました。

```
最新のアクション実行メッセージ
The image definition file imagedefinitions.json contains invalid JSON format
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/5361e538-fa2c-5ccb-2fa4-3fcb78781f7d.png)

上記エラーを解決できたので紹介します。


## BEFORE

```buildspec.yaml
## 略
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest
      - echo Writing image definitions file...
      - cd ../
      - printf "{\"name\":\"%s\",\"imageUri\":\"%s\"}" $REPOSITORY $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest > imagedefinitions.json
      - ls -al
      - cat imagedefinitions.json
artifacts:
  files:
    - 'imagedefinitions.json'
  discard-paths: yes
```

## AFTER


```buildspec.yaml
## 略
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest
      - echo Writing image definitions file...
      - cd ../
      - printf '[{"name":"%s","imageUri":"%s"}]' $REPOSITORY $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest > imagedefinitions.json
      - ls -al
      - cat imagedefinitions.json
artifacts:
  files:
    - 'imagedefinitions.json'
  discard-paths: yes
```

変更点は、以下です。

```yaml
      - printf '[{"name":"%s","imageUri":"%s"}]' $REPOSITORY $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest > imagedefinitions.json
```

`imagedefinitions.json`を配列形式にしたらエラーが解消されました。
元のコードではただの単一のオブジェクトであり、配列ではなかったため、エラーになっていたのです。

## `imagedefinitions.json`と`imageDetail.json`の違いとは？

そもそもなぜ自分がJSON形式のオブジェクトで書いていたかというと、ECS Blue/Green デプロイアクション用の `imageDetail.json`ファイルでは、`[]`で囲ってあげていた記憶がなかったからです。
BG用の場合は、配列の中に入れてあげなくても正常にDodedeployが走ります。

と言うことで、公式ドキュメントを見てみます。

https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html

> Amazon ECS 標準デプロイでは、デプロイアクションへの入力で imagedefinitions.json ファイルが必要です。

> Amazon ECS Blue/Green デプロイでは、デプロイアクションへの入力で imageDetail.json ファイルが必要です。

```json
// imagedefinitions.json

[
  {
    "name": "sample-app",
    "imageUri": "11111EXAMPLE.dkr.ecr.us-west-2.amazonaws.com/ecs-repo:latest"
  }
]
```

```json
// imageDetail.json

{
"ImageURI": "ACCOUNTID.dkr.ecr.us-west-2.amazonaws.com/dk-image-repo@sha256:example3"
}
```

なぬ！
`imagedefinitions.json`と`imageDetail.json`とでは記述の仕方が違う...！

そうなんか...今まで気にしたことがなかった。

（`imageUri`と`ImageURI`とか、細かな表記方法も異なるようです）

## 補足：`printf`コマンド
`printf`コマンドは、指定された形式に従って文字列を生成するコマンド。

上記の場合、形式は`'[{"name":"%s","imageUri":"%s"}]'`となっており、`%s`は文字列を表すプレースホルダーを意味しています。

そして、形式の中のプレースホルダーを次に指定された引数で置き換えます。この場合、最初の`%s`は`$REPOSITORY`で、2つ目の`%s`は`$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY:latest`で置き換えられる、と言うわけです。
非同期リクエスト時の`AbstractController::ActionNotFound`エラーについて

## エラーの背景

あるRailsプロジェクトで、ユーザーがチェックボックスをクリックすると、選択された曜日に応じてビジネス時間を動的に更新する機能を実装しようとしました。この非同期通信のために、JavaScriptの`fetch` APIを使用して`/companies/update_business_hours`エンドポイントに対してPOSTリクエストを送信しました。しかし、リクエストを送信した際に次のエラーが発生しました。

```
AbstractController::ActionNotFound at /companies/update_business_hours
======================================================================
The action 'update_business_hours' could not be found for CompaniesController
```

## エラーの原因
'update_business_hours'メソッドを'public'ではなく、'private'や'protected'メソッドとしており、コントローラ内のメソッドからアクセスできなくなっていたため。

### JavaScriptからの非同期リクエスト

JavaScript（例えば、`fetch` APIを使用）からRailsのコントローラに非同期リクエストを送信する場合、リクエストは`public`メソッド（アクション）に対してのみ行うことができます。`private`や`protected`メソッドは、外部からのリクエストに対して直接アクセス可能なアクションとして使用することはできません。

```javascript
fetch('/articles', {
  method: 'GET'
}).then(response => response.json())
  .then(data => console.log(data));
```

上記の例では、`/articles`エンドポイントに対してGETリクエストを送信しており、これは`ArticlesController`の`index`アクション（`public`メソッド）にルーティングされます。

## （補足）各メソッドについて

### Publicメソッド

Railsコントローラの`public`メソッドは、HTTPリクエストを通じて外部から呼び出すことができるアクションとして機能します。これらは、アプリケーションのルーティング設定に基づいて、外部から直接アクセス可能です。

```ruby
class ArticlesController < ApplicationController
  # 外部からアクセス可能なアクション
  def index
    @articles = Article.all
  end
end
```

### Privateメソッド

`private`メソッドは、コントローラの内部でのみ使用され、外部から直接アクセスすることはできません。これらは、主にヘルパーメソッドやフィルターなど、コントローラ内の他のアクションからのみ呼び出されるメソッドとして使用されます。

```ruby
class ArticlesController < ApplicationController
  private

  # 内部でのみ使用されるヘルパーメソッド
  def set_article
    @article = Article.find(params[:id])
  end
end
```

### Protectedメソッド

`protected`メソッドはRailsのコントローラにおいて一般的ではありませんが、これらも外部から直接アクセスすることはできません。`protected`メソッドは、同じクラスまたはサブクラスのインスタンスからのみアクセス可能です。

## まとめ

Railsコントローラのメソッド可視性は、アクションのアクセス制御に重要な役割を果たします。JavaScriptからの非同期リクエストを含む、外部からのHTTPリクエストは、`public`メソッドに対してのみ行うことができます。`private`や`protected`メソッドは、コントローラの内部ロジックのために保護されています。
PHP でヘッダーを 'Content-Type':'application/json' に指定して POST したら意図しないリダイレクトでデータが消えて困った話

## 環境

- PHP
- Apache

## 解決策

POST 先に指定している URI の末尾にスラッシュをつける。

`localhost:3000/json` -> `localhost:3000/json/`

## 理由

Apache では設定によって URI に [Trailing Slash](https://wa3.i-3-i.info/word11910.html) と呼ばれる末尾のスラッシュが無い場合、末尾のスラッシュが存在する URI へリダイレクトさせられます。

このリダイレクトによって `'Content-Type':'application/json'` 指定で POST した値が消えていました。

この設定は、 [DirectorySlash ディレクティブ](https://httpd.apache.org/docs/2.4/ja/mod/mod_dir.html) と呼ばれるものです。



- Trailing Slash 参照元 : https://wa3.i-3-i.info/word11910.html
- DirectorySlash ディレクティブ 参照元 1 : https://httpd.apache.org/docs/2.4/ja/mod/mod_dir.html
- DirectorySlash ディレクティブ 参照元 2 : https://www.javadrive.jp/apache/ini/index23.html

## 雑記

`php:input` の値が空で、 以下の記事が有力な手がかりだったが、解決せず URI のスラッシュをいじったら解決した。

スラッシュ一つでかなり悩んでしまった。

https://muchilog.com/php-input-json-empty/
フレームワークに銀の弾丸を求めるプログラマの悪癖

# プログラマというものは

ツールやフレームワークが銀の弾丸になり、問題を解決してくれると思い込む傾向にありはしないだろうか。

だが特にコミュニケーションや組織の問題など、人間が対象となる分野において、ツールファースト、フレームワークファーストのやり方は必ずしも功を奏すとは限らない。ビジネスのように不確定要素が大きすぎる分野でもそうだ、

# なぜプログラマはツールに飛びついてしまうのか

ツールやフレームワークは始めるのは簡単で、簡単に飛びつくことができる。だからこそ簡単に飛びついてしまう。

確かに有用に働く場合もあるだろうが、全ての場合においてそうだとは限らない。

試してみるのも良いだろうが、問題解決にあたって、まず最初に試そうとするのがツールやフレームワークだということに危なさを覚える。

「ツールファースト思考」に陥ってないだろうか。

# 悪しきツールファースト

たとえば、一定時間の集中と休憩を繰り返すポモドーロテクニックというものがある。

このテクニック自体は有用だと思うが、まずテクニックを始めるにあたって「どのアプリを使ったら良いのか？」「どんなツールを使ったら良いのか？」と真っ先に会話に登るのは危ない。

何故ならこのポモドーロテクニックの場合、物理的なストップウォッチや、スマホのストップウォッチや、時計さえあればいつでも始められる。

なのに何故「ツールの選定が最優先事項であり、それに命運がかかっている」かのように考えるのか。

# 書籍ファースト

現実世界でもそうだが、プログラミングという領域においてさえ、書籍の中に答えがない事柄ばかりではないだろうか。

真っ先に「どんな書籍を読んだら良いか？」と考えるのは危ない。

# 成功の輸入

ビジネスの現場で「他社でうまく行った方法を我が社でも取り入れてみよう！」と経営陣がフラッシュアイディアで導入するものの、全くの失敗に終わる。

自分がその現場を経験したことがあるわけではないが、いかにも世の中で起きてそうな事例ではないだろうか。

「問題に対しての問題解決」
「何が問題で、どのように解決するのが最善か」

そういった洞察なしにまずは手法に飛びついてしまうのは危ない。

# ではどうすれば良いのか？

ツールやフレームワーク、手法なしでどうやって問題を解決し、物事を進めれば良いのだろう。

そのためにとっておきの斬新なツールがあるので、ここでお知らせ致します…！

ツールファースト思考に陥らないための夢のフレームワークの登場です…！

さあ、どうぞ…！


# 参考書籍
 
プロダクトマネージャーの仕事

<img width="629" alt="image" src="https://github.com/YumaInaura/YumaInaura/assets/13635059/2dcc9d25-ff1f-40e9-96fe-74e4be0c6b58">

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
Hello Worldからの復習 ～変数編～

スポ少からず～っとバスケをやってきた事を思い出すと、ずっとドリブルの練習したなぁ。
エンジニアになってからも基礎をしっかりしないと変なところでミスったりするので、無骨に愚直に理解をしよう。

**お品書き**
+ 変数って？
+ 使い方
+ コンパイル
+ おまけ
----

**変数って？**
-

##### データの入れ物。
例えば「a」という変数を作ったら「a」という入れ物がメモリ上にできる。
その中にデータを入れて計算したり、様々な処理に使ったりできる。

##### 変数名のルール
→1文字目
　英字(a～z、A～Z)、_、$

##### 2文字目以降は数字も使用可

##### キーワードは使用不可
 (public、static、void等の単体で意味を成してしまうもの)


 **使い方**
 -


今回はクラス名をSecondとします。(2回目だから)
 
##### 宣言
変数を作ること。
例)`int month ;`
この宣言で`month`という**変数**がメモリ上にできます。

```java:java
class Second{
	public static void main (String[] args){
		int month ; //データ型　変数名 ;
		month = 3 ;
		System.out.println();
	}
}
```

##### 代入
値を入れること。
例)  `month = 3 ;`
これによりメモリ上にある`month`という変数に`3`という値が**代入**されます。

```java:java
class Second{
	public static void main (String[] args){
		int month ; //データ型　変数名 ;
		mouth = 3 ; //変数名 = 値 ;
		System.out.println();
	}
}
```

ちなみに、変数名をカンマで区切れば一度に複数指定も可能
例)`int month , day ;`


```java:java
class Second{
	public static void main (String[] args){
		int month , day ; //変数を2つ宣言
		month = 3 ; 
		day = 21 ;
		System.out.println();
	}
}

```

※数学の「=」は右辺と左辺が同じという意味だが、代入の記号として使う場合は右の値が左の変数に格納される。

##### 参照
値を見ること。

```java:java
System.out.println(month + day);  //()内が参照するための記述
```

##### 初期化
宣言と代入を同時に行うこと。
例)`int month = 3 ;`
   `int day = 21 ;`
   これでint型の`month`という変数に`3`が代入され、int型の`day`という変数に`3`が代入される。

```java:java
class Second{
	public static void main (String[] args){
		int month = 3 ; //初期化
		int day = 21 ; //初期化
		System.out.println();
	}
}
```
System.out.println();の()内に表示したい参照の記述を書いてコンパイルしてみる。

```java:java
class Second{
	public static void main (String[] args){
		int month = 3 ;
		int day = 21 ;
		System.out.println("誕生日は" + month + "月" + day + "日です！");
	}
}
```
**コンパイルして実行**
-
前回同様ファイルのあるディレクトリで`cmd`でコンパイル

```cmd:cmd
C:\java\1>javac Second.java
C:\java\1>
```
コンパイルが通ったら実行。
```cmd:cmd
C:\java\1>java Second
誕生日は3月21日です！
C:\java\1>
```
私の**リアル誕生日**を世界中に公開。。

**おまけ**
-

```java:java
class Second{
	public static void main (String[] args){
		●int month;
		int day ;		
		    month = 3 ;
		    day = 21 ;
		
		▲int month , day ;
		    month = 3 ;
		    day = 21 ;
		
		■int month = 3 ;
		int day = 21;
		
		System.out.println("誕生日は" + month + "月" + day + "日です！");
	}
}
```
上記の●、▲、■の処理文ですが、全部同じ処理です。


-----

というわけで、第2回目もこの辺で。

まだまだ書き方が甘いけど、一歩ずつ着実に歩を進めていこう。
21日か、21回か続けば習慣になる。
.gitignoreを紛失した

.gitignoreなくした……。

## どうした？
GCPのJupiter Notebook上で、GitHubからクローンしてきたリポジトリに、.gitignoreを作り忘れていた。

慌てて作ろうとして調べたコマンドを打ってみたが、Jupiter Notebookのエクスプローラー上には何も出てこないし、lsしても.gitignoreが見当たらない。

**結論：隠しファイルなので、見えていないだけだった。**

## .gitignoreとは

Gitで、管理外にする（＝リモートリポジトリへのアップロードに含めない）ファイルを指定するためのファイルです。
中身はテキストファイルになっていて、テキストエディタで開いて編集することができます。

.gitignoreと拡張子だけのファイルなので、エディタによっては名前を変更できないそうです。また、隠しファイルなので、Windowsのエクスプローラーなんかで隠しファイルをみえる設定にしておかないと見えません。

たとえば、APIのキーや環境設定（env.txtなど)や、変更の管理が必要ないビルドファイル、大きめの素材ファイルなどは除外して、Gitで管理しないようにします。

これを忘れて秘密のファイルをGitHubのパブリックの場所にどーん、なんかしてしまうと、怒られが発生します。

## .gitignoreを追加する

### gitignoreの階層
Gitで管理されてるフォルダの一番上、README.mdと同じ階層でよい。

- cdコマンドなどを使用して、使いたい場所に移動する。

```
cd [移動したいパス]
```

../で上の階層へ移動、フォルダ名指定でそのフォルダの中に移動する。
pwdコマンドで現在の作業ディレクトリの絶対パスを表示。

- 以下のコマンドで、gitignoreを作成する。

```
touch .gitignore
```

nanoなどのテキストエディタで開いて、編集する。

```
nano .gitignore
```

## うっかり別のフォルダに作ってしまった場合

以下のコマンドで、.gitignoreを検索できる。

```
find . -name .gitignore
```

lsコマンドの場合は、隠しファイルを含めて表示するコマンドオプションを指定する。

```
ls -la
```

要らないところに作ってしまったら削除してあげよう。

```
rm .gitignore
```

## そもそもリポジトリを作るときに作っておこう……。
GitHubでリポジトリを作るときに追加しておいたら正しい位置にできるし、こういう問題は起こらないのだった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/663038/84a9b042-911c-1da6-8531-186a4458e80d.png)

## 参考、その他

https://docs.github.com/ja/get-started/getting-started-with-git/ignoring-files
Array#forEachのcallbackにasync関数をつかってはいけないよ

# TL;DR;
Array#forEach(などの)callbackに非同期処理が必要なときに、内部の処理で非同期処理が必要なときに使用すると怪我をするよという話です

# どういうこと?
Promiseを返す関数やAPIをloopでよびたいとします

```javascript
const images = [image1, image2, image3]
images.forEach(async(_image) => {
    const imagedata = await createImageBitmap(_image) 
    // なにか処理をする
})
```
これが完全に非同期な処理だけならなにも事件はおこらないのですが、内部でfoldingする処理をいれたとします
たとえばwidthの合計とか測定したいとか(そんな需要はほとんどなく、実際は取得したimagedataを処理して全部の結果をとかやるのですが)
```javascript
const images = [image1, image2, image3]
const allWidth = 0
images.forEach(async(_image) => {
    const imagedata = await createImageBitmap(_image) 
    allWidth += imagedata.width
})
console.log(allWidth)
```
実行後allWidthはいくつになるでしょうか?
画像の幅の合計値を期待していたかもしれませんが、答は「0」です


# 何が起こっているのか?
ArrayのforEachのcallbackは関数になりますが、createImageBitmapはPromiseを返します
内容をとりだすためにはawaitすればよいですが、forEachの関数も非同期のfunctionにしないといけなくなります
Promiseの実行は「あとでちゃんとやっておくね」というものなので、Array#forEachがまわりきるまではおよそ、Promiseは未実行の状態になり、forEachの処理はなにもされないままallWidthをconsole.logにながすことになり、答えは0になります

# どうすればよかったのか
普通にforを使うシーンです
```javascript

const main = async() => {
    const images = [image1, image2, image3]
    const allWidth = 0
    for(const i = 0;i < images.lenfth; i++){
        const imagedata = await createImageBitmap(images[i]) 
        allWidth += imagedata.width
    })
    console.log(allWidth)
}
main()
```
createImageBitmapが順番に評価されるためにはforEachとcallbackでは無理で、順番に処理するためにforで一つのfunctionの内部にいれなくてはいけないということです
asyncのついたfunctionはその内部は同期的ですが、外部からみれば予約された処理です。なので、それを待つためにawait keywordがあるわけですがこのあたりをきちんと理解していないと、こういった罠にかかります(async functionは、functionそのものの実行をPromiseとして一旦返しますよという意味です)
今回はcreateImageBitmapでしたが、fetchでも自作のPromiseでも同じことが当然おこります

# まとめ
実際のところ、わたしが罠にかかった経験談です(なんで?って考えて、10分くらいは悩んだ)
これについては、MDNにまでわざわざ記述があります
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
正直、Promiseは非同期処理の記述としては洗練されすぎていて普通の関数と区別がつかないことが多いという優れたデザインなのですが、それゆえにきちんと理解しないと変な挙動に悩まされたりするので、きちんとした理解が必要な仕組みです
mablのテスト間で変数を共有する時に必要な設定について

この記事の情報は2024/02/26時点、下記バージョンのmablについてのものです。
mablアプリ：2.5.40
mablトレーナー：2.2.12.21

# 前置き
mablで変数を共有する機能を使ってテストを作る機会があったのですが、使い方で少し詰まったので公式ドキュメントの補足を兼ねてメモ。
公式ドキュメントは[**こちら**](https://help.mabl.com/hc/ja/articles/17750199158804-%E3%83%86%E3%82%B9%E3%83%88%E9%96%93%E3%81%A7%E3%81%AE%E5%A4%89%E6%95%B0%E3%81%AE%E5%85%B1%E6%9C%89)。

# どんな機能？
テスト内で作成した変数をテストプラン内の後続のテストに受け渡すことができます。
**プラン単位でのクラウド実行**でのみ有効な機能です。

# ユースケース
テスト間に依存関係がある構成のテストプランを作りたいとき。
具体的には、乱数入りのアカウント名でアカウントを作成するテストの後、別のテストでそのアカウント情報を使った操作を行いたい場合など。

# テスト側の設定
共有元と共有先のテストで必要な設定内容が異なります。

## 共有元のテスト

### 手順
1. 変数を作成するステップを含むテストを作成
2. テスト詳細画面のテスト編集画面から「変数を共有する」トグルをonにして保存

共有する変数名は後で使うのでメモっておくのが良いでしょう。
この部分に関しては[**公式ドキュメント**](https://help.mabl.com/hc/ja/articles/17750199158804-%E3%83%86%E3%82%B9%E3%83%88%E9%96%93%E3%81%A7%E3%81%AE%E5%A4%89%E6%95%B0%E3%81%AE%E5%85%B1%E6%9C%89)の「変数の共有の設定」の項目の方が画像付きで詳しいです。

## 共有先のテスト
トレーナーの「データ駆動型変数」の設定画面に、**共有元と同じ名前の変数を追加する**だけです。

### 手順
1. トレーナー下部の変数ボタンから「このテストの変数を管理する」をクリック
2. 「データ駆動型変数」をクリック
3. 「新規変数」をクリック
4. 受け取りたい変数名を入力し（**大文字小文字含め共有元と完全一致させる**）、デフォルト値を設定する
5. 「適用する」「OK」の順にクリックして変数の設定を保存

あとは設定した変数を使用してテストを作成すればOKです。

# テストプラン側の設定
共有元→共有先の順でテストが必ず完了するようにステージを編集する。
[**公式ドキュメント**](https://help.mabl.com/hc/ja/articles/17750199158804-%E3%83%86%E3%82%B9%E3%83%88%E9%96%93%E3%81%A7%E3%81%AE%E5%A4%89%E6%95%B0%E3%81%AE%E5%85%B1%E6%9C%89)でも紹介されている通り、関連するテストを直列で実行させるのがよいでしょう。

# ローカル実行時の注意
この機能はクラウドでのプラン実行向けの機能であるため、ローカル実行では変数を共有することができません。
例えばmabl-cliなどで`--from-plan-id`オプションを用いてプランのローカル実行を行った場合、変数が共有されず、デフォルト値を使用するようになります。
そのため対策無しだと共有先のテストが一斉に落ちる可能性があります。
対策は色々考えられますが、素直にクラウド実行した方がいいですし、いい方法が思いつかないので今回は省略します。。。
Java ~四則演算~

## :ledger:四則演算　
`加算`・`減算`・`乗算`・`除算`の`四則計算`と＋αについて説明していきます。
計算をするためには `算術演算子` を使う必要があります。

| 算術演算子 | 意味 |
|:-:|:-:|
|  ＋ | 加算（足し算）  
|  － | 減算（引き算）  |   
|  * | 乗算（掛け算）  |   
|  /  |除算（ 割り算）|   
| %  | 剰余算（余り）  |   
|  ++ |  １増加 |   
| --  | １減少  |   


~~~ java: サンプル
 int num1 = 100; 
 int num2 = 10; 

        // int型変数resultを宣言し、num1とnum2の加算結果を代入
        int result = num1 + num2;

        //結果
        result1 = 110;
~~~      

## :ledger:文字列の連結
数字の足し算だけではなく文字列も連結させることができます。

実際のコードサンプルを見てください。
~~~ java:文字列の連結
int num1 = 100; 
 int num2 = 10; 

        // int型変数resultを宣言し、num1とnum2の加算結果を代入
        int result = num1 + num2;
        //結果
        result1 = 110;
        
        System.out.println("num1 + num2 = " + result + "です");

        //コンソールに表示される出力結果
        num1 + num2 = 110 です
~~~

:::note info
文字列は`"(ダブルクォーテーション)`または`'(シングルクォーテーション)`で
囲む必要があります。連結させるには`＋`を使用します。
:::

## :ledger:まとめ

プログラミングでは
掛け算は`*` 割り算は`/`を使うルールです。
普段の計算とは記号が異なるので注意してください。

文字列と数値は異なります。
例えば
"2"+"4" = "24" と文字列の扱いとなり2+4=6と数値となります。

少しずつ慣れていけば大丈夫です！

***
batchファイルでゼロ埋めをする時に遅延展開でハマった

# 概要
Windowsのbatchファイルで、数字をゼロ埋めして2桁にそろえるような処理を考えました。  
ですが、ビックリマークで囲んだ変数の特性と、％で囲んだ変数の特性の違いを考えなかったため、思っていた結果が得られませんでした。  
ゼロ埋め処理でハマった点について、本記事では書きたいと思います。

# 何が発生したのか
## 事象
次のように、1から3までをforで回し、画面に出力するバッチファイルがあります。  
```batch
@echo off
for /L %%i in (1,1,3) do (
    echo %%i
)
```  
このスクリプトを修正し、出力結果が01,02,03のようにゼロ埋めされるようにしました。  
```batch
@echo off
for /L %%i in (1,1,3) do (
    set "num=00%%i"
    set num=!num:~-2!
    echo !num!
)
```  
すると出力結果は
```
C:\work\temp>number.bat
!num!
!num!
!num!
```  
と!num!がそのまま出力されてしまいました。

## 原因
ビックリマークで囲んだ変数については、`setlocal enabledelayedexpansion`を使用し、遅延展開を有効化する必要がありました。  
具体的にはスクリプトを以下のように修正します。  
```batch
@echo off
setlocal enabledelayedexpansion
for /L %%i in (1,1,3) do (
    set "num=00%%i"
    set num=!num:~-2!
    echo !num!
)
```  
これで出力結果は
```
C:\work\temp>number.bat
01
02
03
```  
と無事ゼロ埋めされた結果を得ることができました。  

## どういうこと？  
- %で囲まれた変数は静的変数として、コマンドが実行される前に値が評価される
- !で囲まれた変数は動的変数として、コマンドが実行される直前に値が評価される

後者の場合、`setlocal enabledelayedexpansion`が有効でないと値が読み込まれないようです。  
for文を使用し、値が何度も変わるようなケースでは後者を使うのがよさそうです。

# 参考
https://qiita.com/sakaimo/items/07550f555d1c40dd2170
シリーズ Google Home の Cloud-to-cloud を使ってみたい！ Phoenix Frameworkで出来る?!

Google Home + IFTTT + LINE でGoogle HomeからLINEにメッセージを飛ばしていたのですが，いよいよ機能しなくなってきたので，移行を検討していました．

調べたところ，古い記事ですが，次のような記事を発見

https://shimobepapa.hatenadiary.jp/entry/2020/09/20/005016

ウェブサーバーを立てるっぽい．

ということは，Phoenix Frameworkで，そのようなウェブサーバーを立てたら良いのでは？

と思って，これから少しずつ筋トレのように調べて，知見をQiitaに蓄積していくシリーズを立ち上げることにしました．

## 今日の進捗

調べてみると，Google Homeの中でも，Cloud-to-cloud という区分になるらしい．

https://developers.home.google.com/cloud-to-cloud

Get started から少しずつ積み上げていけば良さげ．
Cisco CML内のAlpine Serverネットワーク設定

Cisco CMLで疎通確認に使用するAlpineサーバの設定方法です。

1. Alpineサーバを選択しLab上にドラッグ&ドロップ
![alpine.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/4faf2cc9-b7af-e4cc-a886-a046e2277b8e.png)
1. ドラッグ&ドロップをしたAlpineを選択し、「EDIT CONFIG」タブに移動![alpine-ページ2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/15c664f5-367a-347e-1f1d-f58308e0b2ea.png)
1. 下部のEDIT CONFIG内を編集
    ```:デフォルト
    # this is a shell script which will be sourced at boot
    hostname inserthostname-here
    # configurable user account
    USERNAME=cisco
    PASSWORD=cisco
    ```
    ```:変更後
    # this is a shell script which will be sourced at boot
    hostname alpine-0 ← ※任意のホスト名に変更
    # configurable user account
    USERNAME=cisco
    PASSWORD=cisco
    ip addr add 10.0.10.1/24 dev eth0 ← IPアドレスを設定
    ip route add default via 10.0.10.254 ← デフォルトルートを設定

    ```
    設定変更後[SAVE]で保存
    ![alpine-ページ3.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/189f2d84-79f2-34ff-1334-b98a10025306.png)

1. アイコンを右クリックして[Start]で起動すると設定したIPで立ち上がります
![alpine-ページ4.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/effb6f85-6c99-ee31-ce1c-21f92713f1b5.png)
    ```:IP設定確認
    alpine-0:~$ ip address show eth0
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
        link/ether 52:54:00:13:61:11 brd ff:ff:ff:ff:ff:ff
        inet 10.0.10.1/24 scope global eth0
           valid_lft forever preferred_lft forever
        inet6 fe80::5054:ff:fe13:6111/64 scope link 
           valid_lft forever preferred_lft forever
    ```
    ```:ルーティング設定確認
    alpine-0:~$ ip route show
    default via 10.0.10.254 dev eth0 
    10.0.10.0/24 dev eth0 scope link  src 10.0.10.1 
    ```
ドキュメントと実装とが違っているときのpythonでの対処方法

# 想定する読者
ドキュメントを参照して、スクリプトを書いたときに、該当するデータメンバーやメソッドがない。
そういう経験をPythonでしたことがある人へのヒントです。

## 概要
モジュールもしくは、インスタンスである object に対して以下のように実行するコードを書き加える。
そうすると、どのようなデータメンバーやメソッドがあるのかを実行時に表示させることができる。

```
import inspect

for k, v in inspect.getmembers(object):
    print(k, v)
```


pythonとinspectをキーワードに検索すると、もっと有意義なヒントにたどり着くでしょう。

そうすると、データメンバーの名前が違っていることを確認できます。
(そして、ドキュメントのメンテナンス者に、ドキュメントが古くなって、実装と食い違ってしまっていることを連絡しましょう。)


## 公式ドキュメント
https://docs.python.org/ja/3/library/inspect.html
laravel ページの作り方（初心者）

例）

・web.phpで/admin/groupのルーティングの設定。
・Groupcontroller.phpを作成。 group.blade.phpを表示するようにする。
・app.blade.phpを元に、group.blade.phpを用意する。
・Group.vueを作成し、group.blade.phpで呼び出すように書く。

bladeのもとになるファイルは、**app.blade.php!**

----
1. **ルーティングの設定：**
     **`routes/web.php`** ファイルで、特定のURLに対してどのコントローラーのどのメソッドを使うかを指定しました。例えば、**`/admin`** にアクセスされたら **`AdminController`** の **`index`** メソッドを呼ぶように指定しました。
        
        ```php
        phpCopy code
        use App\Http\Controllers\AdminController;
        Route::get('/admin', [AdminController::class, 'index']);
        ```

　　
2. **コントローラーの作成：**
    - **`AdminController`** という名前のコントローラーを作成し、**`index`** メソッド内で **`admin.admin`** ビューを表示するようにしました。
        
        ```php
        phpCopy code
        // app/Http/Controllers/AdminController.php
        public function index()
        {
            return view('admin.admin');
        }
        ```        
    
    　　**admin.adminはadminファイルの中のadmin.blade.phpフォルダという意味！**

　
3. **Blade テンプレートの作成：**
    - **`resources/views/admin/admin.blade.php`** ファイルを作成し、**`@extends('layouts.app')`** で **`app.blade.php`** レイアウトを継承し、**`@section('content')`** 〜 **`@endsection`** の間に **`admin-dashboard`** コンポーネントを表示するようにしました。
        
        ```
        bladeCopy code
        <!-- resources/views/admin/admin.blade.php -->
        @extends('layouts.app')
        
        @section('content')
            <admin-dashboard :auth="{{ json_encode(Auth::user()) }}"></admin-dashboard>
        @endsection
        ``　　

　　
4. **Vue コンポーネントの作成：**
    - **`resources/js/components/AdminDashboard.vue`** ファイルを作成し、**`<template>`** セクションには表示したいコンポーネントの内容を記述しました。
        
        ```
        vueCopy code
        <!-- resources/js/components/AdminDashboard.vue -->
        <template>
            <div>
                <h1>Welcome to Admin Dashboard!</h1>
                <p>管理者メニュー</p>
                <!-- 他のコンポーネントの内容をここに追加 -->
            </div>
        </template>
        
        <script>
        export default {
            props: {
                auth: {
                    type: Object,
                    required: true
                }
            },
            // 他のコード...
        };
        </script>
        
        <style scoped>
        /* コンポーネント固有のスタイルをここに記述 */
        </style>
        ```
 　 
5. **結果の確認：**
    - **`php artisan serve`** コマンドで開発用サーバーを起動し、**`http://127.0.0.1:8000/admin`** にアクセスして結果を確認しました。
独自拡張した型のスライスをGORM+PostgreSQLで使う

# 独自拡張した型のスライスをgorm+PostgreSQLで使う

## 独自拡張した型とは
筆者のユースケースの一例として、単一のUUID型、すなわちuuid.UUIDを単純に利用したモデルでは、本来ユーザーIDが入るべき場所にコンテンツIDが入ってしまうなどの問題が発生することがあります。

この問題を解決するため、uuid.UUIDを独自に拡張し、value.UserIDのような形で値オブジェクトを作ることができます。これにより、型の安全性を向上させることができます。以下はその実装例です。

```go: value/uesr.go
package value

import (
	"github.com/google/uuid"
)

type UserID uuid.UUID

var _ ID = (*UserID)(nil)

func (v UserID) IsNil() bool {
	return uuid.UUID(v) == uuid.Nil
}

func (v UserID) String() string {
	return uuid.UUID(v).String()
}

func (v UserID) Value() (driver.Value, error) {
	return uuid.UUID(v).Value()
}

func (v *UserID) Scan(value interface{}) error {
	return (*uuid.UUID)(v).Scan(value)
}
```

## 独自拡張したUUID型を利用するにあたっての問題
独自拡張した型を利用する際、value.UserIDのスライス型をGormのフィールドとして利用した場合、スライスとしてのValue()やScan()メソッドがないためエラーとなることがあります。以下はその一例です。

```go: entity/campaign.go
type Campaign struct {
	ID value.CampaignID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id" example:"12345678-1234-1234-1234-000000000001" validate:"required"`
	UserIDs   []value.UserIDs   `json:"userId" example:"12345678-1234-1234-1234-000000000001" validate:"required" gorm:"type:uuid[]"`   // キャンペーン対象のユーザID
}
```

→ 保存時に以下のエラーが発生します。
```pq: malformed array literal: "cd3d7c53-4d32-4bc6-ac55-3c0d0534457e"```

また、スライス型をGormで利用する際に、gormタグのtype指定がないと「got unsupported data type」というエラーが発生しますので注意して下さい。
```
got unsupported data type: &[]: Table not set, please set it like: db.Model(&user) or db.Table("users")
```

## 解決方法
pq.StringArrayなどの実装を参考にして、スライス型専用のValue()とScan()を用意することで解決できます。
参考: https://gorm.io/ja_JP/docs/data_types.html#Scanner-x2F-Valuer

```go: value/user_ids.go
package value

import (
	"database/sql/driver"

	"github.com/google/uuid"
	"github.com/lib/pq"
)

type UserIDs []UserID

func (d UserIDs) Value() (driver.Value, error) {
	// UserIDsが空の場合、nilを返してPostgreSQLにNULLとして扱わせる
	if len(d) == 0 {
		return nil, nil
	}

	// UUIDの文字列のスライスを作成
	uuids := make([]string, len(d))
	for i, id := range d {
		uuids[i] = uuid.UUID(id).String()
	}

	// pq.Arrayを使用して、uuidsスライスをPostgreSQLのuuid[]型に変換
	return pq.Array(uuids).Value()
}

func (d *UserIDs) Scan(src interface{}) error {
	// PostgreSQLのuuid[]型を受け取るための文字列スライス
	var uuidStrings []string

	// pq.Arrayを使用して、srcを文字列スライスにスキャン
	if err := pq.Array(&uuidStrings).Scan(src); err != nil {
		return err
	}

	// 文字列スライスをUserIDs型に変換
	*d = make(UserIDs, len(uuidStrings))
	for i, str := range uuidStrings {
		id, err := uuid.Parse(str)
		if err != nil {
			return err
		}
		(*d)[i] = UserID(id)
	}

	return nil
}
```


```go: entity/campaign.go
type Campaign struct {
	ID value.CampaignID `gorm:"primaryKey;type:uuid;default:uuid_generate_v4()" json:"id" example:"12345678-1234-1234-1234-000000000001" validate:"required"`
	UserIDs   value.UserIDs   `json:"userId" example:"12345678-1234-1234-1234-000000000001" validate:"required" gorm:"type:uuid[]"`   // キャンペーン対象のユーザID
}
```

## あとがき
データベースのカラムで独自拡張した型の配列を利用したいというユースケースは少ないかもしれません。
また、データベース設計上、間違いであることケースが多いと思います。しかし、この問題に直面し、解決策を見つけ出すまでに苦労したため、この記事をメモ代わりに記載しました。
Reactのページ遷移でuseHistoryが使えない

# 始めに
現在Udemyの講座でReactを学習中です！
今回通常のページ遷移は<Link to〜 を用いてページ遷移するのですが、今回は関数を用いてページ遷移するとのことだったので、useHistoryのフックを使用しました。
しかしv5→v6のバージョンアップが原因でuseHistoryはサポートされていませんでした。
そこでuseNavigateを使用したので解説します！

## v5での書き方
```react
import { useHistory } from "react-router-dom";

export const Page1 = () => {
  const history = useHistory();
  const onClickDetailA = () => history.push(`detail1`);

  return (
    <div>
      <h1>Page1です！</h1>
      <button onClick = {onClickDetailA}>detailA</button>
    </div>
  );
};
```
## v6での書き方
```react
import { useNavigate } from "react-router-dom";

export const Page1 = () => {
  const navigate = useNavigate();
  const onClickDetailA = () => navigate(`detail1`);
  
  return (
    <div>
      <h1>Page1です！</h1>
      <button onClick = {onClickDetailA}>detailA</button>
    </div>
  );
};
```

## まとめ
今回はuseHistoryではなくuseNavigateで記述する方法をまとめました。
ちなみにuseNavigateでstateを渡すときにはオプション値として渡すそうです！
```react
import { useNavigate } from "react-router-dom";

export const Page1 = () => {
  const arr = [...Array(100).keys()];
  const navigate = useNavigate();
  const onClickDetailA = () => {
    navigate(`detail1`, { state: arr });
  };
  return (
    <div>
      <h1>Page1です！</h1>
      <button onClick = {onClickDetailA}>detailA</button>
    </div>
  );
};

```
ChatGPTにTODO拡張機能を作らせてみた

# TL;DR
ChatGPTにTODO拡張機能を作らせてみたら、一応できたけどあまり質は良くなかった。

# 経緯
学校のChromebookでタスク管理の拡張機能を使いたかったが、機能が少なかったり、ログインが必要だったり、重かったりして使いにくかったので、自分用の拡張機能を作ろうと思った。
だが、自分はあまりJavascriptがかけないので、テキストボックスに書き込んでボタン押したらローカルに保存するくらいしかかけないため、本末転倒である。なので、ChatGPTにコードを書かせてちょうどいいのを作ってみた。

# 環境
- Lenovo Chromebook 300e 2nd Gen(教育委員会によって管理)
- [Text](https://chromewebstore.google.com/detail/text/mmfbcljfglbokpmkimbfghdkjmjhdgbg)というChromeアプリ
- ChatGPT(無料版)
- Google Chrome 121

# 目標
- ポップアップで作成・管理
- 作成・編集・削除が可能
- chrome.storage.localに保存する
- タイトル・内容・期限・サブタスクが設定可能
- チェックボックスで進行度を管理
- 期限の日に通知
- 一覧ページをオプションページとして作成

# では、実際にやってみる
ChatGPTに生成を頼んだところ構想が返ってきたので、それのコードを実際に生成してくださいとしたところ、しっかりと生成された。しかし、background.jsを定義せずにmanifest.jsonに指定するなどいくつかのミスがあった。
何回か訂正したところ、結果的には

- 通知は送信できない
- 進行度は生成されない
- サブタスクは作成できない

ものの、実際に利用できるようになった。
![Screenshot 2024-02-26 16.24.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723455/7d14d646-bfd1-deef-8846-2a460a01ebad.png)
▲ポップアップ
![Screenshot 2024-02-26 16.25.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3723455/5410b7dd-30b4-1641-f38f-2f108b7ad6d2.png)
▲オプションページ

# やってみてわかったこと
- 自作のコードではないため、デバッグが面倒臭い
エラーが起きてもどこを直せばいいのか、なぜ起こっているのかがわかりにくいので、できないことや、エラーメッセージをChatGPTに送り、返ってきたもので試すしかなかった。
- 機能がいじりにくい
自作ではないので、どこなら変えても大丈夫かわからないため、カスタマイズがしにくかった。
- つけたいと思っていた機能が付かない
人間に依頼しているわけではないので、正確ではない

といういくつかの疑問点が見つかったので、AIに頼りつつも、結局は自分でかけるようになりたいと思った。

# まとめ
ChatGPTは役に立つけど、完璧ではない。自分で覚えた方がいい。
VAEのKLダイバージェンスの振る舞いについてGeminiに聞いてみた

# メモ書きです
VAEのKLダイバージェンスは標準正規分布と潜在変数の分布との距離を求め，最小にしようとするので最終的にはエンコーダ出力部分の平均μ=0分散σ=1の定数となってしまうのではないか。ただ，それだとデコーダ側で入力を再現できなくなるので，再構成ロスとKLダイバージェンスは反比例するような関係ではないか。GoogleAIのGeminiに聞いてみた。

# Geminiとのやり取り

**Question**: 
VAEの損失関数は再構成ロス＋KLダイバージェンスですが，KLダイバージェンスの最小値はエンコーダの出力である平均0，分散1となる場合です。KLDだけを最小化したいのであれば入力に関わらず，常にエンコーダの出力を0と1の定数としてしまえばよいと考えられます。しかしこれでは入力の情報が何も伝わらないため再構成できません。再構成するためには，平均と分散を0や1からわずかに変化させる必要があります。つまり，再構成ロスとKLダイバージェンスは反比例の関係にあるという認識でよろしいでしょうか。 


**Answer**:

はい、その認識でほぼ間違いありません。VAEの損失関数は、再構成ロスとKLダイバージェンスのトレードオフを表現しており、両者のバランスを取ることが重要です。

再構成ロスは、入力データと再構成データの差異を最小化します。つまり、エンコーダとデコーダを通して元のデータにできるだけ近いデータを生成することを目指します。

一方、KLダイバージェンスは、エンコーダの出力分布と事前分布との差異を最小化します。これは、潜在変数が標準正規分布に従うように促し、潜在空間の解釈可能性を高める効果があります。

KLダイバージェンスのみを最小化する場合、エンコーダの出力を常に0と1に固定することで、KLダイバージェンスは0になります。しかし、この場合、潜在変数に情報が全く含まれないため、デコーダは入力データに関係なく常に同じ出力を生成することになります。つまり、再構成ロスは非常に大きくなります。

一方、再構成ロスのみを最小化する場合、エンコーダは入力データをそのまま潜在変数に写像しようとします。しかし、この場合、KLダイバージェンスは非常に大きくなり、潜在変数が標準正規分布から大きく逸脱します。

結論として、VAEの損失関数は、再構成ロスとKLダイバージェンスのバランスを取ることによって、情報量と解釈可能性を両立する潜在変数表現を学習します。

参考資料:
VAEの理論と実装 #Python - Qiita: https://qiita.com/yoshikawat64m/items/fd7e9742d3abe0aa7b86
【徹底解説】VAEをはじめからていねいに - Academaid: [無効な URL を削除しました]

補足:
VAEの損失関数は、再構成ロスとKLダイバージェンス以外にも、様々な項を加えることで拡張することができます。
VAEのハイパーパラメータであるβは、再構成ロスとKLダイバージェンスの重みを調整します。βの値を変えることで、生成データの品質と解釈可能性のバランスを調整することができます。

らしいです，以上。
SI会社で1on1について振り返ってみた

日本で住んでいて、SI会社を通っている外国人エンジニアです。
この立場で1年間1on1を実施してみたので、諸々考えた点を投稿します。
現在SIでチームリーダーを務めていますが、メンバーが全部異なる現場にいるので、軽くサポートする立場にいます。
他の自社開発とは違いがあると思いますが、さまざまなご意見をお聞かせいただければと思います！:bow_tone2:


一応1on1について話してみて、昨年度の目標と今後どうすればいいかについて話してみたいと思います。

# 1on1について軽く事前情報
「1on1」は1対1で行う定期的なミーティングを指します。通常は面談と呼ばれますが、1on1では面談よりも軽い雰囲気で、雑談なども可能なカジュアルなミーティングが一般的です。

### 1on1の目的
```CODE
なぜ1on1を実施するのか

- メンバー育成
    - SIの特有の特徴があり、各メンバーが異なる現場に配属されているため、直接的な育成は実施しません。問題が発生したり、望んでいるものがあれば、エスカレーションをする感じです。
- メンバーのエンゲージメント向上
    - 現在抱えている課題などを早目にキャッチして、サポートしたり、次のステップについて話し合います。

```



### 1on1を通じて得たい効果

- 組織・チーム力の向上
- モチベーション向上(離脱防止)
- 自身のマネジメント能力の向上
`


# 2023年度、1on1についての自身の課題
 > 1. 元々チーム会を実施していましたが、どんどん参加率が低下されている。。
 >     ちゃんと企画したものがあれば、参加したい旨があった。
 > 2. 1on1が事情によってスキップされることがあり、また実施しても5分も話すことがない時があります。


## 実施方法
 - 現場業務の後、月に一度実施
 - 体調不良などがあれば、スキップ可能だが、評価面談とか必ず実施すべきな月にはちゃんと実施
 - 雑談から始め、勤怠・体調・評価面談の三つについては必ず聞いて完了

 - 1on1実施後、ちゃんとメモを取る
 　管理するメンバーの入れ替えがなかったので今は結構各自の関心分野や仕事している先などの情報が蓄積されて、これを基にした会話も増えています。→話のネタ作る！
- 各自出した業務報告を参考してMTGに行く

### 参加率の向上のための質問の細分化
 + 外国人として質問するのが苦手なので例文を作成したりテンプレート化しました
 + 質問を省略すると、話が進まず早く終わる場合が多かったのでちゃんと質問を考える必要があった


**雑談から、勤怠・体調・評価面談の三つは必ず聞いて完了**


 ### アイスブレーキング

今週末はどうでしたか？
最近の日常のルーチンはどうなっていますか？
最近勉強していることはありますか？
興味深いことはありますか？
旅行好きならおすすめの場所はありますか？
ご趣味は何ですか？
次に挑戦してみたい言語などはありますか？
流行っているゲームや音楽などはありますか？
※時々ニュースやチャットGPTさんを参考する
 
### お知らせ共有
- 長期休暇など確認、休暇の残日数確認
- 研修日程確認
- 目標面談についての日程など確認
- 健康診断などのリマインド
- 提出物のリマインド

ーーーーここまでで5分以上～
 
 ### 体調・調子
 天気がよくないですね。風邪など引かないように気をつけてくださいね。
 ちゃんと睡眠は取れていますか？興味を持っている運動はありますか？
 体調が崩れたことはありますか？現在の体調はいかがでしょうか？
 ※インフルエンザ接種とかリマインド
 
 ### 現場の作業量
 (〇〇〇ツール(勤怠ツール)を確認した上)体調を崩したりしていませんか？
 メンタルに変を感じた部分がありますか？
 残業が増えましたか？作業量的に大丈夫ですか？作業について心配はありませんか？
 最近調子はいかがでしょうか？
 リモートワークですか？残業が増えていますか？
 現場は忙しいでしょうか？
 MTGが増えましたか？他の部署とのつながりが増えましたか？

 ###目標面談(自社の評価面談のベース)
 - 目標面談～は順調でしょうか？
 - 学習計画を立てていると思いますが、目標通りすすんでいますか？必要な情報は揃っていますか？
 - 無理でしたら、リスケジュールする必要がありますか？どなたかの手伝いがあれば、スムーズにいけますか？

ここまでが10分以上～

毎度同じ内容でも毎回聞いていることに認識ができ、体調が崩れていることもすぐにキャッチできてよかったですね。
 
 ### Q&A
聴きたいものがあればフォロー


# 2023年度の振り返り
最初は一旦、メンバーたちと親しくなった気がしますが、1on1実施までに得るものがあったかどうか、気になっている状態でした。今回の投稿を通じて、いくつかの点について振り返ることができて足りなかった点、成長できた点が見えて良かったです！

最初は1on1が短い時は5分内で生々しく終わったりしましたが、後半期からは1on1が平均15-30分になっていろんな話ができるようになったと思います。これはちょっとした発展では:relaxed:
 
 1on1に慣れる時期になり、多様なお題で話すことでコミュニケーション力も少し向上した気がします。また、足りないことや新しい試みに対してキャッチし、エスカレーションした経験もあり、マネジメント力も少しずつ増しているのではないかと思います。
 
 でも、今年も同じようなことを聞くと、日常的な話になりすぎてお互いに退屈しているかもしれない気もしました。もう少し様々なテーマを取り入れることが必要かもしれません。

現在は行っていませんが、チーム会議に対する比重を増やしてみるのも一つのアイディアです。日常的な話をすることも重要ですが、他のチームで行っているような↓が印象的だったのでこれもいいかもです。
1. 会社の苦情を話して改善点をまとめて報告する会議
2. 自分の価値を交渉するシミュレーションを作る
のように時間を投資した分、何かを持っていける会議を作ってみたいとも思いました。

### 結論
 昨年度から完全（？）リーダーになったのは始めたのですが、問題なく評価面談などが終わってよかったです。少し自分のメネシットとコミュニケーション能力が成長させる機会になって嬉しいです。問題点を気付いて質問とかはちゃんとテンプレート化して管理やすくなった点も褒められたいと思います～！
  同時に今後チーム会の低下している参加率については確実な企画力は必要べきだった点を確認したので、今後どのように挑むか悩んでもみる今年になりました。


  ここまで読んでくれてありがとうございます～！私は1ON1でこれも話してみた、こういう話もよかったのではないか点があれば自由にご意見ください～！
色を10進数で指定する

# 関連記事

https://qiita.com/aozora_doro/items/81eb493d177da9d96c56

https://qiita.com/aozora_doro/items/0a2dc92f6b5876d52cd5

# 開発環境
- Windows11
- Visual Studio 2022
- DXライブラリ Ver3.24d

# 色の指定
DrawPixel関数やDrawBox関数では色の指定を16進数で行うことができます。

```cpp:例
DrawPixel( 100, 100, 0xFFFFFF );               // ピクセルの描画
DrawBox( 200, 200, 300, 300, 0xFFFFFF, TRUE ); // 矩形の描画
```

DXライブラリでは色コードを直接指定する代わりに、RGBの各値を10進数で指定し、その値から色コードを生成する`GetColor`関数が用意されています。

```cpp
#include "DxLib.h"

int WINAPI WinMain( _In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpcmdLine, _In_ int nCmdShow )
{
    // ウィンドウモード
    ChangeWindowMode( TRUE );

    // DXライブラリの初期化
    if( DxLib_Init() == -1 )
    {
        // -1ならエラーとしてゲーム終了
        return 0;
    }

    // 10進数でRGBを指定して色コードを取得
    unsigned int color = GetColor( 255, 0, 0 );

    // 矩形を描画
    DrawBox( 50, 50, 150, 150, color, TRUE );

    // プログラムの一時停止
    WaitKey();

    // DXライブラリの破棄
    DxLib_End();

    return 0;
}
```

**実行結果**

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713748/95b060b8-9cc3-6ca7-f850-e274a1c859c4.png)

GetColor関数には第一引数にR(赤)、第二引数にG(緑)、第三引数にB(青)の値を0～255の範囲で指定します。戻り値で色コードが受け取れるので、この値をDrawBox等の関数に渡してください。

# 注意
色コードはRGBが1つの整数で管理されています。そのため、0から255の範囲を超える値を指定すると出力される色に影響が出ます。これにより、意図しない色が表示されたり、色が全く表示されないなどの問題が発生する可能性があるので注意が必要です。

```cpp
// 10進数でRGBを指定して色コードを取得
unsigned int red    = GetColor( 255, 0, 0 );
unsigned int yellow = GetColor( 255, 256, 0 ); // Gの値が255を超えている

// 矩形を描画
DrawBox( 50, 50, 150, 150, red, TRUE );
DrawBox( 200, 50, 300, 150, yellow, TRUE );
```

**実行結果**

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713748/b9fa88a5-5351-fa1e-5770-24c1084c46f8.png)

変数yellowは黄色のつもりで設定したところ、結果的に赤色が表示されました。値が1違うだけでも色の出力に大きく影響することがあります。

# 参考

- [GetColor](https://dxlib.xsrv.jp/function/dxfunc_graph3.html#R4N5)
::-ms-revealをMUIのTextFieldで消す方法

個人的には余計なお世話と思ってしまったが、Edge,IE11ではInputのTypeがpasswordである場合、独自にパスワード表示アイコンを実装していても、ブラウザが勝手に表示アイコンを保管してしまって、UI上ダブって見えてしまうことがある。
（右目が実装したもので、左目がブラウザが保管したもの）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2956558/b872f683-127e-5c2d-f58a-37f4c58b5afb.png)

プロジェクトではReactとMUIを利用していることもあり、MUIコンポーネントへのスタイルの当て方を調べていて、Globalのスタイルを調整する方法があった。

https://stackoverflow.com/questions/69279989/having-an-issue-from-material-ui-visibility-password-in-the-microsoft-edge-brows

ただ個別のコンポーネントに対してのみにスタイルを当てたいこともあり、MUIのスタイル指定で解決する方法を模索していたところ、
以下の記事が参考になったため、サンプルコードと共に記載する。

https://www.programcreek.com/typescript/?api=@mui/material.TextField

```tsx
<TextField
  label="Password"
  id="password"
  value="passwordValue"
  type={showPassword ? 'password' : 'text'}
  onChange={onChange}
  size="small"
  InputLabelProps={{
    shrink: true,
    style: { color: 'red' },
    }}
  error={isError}
  InputProps={{
    endAdornment: ( // パスワード表示非表示のアイコンの切り替え実装部分
        <InputAdornment position="end">
          <IconButton onClick={onChangePasswordVisibility} edge="end">
            {showPassword ? <Visibility /> : <VisibilityOff />}
          </IconButton>
        </InputAdornment>
        ),
        }}
  sx={{
  "& input[type=password]::-ms-reveal": { display: "none", width: 0, height: 0 }, // ここ！！
  "& input[type=password]::-ms-clear": { display: "none", width: 0, height: 0 }, // 念のため！
              }}
              />
```
Hello Worldから始める復習 ～Hello World編～

エンジニアになり約一年が過ぎようとしていて、話題の世界一のエンジニア思考という書籍を読んで思ったことが、「ちゃんと理解してねーなぁ。。」と。
今一度、基礎をきちんと理解するためにアウトプットをしよう。


**Hello World**
-
```java:java
class First1{
public static void main (String[] ages) {
	System.out.println("Hello World");
  }
}
```
まずは基本的なコーディングから。
キーワードを「class」、半角開けて「クラス名」。
今回は「First1」とする。

ファイル作成時、ファイル名とクラス名は合わせるのがお作法。


```java:java
class First1{→
中括弧のここをブロックという

←}
```
ブロック内に処理文を記述する


```java:java
class First1{
public static void main (String[] ages) {ここから→
         処理文を記述
  ←ここまで}
}
```

処理のまとまりをメソッドといい、
**mainメソッド**から実行される。

上記記述のmainメソッドである
`public` `static` `void` main `(String age[])` {
}
は一つ一つ意味はあるが今日は割愛。


処理文は
**;(セミコロン)**
で終わる。

**System.out.println();**
は()内のデータを表示する。

作成したファイルのあるディレクトリで`cmd`を入力しコマンドプロンプト。
`javac First1.java`をコンパイル。(※コンパイルとはPCが読めるように変換すること)

```cmd:cmd
C:\Java\1>javac First1.java
C:\Java\1>
```
javacのcはコンパイルのc。
コンパイルが通ったら`First1.java`の実行
コンパイルが通ってフォルダの中を見るとFirst1.classが自動生成される。

```cmd:cmd
C:\Java\1>First1.java
Hello World
C:\Java\1>
```

**コメントアウト**
どんな処理をしているのか等のメモ的存在。
**超重要**
`/**/` →複数行のコメント
`//` →1行のコメント
これがないソースは読みにくいし、読み取るのがめんどくさいし、**優しくない。**

```java:java
class First1{
public static void main (String[] ages) {
    /*
    *文字列を表示させる処理
    */
	System.out.println("Hello World"); //Hello Worldと表示させる
  }
}
```


第一回目としてはこの辺まで。

初めて投稿しましたが、ドキュメント作成の練習にもなりそうで楽しみながら続けていこう。

スポーツ選手も、ミュージシャンも、技術者の方たちは基本を丁寧にしている。
私もエンジニアの端くれ。
基礎は愚直に。

技術は細部にこそ現れる。
FileMaker プリンタが変わっても印刷がほぼズレない 印刷設定

# FileMaker プリンタが変わっても印刷がほぼズレない 印刷設定


## 例: 窓つき封筒に合うように宛名を印刷

![MF17b_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/421ba75c-ecd0-2827-99b5-9a21693f3605.png)

[窓つき封筒 A4三ツ折用](https://www.hisago.co.jp/Search/detail.asp?id=MF17)

FileMaker レイアウトモード
A4サイズでボディーパートを作成
サイズ:
幅:21cm
高さ:29.7cm

A4の用紙は 100×100×97mm で三つ折りにする

>cm(単位)のところをクリックで pt → in → cm と切り替わる

![Image_006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/72722db3-f8ea-555b-7641-4f35a060cb92.png)

ガイドとして封筒の窓と同じサイズの図形を配置 (☑印刷時に非表示にする)

![Image_007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/82ce6d94-e898-7eeb-1dd5-28effb81f814.png)

マージフィールドを設置

封筒のサイズが 220×110ｍｍ なので、封筒の中で用紙が上下左右にズレても大丈夫なように5mmずつ小さく設置

![Image_008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/cfd2d1ba-5828-affd-ac8a-3166130cebf4.png)

## レイアウト設定 - 印刷 - ページ余白設定

「ページ余白設定を使用する」にチェックを入れ、上下左右をすべて"0"に設定

![Image_009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/b5152f5a-aae5-aca8-a5d5-26ce79e07b14.png)

以下ダイアログは、気にせず OK をクリック

![Image_010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/d9ab7d2c-5024-6d30-a911-bc71c5cb3b64.png)

## プリンタが変わっても印刷がほぼズレないレイアウトの完成

![Image_011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/1e58b503-c5d9-5384-6688-10edb728baaf.png)

## プリンタが変わると印刷がズレて困る原因は?

「ページ余白設定を使用する」にチェックを入れていないと、プリンターごとで以下の余白が変わります
コレで困っている人が意外と多いのかも...

「ページ余白設定を使用する」にチェックを入れ、上下左右をすべて"0"に設定した場合、プリンタの余白部分は印刷できませんが、位置はずれなくなります
使用しているプリンタの余白部分には印刷したいモノを配置しないようにすればOK

![Image_012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/6ab539d1-b322-793e-623a-09ec67c1a196.png)

以下のダイアログのせいで、イケナイことをしているようで...結果多くの人が困っているのかも

![Image_010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/d9ab7d2c-5024-6d30-a911-bc71c5cb3b64.png)

デフォルトが、以下の設定のほうが良いように思う

「ページ余白設定を使用する」にチェックを入れ、上下左右をすべて"0"に設定

![Image_009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703491/b5152f5a-aae5-aca8-a5d5-26ce79e07b14.png)
GitHub Enterpriseの課金サイクル

# 概要
GitHub　Enterpriseに登録しているが、ユーザ数の観点でmonthlyの課金のサイクルの詳細がネット上に記載がなかったため、GitHubに問い合わせた。その結果をこちらに共有する。

# 結果
* GitHub enterpriseにおけるmonthlyの課金サイクルでユーザ数の観点では、GitHubのenterpriseの設定画面から確認できる日にちをベースとして請求が実施される

**サイクル基準日の確認方法**：
> Enterprise > Settings > Billing > Billing欄
> 例）Your next payment is due March 26, 2024

上記の例でいうと、
26日時点でのseat数（厳密には、アクティブなseat数）をもとに、課金計算が実施される。（つまり、毎月1日から月末までのサイクルではない）

ここで注意なのが、seat数　not =　user数であることです。
仮に、10saetアクティブで、9ユーザのみが利用している場合、課金されるのは10seatに対してです。また、ユーザ数を減らした場合、seat数は自動的に削減されないれないため、手動でsaet数の削減が必要となります。


**GitHubからの回答：**
> If an account's billing date is 2024-02-15, payment for plan seats would be processed based on the
number of seats on 2024-02-15. Please note the number of seats is not the same as the number of
users.
For example, if 10 seats were purchased for 10 users, then one user is removed from the
organization, there would be 9 users, but still 10 seats. Seats are not automatically downgraded
when they are no longer occupied, as they are often assigned to different members or outside
collaborators at different points in time. Unused seats would need to be manually removed by an
account owner to avoid charges by the next billing date.
For more information, please see Removing paid seats for your enterprise account in the GitHub
Docs
【Rails】No route matchesが起きた不思議な理由

Railsでプロフィール画面を作成、実装したのだが、エラーが起きた。
いじっているうちにエラーの出方が姿を変え、最終的に「えっここ？」というところを直したら解決したので記録しておく。

### プロフィールへのリンクをクリックしても「boardコントローラでprofileのidが見つからない」とエラーが出た。
え、プロフィールの画面へのリンクだからprofileコントローラかuserコントローラしか関係ないし、idを表示させない方法でルーティングを組んでいるので、profile_pathのidがboardコントローラでマッチしないというエラーは、なぜなんだろう・・。と理由が分かりませんでした。

ビューファイルの周辺をいじったりすると逆におかしくなり別のエラーが出たりして直していると、今度は別のエラーが出ました。

### 「No route matches」
「profile_path」は使えない、というエラーが出ましたが、ルーティングの設定はされており、ターミナルで確認しても使えるはず。
またしてもビューファイルの今度は関係なさそうなリンクを直したりしているとまた別のエラーが出てしまい、元に戻したりしていたら、今度は別のエラーが出ました。

### 「end」が足りない
profileコントローラでendが足りないというエラーが出たのでみてみると
```
def update
    if @user.update(user_params)
      redirect_to profile_path, success: t('defaults.message.updated', item: User.model_name.human)      
    else
      flash.now['danger'] = t('defaults.message.not_updated', item: User.model_name.human)
      render :edit, status: :unprocessable_entity
    end

  private

  def set_user
    @user = User.find(current_user.id)
  end

  def user_params
    params.require(:user).permit(:email, :first_name, :last_name, :avatar, :avatar_cache)
  end
end
```
updateアクションの最後にendがついていない！
```
def update
    if @user.update(user_params)
      redirect_to profile_path, success: t('defaults.message.updated', item: User.model_name.human)      
    else
      flash.now['danger'] = t('defaults.message.not_updated', item: User.model_name.human)
      render :edit, status: :unprocessable_entity
    end
  end

  private

  def set_user
    @user = User.find(current_user.id)
  end

  def user_params
    params.require(:user).permit(:email, :first_name, :last_name, :avatar, :avatar_cache)
  end
end
```
ここを直したらエラーが出なくなりました。
最初に出たエラーたちはなんだったんだろう。
[C#] 実行処理中にはボタンを非活性にすると、親切だなって思う

処理に時間がかかるものなら、処理中にアプリ側で実行中なのか分かりづらい。
ボタンを非活性にすると、処理中は「ボタン押せないから処理実行中なんだな」とわかる。
```C#
            //ボタンを非活性にする
            btn.Enabled = false;
            
            //ボタンを活性にする
            btn.Enabled = true;
```

こういう実行処理がある。
```C#
            try
            {
                //実行
                Controller.実行処理();

                //処理完了
                Controller.OutPutLog($"処理が完了しました  時刻:{DateTime.Now}");
                MessageBox.Show("処理が完了しました", "完了", MessageBoxButtons.OK,MessageBoxIcon.None);
            }
            catch (Exception ex)//エラー時
            {
                Controller.OutPutLog($"エラーが発生しました 時刻:{DateTime.Now}\r\n{ex.Message}");
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
```

ボタン活性、非活性を入れ込んでみた。
活性処理をfinallyに入れてあげることで、正常、エラー時、のどちらも対応できる
```C#
            //ボタンを非活性にする
            btn.Enabled = false;
            
            try
            {
                //実行
                Controller.実行処理();

                //処理完了
                Controller.OutPutLog($"処理が完了しました  時刻:{DateTime.Now}");
                MessageBox.Show("処理が完了しました", "完了", MessageBoxButtons.OK,MessageBoxIcon.None);
            }
            catch (Exception ex)//エラー時
            {
                Controller.OutPutLog($"エラーが発生しました 時刻:{DateTime.Now}\r\n{ex.Message}");
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                //ボタンを活性にする
                btn.Enabled = true;
            }
```
Ubuntu22.04にインストールしたVirtualBoxが動作しない場合の対処方法

# 通常インストール
LinuxにVirtualBoxをインストールするには、VirtualBoxボームページからdebパッケージをダウンロードして

apt install <ダウンロードパッケージ>

でインストールできる。

# Ubuntu22.04LTSで仮想マシンを起動すると
カーネルモジュールが無いので/sbin/vboxconfigを実行しろと出てくる。

でrootユーザーで /sbin/vboxconfig を実行するとエラーが出る


# このエラーは・・・
以前のVirtualBoxでUEFI関係のエラーで起動できないなどの現象があったが、インストールログを見てみると違っていた。

このエラーはgcc-12が無いよと言っている。

なので、gcc-12をインストールして、/bin/vboxconfigを実行すると正常に動くようになる。

以上！！
3cdaemonの代わりで使えるフリーのSyslogサーバソフト「Serva」 

## はじめに

ある顧客の対応で、Syslogにメッセージが上がらないとのことでよく多用していた
「3cdaemon」を起動しようとしましたが、PCが変わっていたので、ショートカットだけ
が残っており起動しない。
当然ですが、プログラムが、、インストールされていない。。。
最近の方は知らないかもしれませんが、10年くらい前はネットワークエンジニアの
必須ツールでした。

「3Cdeamon」をダウンロードしようとしたら、有害？でダウンロードできない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/23eb8618-cffb-c628-85a0-dc06fccdbed9.png)

ということで、違うソフトウェアを探しました。

そして、探してよさそうなのが「Serva」でした。
https://freesoft-concierge.com/internet/serva/
この記事は古い情報ですが詳しいです。

https://freesoft-100.com/review/serva.html
これはほぼ最新の情報、これも詳しく書いていただいています。
（有料版の値段が変わっていました。）

Qiitaには、本ソフトの記事がなかった(利用している記事は複数あり)
のでメモ代わりに投稿します。

## ダウンロードして起動

ダウンロード先：https://www.vercot.com/~serva/download.html
対応OS：Serva Community: Windows XP SP3 to Windows 10 plus all the Server versions.
Serva Pro: Windows 7 to Windows 10 plus all the Server versions (see 9).

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/d5e9dd85-2006-bbf6-1681-6660cb79cb1a.png)

Professional　は有料で　92ドル/年
Community　は　Freeで使えます。

対応するサーバサービス機能としては、下記がありました。

HTTP server
FTP server
TFTP server
TFTP client
DHCP server
proxyDHCP server
DNS server
SNTP server
SYSLOG server

ダウンロードして解凍すると下記のようにファイルが展開されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/9c9f7051-8082-14bf-d5d7-ed4408e5abbd.png)

Serva64.exe　が64Bit版のソフトですので、私のPCはWindows10 Pro（64Bit）
マシンですのでこちらを使用します。

初期画面（初めての起動時）はキャプチャし忘れましたが、
Serva64.exe　をマウスでクリックして起動すると
カウントダウンが　7秒くらいでしょうか　その後　下記画面が現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/cc8ef964-ed3f-e648-7d20-7fa0f94d6227.png)

「I’m a “Community”user」をクリックします。すると、本ソフトが起動して
 メイン画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/40eb5396-136f-a575-5780-6f30f7c94a5f.png)

私の場合　Syslogサーバを設定してしまった後なので、これがメイン画面になります。
すっきりですね。　どう設定するのと思いきや。

左の上の角にある、マークをクリックすると、サブメニューが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/649e4942-71f1-6a3b-afa6-c682e1166bd2.png)

Settingsをクリックすると各サーバの設定画面が出てきます。
私の設定したSyslogサーバはこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/5588eee8-10c9-ad33-8467-0e2e13819654.png)

Service Up/Down　でチェックを入れるとサービス起動（次回起動時に起動します。）
PCのアドレス（SyslogサーバのIPアドレス）と、Syslogとして上がってきた、メッセージを
保存するファイルと場所を指定するだけです。

ちなみにSyslogの中身は、こんな感じで、一行おきに色がついていて、
おじさんにも優しい表示でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741340/737fb112-fc3b-4255-753b-507ef8b9e43f.png)


ほかのサービスも同様な手順で設定ができます。
詳しくは、下記リンクに記事がありましたので割愛します。
（助かります、ありがとうございます。）
https://freesoft-concierge.com/internet/serva/
設定終了後は、いったんCLOSE（閉じる）して再起動します。

## サービスの停止

メイン画面の左の上の角にある、マークをクリックして、サブメニューを右クリックし
「Settings」をクリックし、「Serva Community Settings」画面を表示します。
次に、停止するサービスのタブをクリックし、「Service Up/Down」欄のチェックボックス
のチェックを外して「OK」をクリックします。
最後にCLOSE（閉じる）して再起動すると、サービスを停止したタブが閉じたメイン画面
となります。

## 終わりに

おじさんになると、いろんなツール使っていると、使い方わからなくなるんよ！。
ちょっとでもメモっておかないと。　　とし取るのやだね！。
妻のため腹膜透析記録Webアプリを作った話

# 作ったもの
これです。
![腹膜透析アプリ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546752/aec486d6-db93-1b21-fc27-658f768976b0.png)


# 作るに至る経緯
妻は腹膜透析を受けています。
一日3回自室で透析をしてその結果を記録する必要があるのですが
今までは記録用紙に記載していました。
![腹膜透析の記録用紙.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546752/86fdc3ad-5095-0318-48c0-d95155861c20.jpeg)

少し計算したり、時間を測ったりしなきゃいけないのと
病院に行くときにはこの用紙を2週間分持っていく必要があるのが
ちょっと面倒そうだったのでいっちょ作ってみるか、となりました。

# 概要
腹膜透析を実施したときに記録する情報をLocalStrageに記録します。
DBも不要な静的ページだったのでGithubPagesで動作させています。
妻はPC持ってないのでスマホで使う前提のUIになってます。
各々の端末のブラウザのLocalStrageに保存するので
プライバシーやらの問題もないはずです。
同じような記録用紙を使ってる方なら問題なく使用できるはずですが
記録の仕方は病院によって異なるそうなので一概には言えません。


自動で入る値の仕様は以下の通り。
![腹膜透析アプリ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546752/f1b5e0e6-e73f-46cc-2aa6-c69fcfeb76dd.png)

使い方は見てわかると思いますが
カレンダで日付を選択して日々の記録を保存するだけです。
病院には画面を見せるか印刷するかすれば伝わるようです。
（妻からの伝聞のため不鮮明）


# ソースについて
https://github.com/nomura565/peritoneal-dialysis-day-record.github.io/tree/gh-pages

Githubで公開しています。
gh-pagesブランチが最新でgh-pages-deployブランチが公開ページです。

https://nomura565.github.io/peritoneal-dialysis-day-record.github.io/

GithubPagesはこちら。
保存を押下するとブラウザのLocalStrageに保存されますのであしからず。

# 技術的なお話
自分的には初めて状態管理に[Jotai](https://jotai.org/)を使いました。
このアプリにはコンポーネントを大まかに下記のように分けています。

- 情報入力タブ×5
- 画面上部から引き出すドロアー
- カレンダー

それぞれで使う値を最新に保ちつつ、相互に参照するにはUseStateだと面倒だな
ということでこのJotaiはわかりやすかったです。
atomした値を他のコンポーネントでuseAtomするだけなので
UseStateからの載せ替えも楽ですね。

# 最後に
腹膜透析を受けている方が少しでも楽になりますように。