OmniAICreator/Qiita-1.07M huggingface dataset:
Bungeecord で接続したサーバーに存在するプレイヤーを他のサーバーにテレポートさせる

# はじめに
Bukkit プラグインから Bungeecord で管理しているサーバーに接続したプレイヤーを他のサーバーにテレポートさせる方法について。

# 準備
他のサーバーにテレポートさせるにはサーバーに BungeeCord plugin チャンネルを登録する必要があるので、`onEnable` メソッドに登録するコードを含めておきましょう。

```java
@Override
public void onEnable() {
    this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
}
```

これにより、Bukkit プラグインから BungeeCord に文字列を送信する準備が整いました。

# 送信する文字列の準備
登録したチャンネルに命令を送り込むには、文字列を Byte 配列に変換する必要があります。
Spigot のリファレンスには Google Guava の `ByteArrayDataoutput`, `DataInputStream` を使うといいよと書いてあるので、それに従うことにします。

>  The byte array you will send (and receive) will use output generated by the DataInputStream and DataOutputStream classes. For convenience, we will use the ByteArrayDataInput and ByteArrayDataOutput classes from Google Guava, which silences IOException for us.
> [BungeeCord Plugin Messaging Channel](https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/)

```java
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Connect");
out.writeUTF("サーバー名");
Byte[] data = out.toByteArray();
```

# プレイヤーのテレポート
いよいよプレイヤーをテレポートさせます。
(今回は ytshiyugh というプレイヤーを対象とします。)

```java
Plugin plugin = プラグインのインスタンス;
Player player = Bukkit.getPlayer("ytshiyugh");
player.sendPluginMessage(plugin, "BungeeCord", data);
```

`["Connect", "サーバ―名"]` を BungeeCord チャンネルに書き込みました。
対象とした名前のサーバーが存在する場合はサーバー変更が行われます。

# おわりに
ただ単に `player.chat("/server main")` のように書いても、上記のような手順を踏まない限り書いた内容が BungeeCord によって拾われることはないので、プロキシ配下の他のサーバーに接続することは出来ません。

# 参考文献

https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/

https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/plugin/messaging/PluginMessageRecipient.html#sendPluginMessage(org.bukkit.plugin.Plugin,java.lang.String,byte[])
macOSで簡単にローカルサーバーを立ち上げる

ずっと下記記事の方法を使っていたが、macOS Sonomaから利用できなくなったので、情報更新。

https://qiita.com/Arvelt/items/c69003fba95cd993790a


`python`コマンドがなく、`python3`になりました。

## 1. ルートにしたいディレクトリで以下を実行
```
python3 -m http.server 8888
```

## 2. ブラウザで以下にアクセス
`http://localhost:8888`
`http://0.0.0.0:8888`

0.0.0.0の部分にはローカルIPを入れてください。
定期的な振り返りの習慣を作ろう

この記事は [一分で読める小ネタのカレンダー | Advent Calendar 2023 - Qiita](https://qiita.com/advent-calendar/2023/raki) の２０日目

# 振り返りの習慣

- 日記は毎日の振り返り
- さらに週や月で振り返って整理すると、翌週翌月のやるべきことの方針が決められる
- もちろん年次でも振り返る
- サボりすぎた週があったなら、翌週は少しサボるのを控えるとか
- 今週は◯◯しかできなかったから、来週は△△もやろうとか
- □□なことがあったから、■■にしてみようとか
- するのとしないのとでは雲泥の差が出るので、ぜひ定期的な振り返りをして欲しい

# 振り返った次のタイムフレームのテンプレート

- システム開発とか運用とか何にでも応用が効くテンプレート
- DBとかAPI周辺ではCRUDと呼ばれている、すなわち、新規、参照、更新、削除に倣って、新しく始めること、継続すること、改善すること、廃止す（やめ）ること、をリストにする

```bash
# 新規

- 新しく挑戦すること
- 何か始めること
- 習慣づけの始まりみたいな

# 継続

- 習慣になりきってない習慣にしたいこと
- git の練習とか
- 継続して日記を書くとか

# 改善

- 長いコマンドを何度も手打ちしているのを改めるとか
- 既にちゃんと動いている何かを見直すとか
- よくないことをした覚えがあれば改める決意とか

# 廃止

- ついついサボってしまうこととか
- おやつ食べ過ぎちゃうこととかｗ
- これをしない、的なこと

```

- タイムフレーム毎にこれらを更新しながら、自分で自分の成長をコントロールして欲しい

本文はここまで。以下は時間外。

# 週次の振り返り

- 最初はいいんだけど、継続してやっていくと、けっこうたまる
- たまるまではがんばれ
- 慣れたら週次の振り返りはフローデータとして流すのも可

# 月次の振り返り

- 週次の振り返りを月１で振り返るなら４セットで済む
- できたこととできなかったことを見直して、次に役立てる
- やろうと思ってできなかったことは重点的に見直したい。一ヶ月やろうと思ってできなかったのならやりたくないか向いてないんだと思って一旦放棄してもいい。再度挑戦したくなった時に思い出せるように予備としてとっておこう
- 大事なことはまずやってみることで、できたかできなかったは二の次でいい
- 三日坊主も続ければ習慣って言うくらいなので気長にやろう

# ツール

- til に日記を書いているなら、振り返りファイルを別に用意するといい（かもしれない）
- 日記自体を振り返りの単位（週次で振り返るなら週で、月次で振り返るなら月）でファイルにしてもいい
- Trello を使うなら GTD 用のワークスペースか、年間記録ワークスペースを作って使うのもいい
- [こんなかんじ](https://trello.com/b/kgo2qizj)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44420/24bdd64d-421c-8f9a-e8ef-f684edb0f0fd.png)

# 雑

- タバコはね、やめようと思ってやめられるものじゃないんだよｗ
- アンガーマネジメントと併用して、怒りっぽいのは改善したんじゃないかと思う
- git の練習も最初はこういうのから始めた
- 新しい言語の習得とかも新規でやることのネタがつきたついでというか、他に見当たらないからやるか、みたいな（まさに振り返りの習慣が身についたから
- うまくできなくっても気にしないでいいのは前述のとおり
- うまくできたら自分にご褒美をあげよう

# 改善依頼

- 習慣にしてることを挙げていくとか？
- なにかいいツールとかやり方があれば
- 振り返りの方法論へのポインタとか
pgauditを設定して特定カラムアクセス時にemail通知を投げるシステムの構築

# 要点

- テーブル中に記録された個人情報アクセスの監視。
- 開発者並びに特定ユーザが、テーブル中の特定カラムを参照した場合にemail通知を送信する。
AWSを用いて構成した。システム構成図は以下。

![pgaudit_infra_page-0001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367227/42df63a3-85d4-5b54-0fdb-37fc4b0efbaf.jpeg)

# 構築手順
[準備.pgAuditについて](#1-pgAuditについて)
[1.PostgreSQLの設定](#1-PostgreSQLの設定)
[2.CloudWatch Logsのサブスクリプションフィルター設定](#2-CloudWatchLogsのサブスクリプションフィルター設定)
[3.SNSトピック作成](#3-SNSトピック作成)
[4.Lambda関数でメッセージ構築](#4-Lambda関数でメッセージ構築)
[ex.TerraformによるIaC化](#5-TerraformによるIaC化)

# pgAuditについて
 PostgreSQL向け監査用ロギングツール。
[公式サイトはこちら](https://github.com/pgaudit/pgaudit)
本ツールを用いると例えば「○○が○○という操作をしました」「○○カラムを○○ロールが参照しました」というログを証拠として残すことができる。

## pgAuditによる取得可能なログの種類
2種類存在する。
- **セッション監査ログ**
- **オブジェクト監査ログ**
### セッション監査ログ
PostgreSQLのデータベース(CREATE DATABASEで作成したデータベースのこと)を全て監査する場合、ロール(ユーザ)による操作を全て監査する場合はセッション監査ログを用いる。
pgaudit.logに値を設定し、設定した値に従った形式の記録をする。
(例：対象データベースの監査、対象ロールの監査)

#### 例：ロール中の操作を全てログ出力する場合

```sql
ALTER ROLE <role_name> SET pgaudit.log='all';
```
例えば、上記の設定を行うと対象ロール上での操作を全て監査できる。

### オブジェクト監査ログ
あるテーブルの特定カラムを参照した場合に使うことが主。
pgaudit.roleにロールを設定し、そのロールに与えられた権限(監視権限)に関する操作を全て記録する。(例：対象テーブルの監査、対象テーブル中のカラムの監査)

#### 例：テーブル中の特定カラムアクセスの監査

オブジェクト監査の場合、pgaudit.role用の監査ロールが必要。
例として監査ロールをrds_pgauditという名前で作成する。他の記事でもこの名前で作成している場合が多いが、名前は自由で良い。

```sql
CREATE ROLE rds_pgaudit; -- どんなロール名でも良い
```

その後、監査対象カラムへのアクセス権限を監査ロールへ与え、監査対象とするロールのpgaudit.roleに監査ロールを設定する。監査対象が複数であってもそれをロールに設定できる。

```sql
GRANT SELECT (column_name) ON <table_name> to rds_pgaudit; # table_name.column_nameのselect操作のアクセス権を監査ロールへ付与。
ALTER ROLE <role_name1> set pgaudit.role=rds_pgaudit; # 監査対象のロールに割り当て
ALTER ROLE <role_name2> set pgaudit.role=rds_pgaudit; # 監査対象が複数でも可能
...
```
設定反映のために、RDS or Auroraを再起動。
上記設定でpgaudit.role=rds_pgauditが設定されたロールについて、対象カラムへの参照したログを取得できる。

# CloudWatch Logsのサブスクリプションフィルタ設定

AWS RDS, Auroraの場合、通常CloudWatch Logsがログ出力先となる。
次に、CloudWatch Logsからサブスクリプションフィルタを設定し、宛先とログの条件を設定する。

サブスクリプションフィルタのフィルタパターンは、pgAuditからやってくるログがどのようなログになるかを知っている必要がある。よしなにやって頂く他無いが、一応記載。

(CFnの例)
```
FilterPattern: '"LOG: AUDIT: OBJECT" SELECT'
```
としておくと、例えば以下のログがCWLogsに流れた時にフィルターに引っかかる
```
2023-12-12 10:00:00 UTC:192.168.1.123(50123) postgres@postgres:[12345]:LOG  AUDIT: OBJECT,1,1,READ,SELECT,TABLE,public.user,select * from user;,<none>
```
フィルターに引っかかったログは、lambdaへ流れる。

# Lambdaでメッセージ構築

今回はnode_modulesとセットでzip化してデプロイする。
サブスクリプションフィルターによってイベントがlambdaに流れ、eventからログを参照することができる。

```js
const AWS = require('aws-sdk');
const sns = new AWS.SNS();

exports.handler = async (event) => {
  const snsParams = {
    Message: 'Access!',// 本文
    Subject: 'specific column access',// メールタイトル
    TopicArn: 'your_sns_topic_arn',
  };

  try {
    await sns.publish(snsParams).promise();
    return { statusCode: 200, body: 'Message sent successfully!' };
  } catch (error) {
    console.error('Error publishing message to SNS:', error);
    return { statusCode: 500, body: 'Error publishing message to SNS' };
  }
};

```

# SNS設定
トピックを作成して、宛先emailを設定する。
お疲れさまでした。

# Terraformによるサンプル
参考まで。

https://github.com/kilinlili/db_audit
異なるバージョンのカーネルでビルドされたカーネルモジュールを使う方法

# はじめに
Linuxにおいてデバイスドライバ実装などの場面でカーネルモジュールの開発をする際には、原則としてカーネルモジュールを動作させる環境とビルドする環境のカーネルのバージョンを揃える必要がある。
しかし開発過程において、動作させる環境と異なる環境で開発を行いたい場面が出てくるかもしれない。(使用しているディストリビューションの種類などが違えば、環境をまったく同じにするのは困難である)
そのような際に、カーネルのバージョンの異なる環境で開発したカーネルモジュールを、他の環境で動かすためのテクニックを紹介する。

<font color="Red">
!!!

本記事の内容はあくまでも開発の過程においての利便性のための工夫であり、システムが壊れることが許されない本番環境での使用は推奨しません。
また本記事に掲載している行為によりシステムの環境を壊してしまった際の責任を、筆者は一切負いません。
ご了承ください。
!!!
</font>

後述するが本記事では、vermagicによる整合性検証をパスするための方法を解説する。


# カーネルモジュールの整合性検証
参考文献 : [vermagic と modversions に関する覚え書き](https://enakai00.hatenablog.com/entry/20110509/1304910773)
[Linuxのカーネルモジュールの整合性検証の仕組み](https://blog.bitmeister.jp/?p=2916)

カーネルモジュールをカーネルにインストールする際には、モジュール自体がビルドされたカーネルのバージョンと、今から動作させようとしている環境のカーネルのバージョンが一致しているかのチェックが行われ、一致しない場合はインストールできないようになっている。
その検証にはvermagicによる検証とmodversionsによる検証があり、どちらが行われるかはカーネルコンパイル時のconfigで決まる(らしい)。
本記事ではvermagicの検証について扱っている。
詳細は上記2つの記事を参照されたい。
modversionsによる検証についての情報も記載されている。

# vermagic
```
modinfo <モジュール名>
```
で確認することができる。
カーネルコンパイル時に設定されている主な設定値を文字列化したものが入っている。
デフォルトでvermagicに入る文字列は、以下のヘッダファイルで定義されている。
https://elixir.bootlin.com/linux/v6.6.4/source/include/linux/vermagic.h#L42
```
#define VERMAGIC_STRING 						\
	UTS_RELEASE " "							\
	MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT 			\
	MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS	\
	MODULE_ARCH_VERMAGIC						\
	MODULE_RANDSTRUCT
``` 

# vermagicの検証をパスさせる方法


```
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
```
このようなコマンドでカーネルモジュールをビルドすると、```<モジュール名>.mod.c```というファイルができあがる。このファイルにはモジュールの依存関係や、カーネルがモジュールを正しくロードして管理するために必要な情報が含まれている。
このファイルの中に、MODULE_INFOマクロにてvermagicを定義している箇所がある。

```
MODULE_INFO(vermagic, VERMAGIC_STRING);
```
VERMAGIC_STRINGは前節で紹介した、デフォルトでvermagicに入る文字列である。
この部分を、

```
MODULE_INFO(vermagic, "versionX");
```
このように任意の文字列に置き換えて再びmakeを行うことで、任意のvermagicを持ったカーネルモジュールのオブジェクトファイルを生成することができる。
ここを動作させる環境のカーネルバージョンに合った文字列にすることで、異なる環境でビルドしたカーネルモジュールを使うことができる。

## 動作確認
こちらの記事の要領でサンプルのカーネルモジュールを作る流れの中で、本記事で紹介したvermagic改変の動作確認を簡単に行うことができます。
とても簡単にカーネルモジュールを動かすことができるので、興味のある方はぜひ試してみてください。
(上記のmakeコマンドもこの記事のものです。)
[組み込みLinuxデバイスドライバの作り方 (1)](https://qiita.com/iwatake2222/items/1fdd2e0faaaa868a2db2)
.vscode/setting.jsonで設定しているのに、auto importが絶対パスに設定されない場合

# Vscode本体の設定も直すと解決した
![スクリーンショット 2023-12-08 2.16.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1205047/c7ded563-f9bd-0578-2eff-af60efa62db4.png)

 注意! 設定後はwindowのreloadを忘れずに
aptとapt-getの違い

chatGPT4.0に聞きながら裏取りもしつつ。aptとapt-getの違いを調べてみました。
何か間違えていたら誰か指摘して下さい。

# それぞれの特徴
## apt-get
1. 1998年にリリース
1. Debian系のシステムでパッケージ管理(※)をするコマンドラインツール。
※パッケージ…Linuxシステムでソフトウェアを管理する単位。ソフトウェア、プログラム本体やライブラリ、設定情報をまとめた一つのファイル
1. Advanced Package Toolの略。以下の機能を持っている
    1. インストール
    1. アップデート
    1. 最新バージョンへの更新
    1. 削除
    1. 依存関係の管理(あるプログラムで必要なシステムやライブラリをインストールしたり、不要なシステムの削除を行う)
    
1. Non-Interactive mode:こちらの確認をせずに操作を実行する
    


## apt
1. 2014年にリリ―ス
2. apt-getとapt-cacheの機能を統合した
    - apt-cacheの機能
        1. パッケージの検索
        1. パッケージの情報表示
        1. 依存関係の確認
1. Interactive mode:操作を続けるかどうかを確認してくれる

# それぞれの利点
## apt-getの利点
apt-getは、コマンドの出力に一貫性があり、挙動に関して情報が多い
1. スクリプトや自動化ツールで一貫した動作をとってくれて扱いやすい
1. どう動くのか分かっているのでエラーハンドリングしやすい

## aptの利点
一つのコマンドで、apt-get、apt-cacheの機能、両方を使えるようになった。
1. 機能を簡略化できるようになった。
1. 複雑な依存関係に対応しやすくなった
    





## 参照
https://manpages.debian.org/jessie/apt/apt-get.8.ja.html

https://xtech.nikkei.com/it/article/Keyword/20070207/261245/

https://aws.amazon.com/jp/compare/the-difference-between-apt-and-apt-get/

https://www.ktakedev.com/blog/119/


https://strkpy.hatenadiary.org/entry/20100617/1276750564
WindowsでUbuntuのインストールする方法

こんにちは！

今回はWindowsにUbuntuをダウンロードする方法を記載しようと思います。

## インストール手順

まずは、画面下の検索から「コマンドプロンプト」と入力します。

そしてコマンドプロンプトに遷移します。この画面の状態ではインストールするための権限がないため、Ubuntuをインストールすることができません。

そのため、再度検索からコマンドプロンプトと入力し、コマンドプロンプトを右クリックします。すると、一番上にもしくは画面右側に「管理者権限」で開くがあるのでそれをクリックします。

![管理者権限.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3500754/f21fa7eb-e388-3673-e5dc-2a1e44a5e049.png)

管理者権限で開いたコマンドプロンプトで以下のように打ち込みます。

````
wsl --install 
````

こうすることで必要となるコンポーネントのインストールと有効化、Linuxカーネルのダウンロードとデプロイなどの操作が行われます。

その中にUbuntuのインストールも含まれているので、この実行が完了したらUbuntuがインストールされているはずです！！

## Ubuntuの確認方法

確認方法もここで説明したいと思います。

まず、スタートメニューをクリックします。そこからすべてのアプリをクリックします。Uの行を確認し、Ubuntu が存在しているかどうかを見ることができます。(下記の画像を参考にしてください)

![Ubuntu存在確認方法.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3500754/1d06af2c-5d8c-b318-03f0-c38e68bb05e5.png)

## バージョンの確認方法

ついでにUbuntuのバージョン確認を行います。

先ほどの手順の続きからUbuntuを開きます。

その中で以下のコマンドを入力します。

````
lsb_release -a
````

このコマンドの役割は、OSのディストリビューションやバージョン情報を確認することができます。

````
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.3 LTS
Release:        22.04
Codename:       jammy
````

私のデバイスではこのような結果が出ました！

バージョンは22.04みたいですね、、。よかった:relaxed:

今回はここまでにしたいと思います！ありがとうございました。
うるう年の一年前を取得すると…

## 冒頭

現場のＰＪでの詳細設計にて以下のような記載をしました。
「○○日より1年前の日付を取得する。」
特定の日付を基準にして、その前後の日付を取得するというよくある処理だと思うのですが
レビューにて「うるう年の一年前の日付を取得した場合どうなりますか?」と質問があり、私もふと疑問に思いました。

「うるう年の一年前の日付を取得したら、前年の3月1日なのか?それとも2月28日なのか？」

今回の記事はそんな疑問から始まった記事であり、実際にいくつかの言語、DBで試してみた結果を書いていきたいと思います。

## Javaの場合

Javaにて日付の計算を行う場合、Calendarクラスを使用します。Caleandarクラスのインスタンスを生成し、生成したインスタンスのsetTimeメソッドの引数にDate型を指定することで日時の設定ができます。そしてaddメソッドにて第一引数にCalendar.YEAR、第二引数にint型(加算の場合は正の数、減算の場合は負の数)を指定することで日付の計算が可能となります。
以下がサンプルコードになります。
```java:main.java
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;

public class Main {
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date1 = sdf.parse("2020/02/29");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date1);
        calendar.add(Calendar.YEAR, -1);
        Date date2 = calendar.getTime();
        System.out.println(sdf.format(date2)); //2019/02/28
    }
}
```
Javaの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。

## JavaScriptの場合

JavaScriptにて日付の計算を行う場合、Date型のインスタンスを生成した後に、生成したインスタンスのsetFullYearメソッドを使用し、引数にて生成インスタンスのgetFullYearメソッド使用して取得した年を加算、減算することで可能となります。
(本記事では年単位の計算を行っていますが、setMonth、setDateメソッドを使用することで月単位、年単位での計算も可能です。)
以下がサンプルコードになります。

```javascript:main.js
    var dt = new Date(2020, 1, 29);
    dt.setFullYear(dt.getFullYear() - 1);
    console.log(dt);//2019/03/01
```

JavaScriptの場合はうるう年の一年前の日付を取得した場合、3月1日となりました。
Javaでは2月28日、JavaScriptでは3月1日と「うるう年の1年前の日付を取得する」という処理について言語により結果が異なるということがわかりました。
引き続き他の言語、ＤＢも見ていきます。

## PHPの場合

PHPにて日付の計算を行う場合、strtotime関数を使用する方法とDateTimeクラスのmodifyメソッドを使用する方法の2つのやり方があります。
以下がサンプルコードになります。

```php:main.php
<?php
echo date("Y-m-d", strtotime("2020-02-29 -1 year")); 
$date = new DateTime('2020-02-29');// 2019-03-01
$date->modify('-1 year');
echo $date->format('Y-m-d'); // 2019-03-01
?>
```

strtotime関数を使用する方法、DateTimeクラスのmodifyメソッドを使用する方法いずれも3月1日となりました。

## PostgreSQLの場合

PostgreSQLにて日付の計算を行う場合、INTERVAL型で時間を指定し、日付から加算、減算することで可能となります。
以下がサンプルコードになります。

```sql:postgre.spl
select cast('2020/02/29' as date) - cast('1 year' as INTERVAL)
--2019-02-28T00:00:00Z
```

PostgreSQLの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。

## MySQLの場合

MySQLにて日付の計算を行う場合、DATE_ADD、DATE_SUB関数を使用し、第一引数に計算元となる日付、第二引数にINERVAL型で時間を指定することで、計算元日付からそれぞれ加算、減算することが可能となります。
以下がサンプルコードになります。
(減算のため、DATE_SUB関数を使用していますが、DATE_ADD関数で第二引数にINTERVAL -1 YEARを指定することで実質減算を行うこともできます。)

```sql:mysql.spl
select DATE_ADD('2020/02/29', INTERVAL -1 YEAR) FROM dual;
--2019-02-28
```

MySQLの場合はうるう年の一年前の日付を取得した場合、2月28日となりました。

## SQLiteの場合

SQLiteにて日付の計算を行う場合、date関数を使用し第一引数に計算元となる日付、第二引数に時間を指定することで加算、減算することが可能となります。
以下がサンプルコードになります。
```sql:sqlite.spl
select date('2020-02-29', '-1 year');
--2019-03-01
```

SQLiteの場合はうるう年の一年前の日付を取得した場合、3月1日となりました。

## まとめ

ここまで、いくつかの言語、DBでうるう年の一年前の日付の取得結果を確認してきましたが、まとめると以下のようになります。

Java             →  2月28日を出力
Java Script      →  3月1日を出力
PHP              →  3月1日を出力
PostgreSQL       →  2月28日を出力
MySQL            →  2月28日を出力
SQLite           →  3月1日を出力

言語、DBによってうるう年の一年前の日付の取得結果は異なるという面白い結果が得られました。
他の言語、DBではどうなるのか、あるいはどうしてこのような違いが生まれるのか、日付計算のロジックに違いがあるのかといったように話を広げても面白いかもしれません。
今回の記事は以上となります。
UbuntuからのGitHub操作でよく使うコマンド

今回はUbuntuでのGitHub操作をする際によく使うコマンドを紹介したいと思います。

## コミットの際に使用するコマンド

おそらく開発の際に最も使用するコマンドはコミットの際に使うコマンドかと思います。そのためここではコミットの際に使用するコマンドに関して記載したいと思います。

#### git status

このコマンドは編集して変更があったファイルを確認することができます。このコマンドで編集したファイルを確認し、コミットするという手順になります。

````
git status
````

#### git add . もしくは git add [ファイルパス]

次にファイルをaddするときのコマンドです。

````
git add .
````

このコマンドの意味としては、編集したファイルや追加したファイルを一括でaddしてくれるコマンドです。このコマンドがファイルパスを打つ必要がないので個人的に愛用しています！！

#### git commit -m "コミットメッセージ"

次はコミットするコマンドです。

````
git commit -m "first commit"
````

括弧内にコミットメッセージを記入してコミットしないとプッシュできないのでここは必須のコマンドになりますね、、。

メッセージはなかなか毎回困るところなのですが、私は日時にしています！！GitHubからいつコミットしたかは確認できてしまうので重複してしまうのですが一番無難で楽だなーと思ってそうしています。

#### git push origin main

````
git push origin main
````

このコマンドを実行してプッシュします。

おそらく多くの人はこの後にgit status で状況を確認するかなーと思います！

## ブランチの操作の際に使用するコマンド

コミットの際程このコマンドは使用しませんが、チーム開発などをする際にはこのコマンドをたくさん使用することがあると思いますのでここで記載したいと思います。

#### git branch [ブランチ名]
[ブランチ名]にブランチを切るときに使用するコマンド

````
git branch [ブランチ名]
````


#### git branch 
ローカルのブランチの一覧を表示する

````
git branch 
````

#### git branch -a

リモートのブランチを含めたブランチの一覧を表示する

````
git branch -a
````

#### git branch -r

リモートのブランチのみの一覧を表示する

````
git branch -r
````

#### git checkout [ブランチ名]

これはブランチを切り替えるときに使用するコマンドです。

````
git checkout [ブランチ名]
````

#### git checkout -b [ブランチ名]

このコマンドを実行することで新規ブランチの作成とブランチの切り替えを行うことができます。

````
git checkout -n [ブランチ名]
````

まだまだ使うコマンドあるよ！！と思う方もいらっしゃるかと思うのですが、これくらいにしておこうかと思います！！

ありがとうございました:santa_tone3:
ゲーミフィケーションで健康寿命を延ばす！ウェルネス業界の新たな潮流

# 現代社会における健康寿命の延伸とゲーミフィケーションの活用

現代社会では、健康寿命の延伸が重要な課題となっています。しかし、健康的な生活習慣を維持することは、なかなか難しいものです。

例えば、厚生労働省の調査によると、日本人の3人に1人は運動不足であると言われています。また、肥満率も年々上昇しており、2022年には成人の3割以上が肥満と診断されています。

このような状況の中、注目を集めているのが「ゲーミフィケーション」の活用です。

![gamification-20231208-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129712/0e5db0c8-554b-1263-54bc-c76c70629298.png)


# ゲーミフィケーションとは
ゲーミフィケーションとは、ゲームの要素や手法を非ゲームの分野に応用する手法です。ウェルネス業界においては、ゲーミフィケーションを活用することで、ユーザーのエンゲージメントを向上させ、行動変容を促すことが期待されています。

例えば、フィットネスアプリでは、歩数や消費カロリーなどのデータを記録し、目標達成に向けて、ポイントやバッジなどの報酬を獲得することができます。また、ダイエットアプリでは、食事や運動の記録をサポートし、ユーザーの食生活や運動習慣を改善することができます。

![gamification-20231208-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129712/a7a67260-3e81-dfba-e7a0-0a7d703b17de.png)


# 海外の事例
- アメリカのフィットネスアプリ「Nike Run Club」では、ユーザーのランニングの距離やペース、時間などに基づいて、ポイントやバッジ、ランキングなどが付与されます。これらの要素により、ユーザーは競争心や達成感を刺激され、ランニングを継続しやすくなります。

- イギリスのダイエットアプリ「MyFitnessPal」では、ユーザーが食事や運動を記録すると、カロリーや栄養素の摂取量を把握することができます。また、コミュニティ機能により、他のユーザーと交流しながら、ダイエットを続けることができます。

- アメリカのメンタルヘルスアプリ「Moodpath」では、ユーザーが自分の気分を記録すると、その気分に基づいて、リラックス方法や対処方法などのアドバイスが提供されます。これらのアドバイスにより、ユーザーはストレスや不安をコントロールしやすくなります。

これらの海外の実例からは、ゲーミフィケーションを活用することで、ユーザーのエンゲージメントを向上させ、行動変容を促すことが有効であることが示唆されます。

# ゲーミフィケーションの効果
ゲーミフィケーションを導入したウェルネスサービスの利用者のうち、目標を達成した人は70%に上ることが明らかになっています。これは、ゲーミフィケーションがユーザーの行動変容を促す上で、有効な手段であることを示しています。

例えば、アメリカの調査会社であるForrester Researchの調査によると、ゲーミフィケーションを導入したウェルネスサービスの利用者のうち、目標を達成した人は70%に上ることが明らかになりました。この調査では、ゲーミフィケーションを導入したサービスは、従来の非ゲーミフィケーションのサービスと比較して、目標達成率が2倍以上高いことが示されています。

また、イギリスの保険会社であるアクサが、ゲーミフィケーションを活用した健康増進プログラムを実施したところ、参加者の体重減少率が、従来のプログラムと比較して2倍以上高くなりました。このプログラムでは、歩数や睡眠時間などのデータを記録することで、ポイントやバッジを獲得することができ、参加者は競争心や達成感を刺激され、健康的な生活習慣を継続しやすくなりました。

![gamification-20231208-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129712/0683483d-3eeb-78fa-2192-3a446a6dcfbd.png)


# 結論
ゲーミフィケーションは、ウェルネス業界における新たな潮流として、ますます注目を集めています。ゲーミフィケーションを活用することで、ユーザーのエンゲージメントを向上させ、行動変容を促し、健康寿命の延伸に貢献することができるでしょう。

また、ゲーミフィケーションは、健康以外の社会問題にも応用できる可能性があります。例えば、環境問題や教育問題など、人々の行動変容が求められる分野で、ゲーミフィケーションを活用することで、社会課題の解決に貢献することができるかもしれません。

次回の記事では、『「遊んで地球を救え！」 ゲーミフィケーションで社会問題に挑む』というタイトルで、ゲーミフィケーションが社会問題にどのように応用されているのか、具体的な事例を交えてご紹介します。
入茶しました

# ご挨拶
皆様初めまして、Kawa_です。この度ABC314で入茶しました。
正直このような記事は世にあふれるほどあるでしょうが、この記事はその中でもトップクラスで参考にならないです。こんなことが書いてあるにもかかわらずまだ読もうと思っておられる物好きのお方がおられましたら、どうぞお楽しみください。

# 入茶までにしたこと
ほんとは、「ご挨拶」と「最後に」だけで乗り切るつもりでしたが、もしかしたらここに入茶の秘訣をみにくる人がいるかもと思い、こんなトピックを作りました。正直、私の記事を見るくらいならもっと有益な記事がたくさんあるのでそれを参照してください。（寝る前でめんどうなのでリンクなどはありませんが）

- 毎週ABC参加
- A,B問題埋め、早解き
- C問題の練習
- 奇跡の４完
継続は力なりです。あと、A,B問題は少なくとも安定して、なるべく早く解けるといい気がします。C問題は正直ギャンブルなところもありましたが、ちょこちょこ正解していたのでレートの足しにはなっていたと思います。入茶回は奇跡的に4完してレートをたくさんもらったので、それも入れています。
これをみている灰色の皆さんが入茶できることを願い、応援しておきます。頑張ってください。
私も落灰しないよう、入緑できるようにこれからも精進しようと思います。

# 最後に
と、まぁ所詮入茶ごときで語ることなどほとんどありませんでした。
8ヶ月というのは地味に長い時間でしたが、精進などをしてからは割と順調にレートが上がったので向き合い方が大切だと感じましたね。これからも、緑を目指して頑張ってまいります。応援、対戦よろしくお願いします。
Arduinoのレジスタをざっと理解する 前編

:::note warn
情報処理の講義でArduinoのレジスタを触り始めたのですが、解説が絶望的なのでクラスの誰か一人でも救済できたらいいなと思って書いた記事です。
:::

## Arduinoとは

ATmega328Pというマイコンが載っている基板のことです。マイコンの入出力ピンがそのままピンソケットにつながっているって解釈して問題ないです。マイコンをジャンプワイヤー刺しやすくしましたみたいな。

## Arduino言語やめろ

Arduino言語とかいうあたかも別言語が登場したかのように講義では扱われてますが、この解釈かなり危険なのでマジでやめた方がいいです。基本的にただのC言語[^1]です。

Arduino IDE上では省略されていますが、内部では

```c:
#include <Arduino.h>
```

が記述されています。普段、プログラムの先頭に

``` c
#include <stdio.h>
```

して`printf()`とかが使えるようにするのと同じように、`Arduino.h`をincludeして`pinMode()`や`digitalWrite()`を使えるようにしてるだけです。

で、`stdio.h`をincludeしたからといって**stdio言語**って言わないですよね？なのでArduino言語って言うのは良くない（というよりキモい）と言うわけ。

まとめると講義中に言われている

> Arduino言語ではなくC言語で記述しなさい

は表現として適切ではなく、

> Arduino.hで定義されている関数を使うな

と解釈するのがいいと思います。

要するに`printf()`を使わずに文字を画面に表示しろって言われてるのと立ち位置は完全に一緒です。多分（知らんけど）メモリ操作をゴリれば`printf()`を使わずに文字列を出力できると思います。

それと同じであの先生は`digitalWrite()`を使わずにLEDチカチカしろと俺たちに言っています。ただの縛りプレイが科されているだけです。

:::note info
- `Arduino.h`というものが内部に存在する。
- `Arduino.h`があろうとなかろうとC言語であることに変わりはない
- ただの`digitalWrite()`と`pinMode()`禁止縛り
:::


## レジスタ操作のトリック

ポートレジスタはピンの状態を表す変数です。`pinMode()`や`digitalWrite()`はポートレジスタを簡単に操作するための関数です。

なので、ポートレジスタを直接操作できるプログラム力があるなら`pinMode()`や`digitalWrite()`がなくても問題ないという理論です。~~マジでやめた方がいい~~


Arduinoには3つのポートがあり、1ポートあたり最大8ピンを操作できます。

- ポートB : D8からD13
- ポートC : A0からA5
- ポートD : D0からD7

が割り当てられています。

![アートボード 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/f8b1c5dc-97ae-8177-f8a2-a8d32d2a8698.png)

それぞれのポートには3つのレジスタ変数があります。（にはB, C, Dが入ります）

|レジスタ変数|役割|
|---|---|
|  DDRX  |  入出力方向の設定レジスタ。`pinMode()`に対応|
|  PORTX |  出力レジスタ。`digitalWrite()`に対応  |
|  PINX  |  入力レジスタ。`digitalRead()`に対応 |

例としてDDRXについて記します。PORTX, PINXについても同様です。

![アートボード 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/4a1a9044-f9cc-93d8-0a11-4a57e1c3d543.png)

例えばDDRDを操作してPD6だけを出力ピン、ポートDの他のピンを入力にするためには

```c
DDRD = 0B01000000;
```

とします。`0B`は2進数表記であることを示す記号です。

## レジスタのメリット

例えば、8番ピン(PB0)と9番ピン(PB1)をHIGHに設定したい時、普通は

```c
digitalWrite(8, HIGH);
digitalWrite(9, HIGH);
```

としますが、プログラムは上から下へ流れるのでこれだと8がHIGHになった後に9がHIGHになります。ほんとに数[ns]のお話ですが、遅れることは遅れます。

レジスタ操作を使うと

```c
PORTB = 0B00000011;
```

と書けます。これなら8と9を同時にHIGHにできます。

## レジスタのデメリット

- シンプルにプログラムが読みにくい
- 沼ったら数[ns]節約するために、デバッグに数時間食われる

ほとんどの場合デメリットの方が大きいと思うので、普段はdigitalWriteとかを使うのが推奨です。なんかかっこいいからとかすごそうだからのノリで使うべきではない気がします。

## 次回予告

- 具体的なレジスタ操作
- ピンを個別で設定する方法（ブール代数）

[^1]: 正確にはC++言語ですが、C++は基本的にCの機能を含んでいるので、この講義中はC言語と解釈してしまって問題ないです。
BigQuery → TensorFlow Dataset

:::note info
本記事は [ZOZO Advent Calendar 2023](https://qiita.com/advent-calendar/2023/zozo) シリーズ 7 の 8 日目の記事です。

:::


# 概要
BigQuery のテーブルを TensorFlow Dataset に変換する方法を2つご紹介します。

# 1. BigQuery リーダーを使用する方法

[チュートリアル](https://www.tensorflow.org/io/tutorials/bigquery?hl=ja#bigquery_%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E3%80%81tensorflow_dataset_%E3%81%AB%E5%9B%BD%E7%A8%8E%E8%AA%BF%E6%9F%BB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80
)記載の方法です。

```python
import tensorflow as tf
from tensorflow_io.bigquery import BigQueryClient
from google.cloud import bigquery

PROJECT_ID = "project_id"
DATASET_ID = "dataset_id"
CSV_SCHEMA = [
      bigquery.SchemaField("user_id", "INTEGER"),
      bigquery.SchemaField("item_id", "INTEGER"),
      bigquery.SchemaField("score", "INTEGER"),
]

def read_bigquery(table_name):
    tensorflow_io_bigquery_client = BigQueryClient()
    read_session = tensorflow_io_bigquery_client.read_session(
        "projects/" + PROJECT_ID,
        PROJECT_ID,
        table_name,
        DATASET_ID,
        list(field.name for field in CSV_SCHEMA),
        requested_streams=2,
    )

    dataset = read_session.parallel_read_rows()
    return dataset

train = (
    read_bigquery("table_name")
    .batch(256)
)
```

# 2. Cloud Storage を介する方法
以下の方法は Cloud Storage に中間データを保存したい場合に使えます。
## 1. BigQuery → Cloud Storage

```sql
EXPORT DATA
  OPTIONS (
    uri = "gs://bucket_name/dataset/*.csv",
    format = "CSV",
    overwrite = true,
    header = true
  )
AS 
SELECT
  user_id,
  item_id,
  score,
FROM `project_id.dataset_id.table_name`
```

 ## 2. Cloud Storage → TensorFlow Dataset 
```python
import tensorflow as tf
train_data = tf.data.experimental.make_csv_dataset(
    file_pattern="gs://bucket_name/dataset/*.csv",
    batch_size=256,
)
```
ISUCON13に参加した

ISUCON13に「3年A組大前先生」というチームで初参加しました。
来年に向けてのふりかえりと記録を残しておきます。

# 参加するきっかけ

パフォーマンスチューニングの勉強会として、社内ISUCONを主催したのがきっかけ。
最初に座学をした後、実際にパフォーマンスチューニングを競ってもらって楽しい勉強会になった。
主催するにあたりISUCON本も読んで知識も付けたし、実際のISUCONに参加したら楽しそうだなと思いとりあえず申し込んで枠を確保してからメンバーを募った

# メンバーと役割
- eichisanden（自分）
主にインフラ担当
AWS環境構築したりデプロイスクリプト書いたり、pprof仕込んだり

- oohira
GitHubにコードをプッシュしたり
主にN+1解消とキャッシュ周りを担当していた

- taclose
インデックスや、MySQLとnginxのパラメータチューニングしたり

最初はエンジョイのつもりだったが、どうせならAREを目指そうということになった

# 練習
集まっての練習は週末に3回やった。
- 1回目 作戦会議。タスク洗い出して担当を割り振り。次回までに各自が準備
- 2回目 本戦を想定しながらISUNARABEでisucon12予選をやってみた
- 3回目 APとDBを分離したり、AP2台にしたり、サーバー分割の練習

元々練習は2回の予定だったが、サーバー分割する練習しとかないとマズイだろうということで追加した

それとは別に自主練で過去問を２つほどやった
他のメンバーも同じ感じで自主練してくれていた。

# 試合前半

今年を象徴するアプリがお題という事前情報があったので生成AI的なチャットアプリとかかなと想像していたが、まさかのDNSがお題（アプリ自体は動画サイトだが）
PowerDNSというものを初めて聞いたし、DNSとMySQLがどう関連するの？ DNS水責めってどう対策するの？と疑問だらけの中、試合開始。

初動は自分はAWS環境構築したり、マニュアル読んだり、各種セットアップでお昼近くまで掛かってしまったけど、まあ想定範囲内な立ち上がり。
oohiraがER図とテーブルのレコード数を貼ってくれたり、自分がCRUDを作成したのはアプリの構成を理解するのに役立った。

# 試合中盤

インデックス、N+1、キャッシュを他メンバーが頑張ってくれて順調にスコアを伸ばして15:00頃にポータルを見た時は25位だった。ここまでは順調。
誤算だったのは、練習では１つ改善して即ベンチマーク実行のサイクルで回していたが、
当日はなかなかベンチマーカーの順番が回って来ず、１つずつ流してたら修正が溜まってしまったり、pprofもベンチマーカー実行中に手動で叩くようにしてたので、ベンチマーカーが動くのを待つ必要があったりして、特に自分はそれに時間を取られて他の改善に手が回らなかった。


# 試合後半
残り2時間になり、予定ではサーバ分割をする時間になった。
ただ、まだまだアプリの直すところはあったので1台構成のままフィニッシュした方がスコアは伸びる可能性もあったが、早く分離できれば続きのチューニングが出来るし悔いも残したくないのでサーバ分割を始めた。 

（ここから殆どスコアが上がらず泥沼に入っていく...）

とりあえず、DBの負荷が高いので分離、DNSも専用機を割り当てたかったが構成がややこしかったので、とりあえずPowerDNSのMySQLだけ分離して下記のような構成とした。

- サーバ1 nginx、PowerDNS, アプリ
- サーバ2 PowerDNSのMySQL
- サーバ3 アプリのMySQL

後から他のチームの構成を見てもこれに近かったのでコンセプトは外してなさそうだが、実際やるのは大変だった。

普段仕事で使っているPostgreSQLと違って、MySQLはユーザの属性にlocalhostだけなのか別のサーバーにも接続できるのかの属性を持っているが、初期状態はlocalhostになっていたのを変えるだけで一苦労だった。
焦ってmysqlと叩くつもりが手癖で何回もpsqlと叩いてる始末だったので、もう来年は`alias psql=mysql`するか。

あと、今回ややこしかったのはベンチマーカーはPowerDNSで名前解決してnginxにアクセスが来る作りだったが、DNSのレコードの向き先を間違ったり、無理やりソースコード上でDBの接続先を変えようとして失敗したり、使わないサーバのAPのサービスを落としたら引きずられて依存関係にあるMySQLが落ちたりして、終了3分前のベンチでfailしてスコア0を叩き出した。

終わった...

でも諦めずに対処して残り1分前にベンチを実行。
何とかベンチの順番回ってきてベンチが開始されたのは見たが、あえなくポータルは閉じて結果が見れなかった。

本当に終わった...

# 結果
後日、全チームの順位を確認したら最後のベンチが実は成功していたようで何とか13,962が最終スコアとして残った。
処理時間は18:00:30なので本当にギリギリ。
最高スコアが17,794だったので落ちてしまったが、スコア0だと思っていたのでまあ良かった。
来年も同じメンバーで出場すると思うので、次回はもうちょっと頑張りたい。
ElixirDesktop CRUD画面作成とDBマイグレーションについて

# はじめに
この記事は Elixirアドベントカレンダーのシリーズ４の7日目の記事です

4-6日目の記事でまっさらなPhoenix ProjectをElixirDesktopで動くようになりました
本記事は実際に画面の作成とDBマイグレーションについて解説します

# 開発時のErlangのバージョンについて
開発環境ですが、デスクトップアプリ、iOS、Androidで行うことができますがコンパイルするErlangのバージョンを指定する必要があります

iOS,Android 
-> Erlangをスマホ上で動かすスタティックライブラリのバージョンが 25.0.4のため
`Erlang 25.0.4`を使用すること
デスクトップアプリ 
-> 25.0.4はインストール時にアプリとして起動するためのWxWidgetのパスを解決してくれないため 、それが対策された
`Erlang 26.1.2`を使用すること


# 開発環境
ネイティブ機能を使わない限りはデスクトップアプリがお手軽ですし、スマホの開発環境を整えなくても開発ができます
以下のコマンドで表示できます

```
iex -S mix
```

スマホのシミュレーターで実際試しながら開発する場合、ビルドスクリプトでprod環境でコンパイルされていてホットリロードが効きかないため、

デスクトップアプリ起動したPhoenixサーバーを各OSのWebviewをから表示する設定を行います

```diff_elixir:lib/bookshelf.ex
    {:ok, _} =
      Supervisor.start_child(sup, {
        Desktop.Window,
        [
          app: @app,
          id: BookshelfWindow,
          title: "bookshelf",
          size: {400, 800},
-          url: "http://localhost:#{port}"
+          url: "http://localhost:#{4000}"
        ]
      })

```
この設定を行って以下のコマンドでデスクトップアプリとPhoenixサーバーを起動し、再度ビルドを行ってください

```
iex -S mix
````

本記事ではデスクトップアプリで開発を進めます

# CRUD作成

それではいつものコマンドでCRUD画面を作ります

```
mix phx.gen.live Shelves Shelf shelves name:string
```

今回は認証はしないのでそのまま貼り付けます

```lib/bookshelf_web/router.ex
defmodule BookshelfWeb.Router do
  ...
  scope "/", BookshelfWeb do
    pipe_through :browser

    get "/", PageController, :home
    
    live "/shelves", ShelfLive.Index, :index
    live "/shelves/new", ShelfLive.Index, :new
    live "/shelves/:id/edit", ShelfLive.Index, :edit

    live "/shelves/:id", ShelfLive.Show, :show
    live "/shelves/:id/show/edit", ShelfLive.Show, :edit
  end
  ...
end
```

# Migration周り

ElixirDesktopでは端末のSqliteファイルに対して`mix ecto.migrate`はできないので起動時にmigrationをするようにします

`priv/repo/migrations`フォルダを`lib/bookshelf/`にコピーします

コピー後は、マイグレーションのexsファイルのモジュールはPhoenix起動時に実行できないので拡張子を、exsからexに変更します


repo.exのinitialize関数に実行するマイグレーションのモジュール名を追加します
`Ecto.Migrator.up`はマイグレーションを実行する関数で
第1引数はRepoモジュール
第2引数はマイグレーション番号、今回はファイル名のタイムスタンプ
第3引数は先ほどコピーしたマイグレーションファイルのモジュール名
をそれぞれ指定します
```lib/bookshelf/repo.ex
defmodule Bookshelf.Repo do
  ...
  def migration() do
    Ecto.Migrator.up(Bookshelf.Repo, 20_231_207_144_335, Bookshelf.Repo.Migrations.CreateShelves)
  end
end
```

# 起動時に開くページを指定

現在だと起動時にトップページが開かれて、別のページに行けないのでので棚一覧が開くようにします

```diff_elixir:lib/bookshelf.ex
defmodule BookShelf do
  ...

  @app Mix.Project.config()[:app]
  def start(:normal, []) do
    ...
    {:ok, _} =
      Supervisor.start_child(sup, {
        Desktop.Window,
        [
          app: @app,
          id: BookshelfWindow,
          title: "Bookshelf",
          size: {400, 800},
-          url: "http://localhost:#{port}"
+          url: "http://localhost:#{port}/shelves"
        ]
      })
  end
end
```

CRUD画面が表示されデータを追加できました

![スクリーンショット 2023-12-08 0.41.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/eccb4892-b691-1207-040c-9a3c783415fc.png)

# 最後に
本記事では、開発時の注意点、CRUD画面、DBマイグレーションにつて解説しました
ですが、このままだとブラウザのネイティブ機能でWebViewでは使えない削除時のComfirmダイアログが、表示されないため独自に実装を行います

本記事は以上になりますありがとうございました
event.preventDefault();について

:::note
`event.preventDefault();` はJavaScriptで使われるメソッドで、イベントのデフォルトの動作をキャンセルするために使用される。
これは特に、HTMLフォームの送信、リンクのクリック、キーボードやマウスイベントなど、ブラウザが特定のイベントに対して持つデフォルトの動作を防ぐ場合に役立つ。

例えば、HTMLフォームで「Submit」ボタンを押すと、デフォルトではブラウザはそのフォームデータをサーバーに送信しようとする。
しかし、JavaScriptでevent.preventDefault();を使うことで、この送信動作をキャンセルし、代わりにJavaScriptでカスタムの処理を行うことができる。
:::


```javascript
document.getElementById("myForm").addEventListener("submit", function(event){
    event.preventDefault();
    // ここにカスタムの処理を記述
});
```

上記のコードでは、IDがmyFormのフォームに対してsubmitイベントが発生したときに、デフォルトのフォーム送信動作をキャンセルし、代わりにカスタムの処理を行うようにしている。
これにより、フォームのデータを非同期的に処理するAjax呼び出しなど、さまざまなカスタム動作を実装することができる。
【Rails】開発中にターミナル上へのログ出力が煩わしかった際のメモ

とある案件開発の際、ローカル開発環境にてpメソッドなどを利用しつつ処理の大まかな流れを軽く追いたいが、
処理の性質上ターミナル上に大量のログが出力されてすぐに埋もれてしまう...という状況に遭遇した際のメモになります。

## 解決法
ログレベルを一時的に変更し、(確認時の自分にとって)不要なログをターミナル上に表示させないようにすることで今回の確認をスムーズに進めることが出来ました
```ruby:config/environment/development.rb
Rails.application.configure do
  config.log_level = :error
end
```
developmentモードでの現状のログレベルを確認したところ、特に設定がされていませんでした。
設定がない場合ログレベルは:debugとして動作する＝全て出力されるようです。  

このログレベルを一時的に当案件のproductionモード時と同様の:errorに設定してみたところ、期待通りに例外発生時などを除いてターミナル上にログが出力されなくなりました😄

さらに、pやputsメソッドはあくまでもRubyの基本的なメソッドであり、
Railsのログシステムとは無関係に動作するために、
ログレベルなどの影響も受けず直接標準出力に書き込まれるという性質があるそうです。

これらの設定や性質によって不要なログを非表示にしつつpやputsメソッドによる出力のみをターミナル上から確認することが出来るようになり、確認がとても容易となりました😄

## 参考にさせていただいた記事
https://qiita.com/sobameshi0901/items/b963e7046e2ae8b8e813
XcodeのDebug Memory Graphを使用して参照関係を理解しよう！~クロージャのキャプチャ~

# Debug Memory Graphとは
Xcodeでは、参照関係を視覚的に確認することができ、Xcodeの Debug Memory Graph をクリックするとインスタンス間の参照関係を見ることができます。
### Debug Memory Graphの表示方法
①　command + R　で、Xcodeの実行を行う！
②下記の画像の赤で囲んだボタンを押すと、Debug Memory Graphを表示することができる！
下記の画像のViewControllerの右上の...のボタンを押すと、参照関係の表示を指定できます！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/c5eced2d-cf67-5ceb-69b3-bbcc0abf3abb.png" width=700>

# クロージャのキャプチャを理解する！
### クロージャのキャプチャとは
通常ローカルスコープで定義された変数や定数は、ローカルスコープ内でしか使用できない。
しかし、ローカルスコープ外の変数や定数であっても、クロージャ実行時に参照することができる。
これは、クロージャが自身が定義されたスコープの変数や定数への参照を保持することによって行われます！
この保持のことを「キャプチャ」という！

```swift:ViewController.swift

import UIKit

class C {}
class D {}

class ViewController: UIViewController {
    var closure: () -> Void = {}

    override func viewDidLoad() {
        super.viewDidLoad()
        let c = C()
        let d = D()
        closure = { print(c,d) }
        closure()
    }
}
```
上記のコードをDebug Memory Graphで見てみると、下の画像のような参照関係になる！
クロージャが自身が定義されたスコープの変数や定数への参照を保持している！
この場合の自身が定義されたスコープとはviewDidLoad()のスコープを指している！
そのため、クロージャーがCインスタンスとDインスタンスをキャプチャすることができる！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/5e43ce20-5492-f13a-e899-91414024e3de.png" width=700>

# インスタンス間の参照関係を見てみよう！
```swift:ViewController.swift
import UIKit

class C {}
class D {}

class ViewController: UIViewController {
    var closure1: () -> Void = {}
    var closure2: () -> Void = {}

    override func viewDidLoad() {
        super.viewDidLoad()

        let c = C()
        let d = D()
        closure1 = { print(c,d) }
        closure2 = { print(c,d) }
        closure1()
        closure2()
    }
}
```
下記の画像のDebug Memory GraphのCをクリックすると同じ色になります！
これは同じCインスタンスを参照しているということです！
このようにXcodeのDebug Memory Graphを使用することで、参照関係を視覚的に確認することができます！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/c3034c3b-a075-c35b-b0fd-bb7abd27e16e.png" width=700>

# 参考文献
https://swift.codelly.dev/guide/%E3%83%A1%E3%83%A2%E3%83%AA%E7%AE%A1%E7%90%86/#%E5%BE%AA%E7%92%B0%E5%8F%82%E7%85%A7%E3%81%AE%E8%A7%A3%E6%B1%BA

https://qiita.com/shinya4848/items/e472b217eb3d2bf1be11
【python】isなのに==じゃないもの、な～んだ？

この記事は[KCS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/kcs)の8日目の記事です。
[←7日目](https://lemolatoon.hatenablog.com/entry/2023/12/07/172042) [9日目→](https://besshy8.hatenablog.com/entry/2023/12/06/164647)
# pythonの`is`と`==`の違い
pythonには"同じであること"を判断するような比較演算子が二つ存在します。一つは`==`、もう一つは`is`です。
`==`は各クラスに設定されている、`__eq__`メソッドの結果を返します。基本的には値が等価であるかどうかという意味に使われ、そのようなメソッドが設定されています。
一方で、`is`はオブジェクトの`id`を取得し、それが一致しているかを判定します。つまり、オブジェクトが同一であるかどうかを判定します。この挙動は変更ができません。
pythonのすべてのオブジェクトは"識別子"であるidを持っています。組み込み関数の`id()`を使うことで確認することができます。
# `==`がTrueなのに、`is`がFalseである例
これはめっちゃあります。
例えば、以下のものがあげられます。
```python
a = [1,2,3]
b = [1,2,3]
c = 100000
d = sum(1 for i in range(100000))
e = "hello"
f = "".join(["h","e","l","l","o"])
print(a == b,a is b)
print(c == d,c is d)
print(e == f,e is f)
```
上記のコードのprintの出力はすべて`True False`となります。(CPython3.11.4で検証)
`==`だとしても、オブジェクトが異なる場合は当然`is`は**False**となります。
なお、`CPython`では上記の結果となりますが、別のpython処理系では異なる結果が返ることがあります。(例えば`PyPy`では、`c is d`は**True**になります。) 
# `is`がTrueなのに、`==`がFalseである例
そもそも、pythonのドキュメントにこのような記述があります。
>等価比較は反射的でなければなりません。 つまり、同一のオブジェクトは等しくなければなりません:
>x is y ならば x == y

同じオブジェクトであるのに`==`がFalseを返すというのは、結構変な話ですよね。
当然、以下のようにクラスを書いてあげれば、これが実現できます。
```python
class hoge():
    def __eq__(self,other):
        return False
a = hoge()
print(a == a,a is a)
```
結果は`False True`となりました。実際にこのような挙動をするクラスはあるのでしょうか？
```python
import decimal
a = float('NaN')
b = decimal.Decimal('NaN')
print(a == a,a is a)
print(b == b,b is b)
```
上のコードを実行してみると、結果が`False True`になります。
Pythonのドキュメントを読んでみると、以下のような記述があります。
>非数値である float('NaN') と decimal.Decimal('NaN') は特別です。 数と非数値との任意の順序比較は偽です。 直観に反する帰結として、非数値は自分自身と等価ではないことになります。 例えば x = float('NaN') ならば、 3 < x, x < 3, x == x は全て偽で、x != x は真です。 この振る舞いは IEEE 754 に従ったものです。

よって、x == xが偽という珍しいことが起こったのですね。

# おまけ
こんなリストを用意してみます。
```python
a = [float('NaN')]
```
当然、`a is a`は**True**ですよね。では、`a == a`はどうなるでしょうか。
```python
print(a == a) # True
```
予想した結果と一致しましたか？

# 参考・引用文献
[python言語リファレンス 6. 式 (expression) 6.10.1. 値の比較](https://docs.python.org/ja/3/reference/expressions.html#value-comparisons)
照合順序（Collation）の役割と選択

アドベントカレンダー8日目になりました。

業務でMySQL 5.7とMySQL 8系を触る中でutf8mb4文字コードのデフォルトの照合順序で詰まることがありました。この記事では、照合順序について書きたいと思います。

# 照合順序とは
照合順序（Collation）は、データベース内のテキストデータ（文字列）を比較する際のルールや規則のセットのことです。照合順序は、文字列のソート（並べ替え）や文字列同士の比較に影響を与えます。異なる言語や文字セット、地域で使用されるテキストデータに対して、適切な比較やソートを行うために必要です。

# 照合順序の要素
## 1. 大文字と小文字の区別
照合順序によって、文字列比較時に大文字と小文字を区別するか、区別しないかが決まります。ケースインセンシティブ（大文字小文字を区別しない）やケースセンシティブ（大文字小文字を区別する）などがあります。

## 2. アクセント記号や発音の違い
一部の照合順序では、アクセント記号や発音の違いを無視したり、正確に比較したりする方法が異なります。例えば、"é"と"e"を同じとみなすかどうかが照合順序によって異なります。

## 3. 文字の順序
照合順序は、文字のソート順序を定義します。異なる言語や地域では、文字の順序が異なることがあります。例えば、英語とフランス語ではアルファベットの順序が異なります。

## 4. 文字セット
照合順序は、使用する文字セット（character set）に依存します。UTF-8やLatin-1など、さまざまな文字セットが存在し、それぞれに対応した照合順序が提供されます。

# 照合順序の選択
データベースアプリケーションを開発する際、正確なテキストデータの比較と検索が重要です。照合順序は、データベース内の文字列をソートする方法や比較する方法を制御します。したがって、照合順序の選択はアプリケーションの要件に依存します。特定の言語や地域、テキストデータの特性に合わせて最適な照合順序を検討し、適切な比較と検索を確保する必要があります。

# MySQL 8のutf8mb4デフォルト照合順序の違い
例えばMySQL 8では、utf8mb4デフォルトの照合順序は`utf8mb4_0900_ai_ci`です。

`ci`は"Case Insensitive"（大文字小文字を区別しない）を意味しています。この照合順序では、大文字と小文字を区別せずに文字列を比較します。つまり、"A"と"a"は同じとみなされます。

`ai`は"Accent Insensitive"（アクセントを無視する）を意味しています。これにより、アクセント記号や発音の違いを無視して文字列を比較します。例えば、"café"と"cafe"は同じとみなされます。

`utf8mb4_0900_ai_ci`は、Unicode規格のバージョン9.0.0に基づいています。新しい文字や絵文字に対するサポートが向上しており、より広範な文字セットを正確に比較できるようになっています。

# おわりに
データベースシステムは、細かい設定を通じて多言語対応や正確なデータ操作を実現していることを感じました。
dialog要素をEscキーでcloseした際にoverflow:hiddenを解除したい

# この記事で分かること
-  dialog要素でEscキーでcloseした際にoverflow:hiddenを解除する方法
-  dialog要素を使う利点、懸念点

# 背景
ダイアログをdialogタグで記述した際にescキーで閉じることができます(ブラウザーによって提供)。
しかし、overflow:hiddenを解除できなかったのでモーダルが消えてもスクロールができません。
そこでどうしたら良いか考えてみました。

補足）showModal() メソッドによって呼び出された```<dialog>```は、Escキーによって閉じることができます。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/dialog

# 実装
```ts:モーダルを開く関数
openModal(){
    document.body.style.overflow = 'hidden';
    const modalElm = this.getModalElm();
    if (!(modalElm instanceof HTMLDialogElement)) return;
    modalElm.showModal();
}
```

以下の実装を当初は考えてました。
しかし、これだとキーを打つたびに毎回この処理が走るので良くないですね。

```ts:Escキーを押した際にバックドロップの固定を解除する関数
registerCanselModalEvent() {
        document.addEventListener('keydown', function(event) {
            if (event.key === 'Escape') {
                document.body.toggleAttribute('style');
            }
        });
};
```

このブログでcancelイベントというのを知りました。
[dialog要素を使う今後のモーダル実装](https://zenn.dev/ankoromochitaro/articles/fc348b8829971f)

[HTMLElement: cancel event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/cancel_event)
>The cancel event is fired by <input> and <dialog> elements. The event is fired when the user cancels the currently open dialog by closing it with the Esc key

mdnによると
```dialogを開いている際にEscキーを押したらcancelイベントが発生する```と記載があります。

こちらを採用したいと思います！
```ts:cancelイベントが起きた際にバックドロップの固定を解除する関数
registerCancelModalEvent() {
  const dialog = document.querySelector('dialog');
  if (dialog) {
    dialog.addEventListener("cancel", (event: Event) => {
      if (event.currentTarget === dialog) {
        document.body.toggleAttribute('style');
      }
    });
  }
}
```

inputタグをEscで閉じた際もcanselイベントが発生するので、`Event.currentTarget`を使っています。

https://developer.mozilla.org/ja/docs/Web/API/Event/currentTarget

例）[ファイルピッカーダイアログ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file)

# 補足
紹介した方もブログで書かれてましたが、以下の点でdialogタグは利点があります。
### 直感的な記述かつ実装が簡潔
今まで```<div role="dialog">```で記述して実装していましたがコード量が減ります。

```modal.showModal();```
- モーダルを表示します。
- CSS の ::backdrop 擬似要素を使用して、 <dialog> 要素が HTMLDialogElement.showModal() で表示されたときの背後のスタイルを設定することができます
例）ダイアログの背後にある操作できないコンテンツを暗くする

```modal.close();```
- モーダルを閉じます。
- backdropが消えます。

###  デフォルトでEscキーで閉じることができる
###  cancelイベントを扱って実装ができる

# 懸念点
### IEでは使えないです。

https://caniuse.com/?search=HTMLDialogElement

![image.png>](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/bfd28ee4-0ea1-2503-0017-60dcc5f788c1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405182/2cd3421e-ce21-6014-dead-23be3534e529.png)

### デフォルトだとモーダルより下はスクロールしてしまいます。
今回のような実装はしないといけないですね。
- modal.close()
- Escキー押下時

# 参考
- [dialog要素を使う今後のモーダル実装](https://zenn.dev/ankoromochitaro/articles/fc348b8829971f)
- [jQueryを使ってHTMLをオーバーレイ表示する方法を現役エンジニアが解説【初心者向け】](https://magazine.techacademy.jp/magazine/24568)
Google Meetに参加/退出するとSlackに通知するBotの作り方(プレビュー版)

Slackのハドルミーティングでは参加/退出の際にチャンネル内に表示されるため一目でわかりますが、Google Meetを使う際もSlackに参加/退出の通知が来ると便利ですよね。
今回はプレビュー版のGoogle Meet APIを使用してMeetの入退出イベントを取得し、Slackに通知を飛ばす方法を解説します。
通知部分のコードを変えればSlack以外にDiscordなどでも通知可能です。

https://developers.google.com/meet/api/guides/overview

# 前提条件
- Google Workspace を利用している人
(個人で使用している人は対象外です。)
- [Google Workspace Developer Preview Program](https://developers.google.com/workspace/preview) に申請し、承認されている人
以下のリンクの1番下に申請フォームがあります。承認には数日かかります。
https://developers.google.com/workspace/preview
- プレビュー版APIのため動作が不安定なことに同意できる人
- 社内のみでの使用

その他条件については [Developer Preview Program Terms](https://developers.google.com/workspace/preview#developer_preview_program_terms) 参照。

# 注意
2023/12/21に仕様変更があり、Space Nameが今までは `spaces/3eaf892f-2628-445d-ba90-918e9f53e077` 形式だったのが、`spaces/abc-mnop-xyz` 形式に変わるようです。
それに伴い、12/21以前から使用している場合は、環境変数 `SPACE_NAME` の変更およびサブスクリプションへの再登録が必要です。

# 用語について

- Space: ミーティングID (例: `abc-mnop-xyz`) 単位。
- Conference: Space内に誰かが参加すると自動的に作成され、参加者が0人になると自動的に終了する。

# コード
https://github.com/shge/meet-notify

# GCP周りの設定

## Pythonライブラリのダウンロード
承認されたユーザーにのみPythonクライアントライブラリは公開されているため、pip等でそのままはインストールできません。
上記の[申請](https://developers.google.com/workspace/preview)が承認されると、以下の `Download the client library` リンクから飛べるGoogleドライブにアクセスが付与されているはずなので、ダウンロードします。

https://developers.google.com/meet/api/guides/tutorial-events-python#install_the_client_library

## OAuth設定
OAuth同意画面を `https://www.googleapis.com/auth/meetings.space.created` スコープで作成します。
https://console.cloud.google.com/apis/credentials/consent

また、クライアントIDを作成します。
https://console.cloud.google.com/apis/credentials

## gcloud CLI のインストール
以下のような記事を参考に `gcloud` コマンドを使用可能な状態にしてください。
https://qiita.com/G-awa/items/e6904b040caa0096fba0

```sh
gcloud auth
gcloud auth application-default login
```

## Artifact Registry にアップロード

以下を参考にして `twine` でアップロードします。
https://cloud.google.com/artifact-registry/docs/python/store-python

なお、ローカルで動かすだけであればpipでそのままローカルからパッケージをインストールしてOKです。

## 

# 設定
```sh
mv .env.example .env
mv .env.yaml.example .env.yaml
```

1. `.env`, `.env.yaml` ファイルに環境変数を入力
2. `requirements.txt`の1行目にArtifact Registryのリポジトリ名を入力

```sh
pip install -r requirements.txt
```

# 実行
ローカルでの実行
```sh
python main.py
```

Cloud Functionsへのデプロイ
```sh
gcloud functions deploy your-function-name \
--gen2 \
--runtime=python312 \
--region=asia-northeast1 \
--source=. \
--entry-point=subscribe \
--trigger-topic=your-topic-name \
--env-vars-file .env.yaml
```

# サブスクリプションの更新について
`Expire time must be between 0 and 7 days from now` とのことで、サブスクリプションは1週間ごとに更新が必要です。

https://developers.google.com/workspace/events/guides/update-subscription?hl=ja

https://developers.google.com/workspace/events/reference/rest/v1beta/subscriptions/patch?hl=ja

# 参考
設定の詳細や最新情報はこちらを参照してください。

https://developers.google.com/meet/api/guides/tutorial-events-python
写真を残そう、時々思い出してもいいし

## 画像をドライブにアップロード

現在 二児の父ではあるが
上が高校、下が中学とそれなりに大きくなってしまい
特にかわいげもなく、昔は素直でノリがよかったのになあと懐かしんだり
なんとなく自分の思春期の頃の様に中二病っぽくなったり、突然悟りを開いたように冷めた発言したりと
成長を楽しんでいる毎日ですが


時々スマホのGoogleフォトを開いたときに出てくる
あれから○○年って画像にほっこりしています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2617007/2dd1ddac-aed5-f836-efab-7e2b3e34266a.png)



それを見るたび もっと写真撮っておけばなあなんて少し後悔しています
時間の流れは取り返せませんが あの時あればなーって思いから
LINEで画像をGoogleDriveに簡単アップロード作りました
簡単なシナリオなので よろしければご参考にしてください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2617007/a2a64624-2254-ff5f-6ac8-b72d321c71bb.png)

Routerでmessageが画像かテキストかを判別
画像ならGoogleDriveにアップロードし完了通知
テキストならGPTさんと粋な会話
の流れになります

スプリッターをつかって画像はDriveにアップロードして
コメントはスプレッドシートに保存もアリかも

グループLINEからも共有のDriveにアップロードされるので
夫婦でまとめて画像をフォルダに集められます

## ノーコードでつくるこんな事ありました通知

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2617007/d7b30b58-be62-18c6-5712-f6ff5df8ce0d.png)

こちらは 毎日 今日の日付を取得して、GoogleDriveから一年前の画像を取得してmessageをくれるシナリオ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2617007/672f9a1b-12f5-e1e0-c511-11ac5738355c.png)

先ほどのシナリオで保存するときにファイル名をタイムスタンプからとってきているので
日付でダウンロードするファイルを選んでいます

カレンダー関数なんかを使うともう少し工夫できる感じです


## まだ小さいお子さんがいる皆様へ
ホント写真は腐るほどとっておいた方が良いです、かしこまったのじゃなくて普段の日常的な奴もいっぱい残した方がいいです
親戚の集まりの時なんかに見せると時間が凄くつぶせますし
時々夫婦の写真や自分の写真も残してください、

あと、まとめてDriveなどに保存しておいた方が
後々見返すときに楽です



時々ひょんなことからパソコンのピクチャフォルダの奥から
ほっこり笑える画像が出てくるのも楽しいですけどね

---
写真にまつわる、どうでもいい話
実はこのシナリオ、定年退職してパン屋を開いた叔父が
毎日お店の店頭からの景色を写真に撮っていたんで
それをDriveにアップロードしたり、通知がきたら面白いんじゃないかと考えて作ったシナリオを作り直しました
なんだかんだで写真は一年くらいであきたらしいし、パン屋に飽きたのでたたんで次の商売を始めるらしいっす

あとウチの親父は写真家でもなんでもなく、ただの公務員だったんですが
母が死んだときに自分が母と知り合った時からの写真あつめて
小さい画廊借りて展覧会を開いていました、へたな葬式よりも、皆が思い出に浸ってくれました 
家族の思い出はきっと宝物です、仲がいい家族ばかりじゃないかもしれんし、しらんけど

参考にした記事

https://qiita.com/okinakamasayoshi/items/f98bc993caf3cefdcfa9

https://qiita.com/sawayamakouji/items/68ec2c42e6471f982cc5
【Rust】最近、「error: failed to run custom build command for `openssl v0.9.x`」って言われるようになった

新しい環境で Rust の `openssl` を使うやつをビルドしようとしたところ、表題のようなエラーが出て失敗します。

https://zenn.dev/dozo/articles/ed2e846847af5d

https://qiita.com/nacika_ins/items/465e89a7b3fbeb373605

こんな感じで、`build-essential` とかを入れろと言われますが、エラーは消えません。何が起きているのでしょうか？

# クレート `openssl` のバージョンが古い
https://github.com/sfackler/rust-openssl/blob/0.9.x/openssl/build.rs#L16

によると、0.9.x のバージョンだと `OpenSSL` の `1.1.0` くらいまでしか対応していないようです。
このとき、自分の環境は `3.0.10` でした。これでは match に引っ掛からないので `openssl` がビルドできないよーと言われてしまうわけです。

# そもそも `OpenSSL` 1 系は EOL
https://endoflife.date/openssl

によれば、1 系のサポートは 2023 年 9 月に終わっており、サポートされているのは 3 系のみとなっています。

# クレート `openssl` のアプデをしよう！
Ubuntu 18.04 とかで `1.1.0` の `OpenSSL` を使えばとりあえずビルドはできますが、それよりはクレートのアップデートをした方がよいでしょう。

よくあるのは `reqwest` のバージョンが古いために依存の `openssl` のバージョンも古いとかです。

# おわり
最近、WSL 環境を吹き飛ばして再構築したら急にこのエラーが出るようになったので辛い思いをしました。
【備忘録】numpyでcos類似度を実装する

# はじめに
この記事は完全に自分用の備忘録となります。
記事の本文ではnumpyを使ってcos類似度を計算する関数を３つほど紹介しています。
主にノルムがゼロの場合の対処方法が異なるものを紹介しています。

# 本文
## cos類似度とは
cos類似度とはベクトル間の類似度を求める方法で以下のような式で定義されます。
この値が1に近いほどベクトルが類似しており、-1に近いほどベクトルが異なることを意味します。

```math
\
\text{cosine_similarity}(A, B) = \frac{A \cdot B}{\|A\| \|B\|}
\

```
## 実装
ここで扱う問題点はベクトルのノルムがゼロとなるケースです。筆者は文書分類を行う際に、空の文書を扱う際にノルムがゼロになるケースがありました。
ここで実装するのは以下の３パターンです。

- ノルムがゼロの場合の対策をしない
- ノルムがゼロの場合にも値が得られるようにする
- エラーを返して終了する

### 対策なし
```py
def cos_sim1(v1: np.array, v2: np.array):
    sim = np.dot(v1, v2)/(np.linalg.norm(v1) * np.linalg.norm(v2))
    return sim

```
normがゼロの時はnanが帰ってきます。
単一の値だけ欲しい時は許容できるかもしれませんが、類似度順で並べ替える際などは扱いにくいでしょう。
`nan`はどの値との比較に対してもFalseを返す値なので、比較の際は注意です。

### 小さな値を挿入する
```py
def cos_sim2(v1: np.array, v2: np.array, eps=1e-8) -> float:
    v1_norm = np.linalg.norm(v1)
    v2_norm = np.linalg.norm(v2)
    v1_v2_dot = np.dot(v1, v2)
    sim = v1_v2_dot/(v1_norm * v2_norm + eps)
    return sim
```
こちらの書方は『ゼロから作るDeepLearning』に掲載されていたものです。
epsという小さな値を分母に足すことで
- ノルムがゼロでない場合→丸め誤差により吸収
- ノルムがゼロの場合→epsによりゼロ割算を防ぐ
という挙動になります。

ただ、ベクトルがゼロベクトルである場合が「データが存在しないから０」とかだと類似度が0と返されるのは嬉しくないですね。また、norm 0のベクトル同士での計算も類似度が１となります。ここでは１を返して欲しい気持ちます。

### 例外を返す
私は基本この関数を利用しています。ゼロ除算が発生する際にZeroDivisionErrorを返すようにすることで、対策を行う実装です。

```py
def cos_sim3(v1: np.array, v2: np.array) -> float:
    v1_norm = np.linalg.norm(v1)
    v2_norm = np.linalg.norm(v2)
    if v1_norm == 0 or v2_norm == 0:
        raise ZeroDivisionError("one of the norms is 0")
    v1_v2_dot = np.dot(v1, v2)
    sim = v1_v2_dot/(v1_norm * v2_norm)
    return sim
```

# 最後に
他にも様々な対策方法が考えられると思うので、この例はあくまでも１例としてお考えください。
より良い実装が思いついた方はコメント等で教えてくれると嬉しいです😀
SQLにおける集約関数でのnullの扱い

# 1. はじめに
Oracle Bronze Silverに向けた試験勉強として、集約関数でのnullの扱いに関して理解を深めようと思い、記事を書きました。

# 2. 集計関数でのNull
集計ファンクションの引数に指定した列にNULLが含まれる場合、NULLは集計対象外となります。
例として、次のようなデータベースの「scores」テーブルを考えてみましょう。

```sql
CREATE TABLE scores (
  id INT,
  student_name VARCHAR(50),
  score INT
);

INSERT INTO scores VALUES
(1, 'Alice', 80),
(2, 'Bob', NULL),
(3, 'Charlie', 90),
(4, 'David', 85),
(5, 'Eva', NULL);

```

このテーブルに対して、AVG関数を使用すると以下のようになります。

```sql
SELECT AVG(score) AS average_score FROM scores;

average_score
--------------
  85
```

# 3. COUNT関数とNULL
集計ファンクションCOUNTでもNULLは集計対象外となります。ただし例外として、引数に「＊」を指定した場合は、件数のカウント対象となります。
例として、次のようなデータベースの「users」テーブルを考えてみましょう。

```sql
CREATE TABLE users (
  id INT,
  name VARCHAR(50),
  age INT
);

INSERT INTO users VALUES
(1, 'Alice', 25),
(2, 'Bob', NULL),
(3, 'Charlie', 30),
(4, 'David', NULL),
(5, 'Eva', 28);

```
1. NULLを含まないカウント（列名ageを指定）
```sql
SELECT COUNT(age) AS count_not_null_age FROM users;

count_not_null_age
------------------
3

```

1. NULLを含むすべての行のカウント（引数に「*」を指定）
```sql
SELECT COUNT(*) AS count_all_rows FROM users;

count_all_rows
--------------
5

```

# 4. おわりに
この記事を通じて、集約関数におけるNULLの挙動について整理できました。