OmniAICreator/Qiita-1.07M huggingface dataset:
タイミーのBIグループにおけるオンボーディング期間の振り返り

※こちらは[Timee Advent Calendar 2023](https://qiita.com/advent-calendar/2023/timee)の12月22日分の記事です。


データ統括部 BIグループに所属している井上です。

現在データアナリストとして、主に事業戦略・セールス領域のデータ分析に携わっています。

…といいつつも、私はまだ入社したばかりの新参者で、最近ビジネス領域を担当するチームに配属されたばかりです。

BIグループでは、入社後に一定のオンボーディング期間を経て各ユニットへ配属される仕組みがあり、私もその流れに従っていました。

本記事ではタイミーへ入社後にどのようなオンボーディング期間を過ごしたのか、について記載します。

### **オンボーディングの全体感**

BIグループにおけるオンボーディング期間は、大きく3つのフェーズに分けられます。

(明確にフェーズが設定されているわけではなく、あくまでも主観として)

1. 業務のために必要な知識を得る
2. 実際に手を動かしながら業務に慣れる
3. 自分が手を出したい領域に入り込む

また、メンターとなる社員が付いてくれるので、わからないことは相談しながら進めることができます。

以下、各フェーズ毎に振り返っていきます。

### 1. 業務のために必要な知識を得る

組織の理解、データ構造の把握、各種ツールの権限取得など、インプットが多い段階でした。

(正直あまり書けることがないフェーズですので詳細は割愛します。)

### 2. 実際に手を動かしながら業務に慣れる

知識のインプットが終わった後は、実際に業務の中でデータ抽出や分析を行うフェーズに移行しました。

BIグループでは、各メンバーが担当するプロジェクトとは別に、事業部からのデータ業務依頼を輪番で対応する仕組みがあり、オンボーディング期間は輪番制を無視して手を挙げることができるルールでした。

そのため、ほぼ毎日手を挙げて新しい依頼を担当させてもらうようにしていました。様々な職種の人からの依頼対応を通じてデータ理解やドメイン理解を深めることができたので、非常に有意義な期間になりました。

わからないところはメンターや他のメンバーに聞くことができる (皆さん嫌な顔をせず対応してくれた) ので、申し訳ない気持ちもありながら、見たことのない業務でも｢とりあえずやります！わかんなかったら助けてください！｣と言いやすい環境だったなと思います。

また別軸ではありますが、同じタイミングで自己紹介も兼ねてBIグループメンバー全員と1on1をしていました。一緒に働くメンバーがどういった人で、どういったプロジェクトに関わっているのかを理解することができて、これも良かったと思います。

(趣味の話だけで終わってしまうこともありましたが、それもある意味で人となりを知ることができたのでOK。笑)

### 3. 自分が手を出したい領域に入り込む

2のフェーズで業務を進めつつ、｢自分がどんな領域でどんな仕事をするのか？｣ということを考えていました。

決め方は人によっても異なる部分かとは思いますが、どういう領域に関わるか？について上長とすり合わせをした上でプロジェクトに入っていくようなイメージかと思います。

先述した依頼対応や1on1を通じて得た知識から、自分が関わりたい・関わるべきだと思うプロジェクトの会議やチームの定例MTGに呼んでもらい、内容をキャッチアップしつつ、チャンスがあれば手を挙げて仕事をもらうような動き方でした。

既存のメンバーからすれば｢まだ入って間もない右も左もわかっていない人が突然入ってくる｣状況でしたが、どのメンバーの方も優しく受け入れてくれたので、人の｢やりたい｣という気持ちを尊重してくれる環境なんだなと感じることができました。

現在も、オンボーディング期間中に生まれたプロジェクトに取り組んでいる最中です。

### まとめ

総じてタイミーのバリューである｢やっていき｣を尊重してもらいながら、色々とキャッチアップしつつプロジェクトにも入っていける、充実した(？)オンボーディング期間を過ごせたかなと思います。

とはいえ、オンボーディングには改善の余地もあります。BIグループでは、オンボーディング期間中に感じた課題についてドキュメントに蓄積しつつ、それを元に改善していく試みを実施中です。

個人的には｢新しく入ってきた人に自分たちよりも良いオンボーディング体験を届ける｣という意味でとても良い取り組みだなと思っています。

### **We’re Hiring!**

タイミーのデータ統括部では、私たちとともに働くメンバーを募集しています。

現在募集中のポジションは[こちら](https://hrmos.co/pages/timee/jobs?category=1814216944314011650)です。

[カジュアル面談](https://devenable.timee.co.jp/casual)も行っていますので、興味を持っていただけた方は、ぜひお話しさせてください！
Autonomous Database：指定したディレクトリ内の全ファイルを一撃で削除する方法

# はじめに
Data Pump等を使用した際に、ディレクトリ内に複数のファイルが作成され、ファイルを一つ一つ削除するのは面倒な場合があります。

そんな時に使えそうな、特定のディレクトリ内の全ファイルを一撃で消す方法をご紹介します。

# ディレクトリ内のファイルの一覧を表示
まずは現在のディレクトリDATA_PUMP_DIR内のファイルの一覧を表示してみます。
DBMS_CLOUD.LIST_FILESファンクションを使用します。
```
SQL> SELECT * FROM DBMS_CLOUD.LIST_FILES('DATA_PUMP_DIR');

OBJECT_NAME          BYTES CHECKSUM    CREATED                                LAST_MODIFIED                          
_______________ __________ ___________ ______________________________________ ______________________________________ 
exp02.dmp           274432             22-DEC-23 09.09.54.381150000 AM GMT    22-DEC-23 09.09.55.506786000 AM GMT    
expdp02.dmp          28672             22-DEC-23 09.12.06.853427000 AM GMT    22-DEC-23 09.12.21.623613000 AM GMT    
expdp101.dmp         32768             22-DEC-23 09.14.52.073214000 AM GMT    22-DEC-23 09.15.45.559141000 AM GMT    
expdp104.dmp         32768             22-DEC-23 09.15.05.567620000 AM GMT    22-DEC-23 09.15.45.555805000 AM GMT    
export.log            2039             22-DEC-23 09.09.27.321014000 AM GMT    22-DEC-23 09.15.47.640452000 AM GMT    
exp01.dmp            12288             22-DEC-23 09.09.27.591345000 AM GMT    22-DEC-23 09.09.57.816385000 AM GMT    
exp03.dmp            28672             22-DEC-23 09.09.54.488724000 AM GMT    22-DEC-23 09.09.57.824931000 AM GMT    
exp04.dmp            12288             22-DEC-23 09.09.55.595007000 AM GMT    22-DEC-23 09.09.57.821460000 AM GMT    
expdp01.dmp         274432             22-DEC-23 09.11.48.593315000 AM GMT    22-DEC-23 09.12.08.179151000 AM GMT    
expdp03.dmp          12288             22-DEC-23 09.12.08.010860000 AM GMT    22-DEC-23 09.12.09.211921000 AM GMT    
expdp102.dmp         24576             22-DEC-23 09.15.04.228428000 AM GMT    22-DEC-23 09.15.45.559179000 AM GMT    
expdp103.dmp       1925120             22-DEC-23 09.15.04.606600000 AM GMT    22-DEC-23 09.15.45.561670000 AM GMT    

12 rows selected. 

SQL> 
```
ディレクトリDATA_PUMP_DIR内には12個のファイルがあることが確認できました。

# ディレクトリ内のファイルを一括削除するPL/SQL
こちらのPL/SQLブロックを実行することで、ディレクトリ内の全てのファイルを一括で削除することができます。
```
BEGIN
    FOR rec in (SELECT object_name FROM DBMS_CLOUD.LIST_FILES('ディレクトリ名'))
    LOOP
       DBMS_CLOUD.DELETE_FILE(directory_name => 'ディレクトリ名', file_name => rec.object_name);
    END LOOP;
END;
```
ディレクトリ名にDATA_PUMP_DIRを指定して、早速実行してみます。
```
SQL> BEGIN
  2      FOR rec in (SELECT object_name FROM DBMS_CLOUD.LIST_FILES('DATA_PUMP_DIR'))
  3      LOOP
  4         DBMS_CLOUD.DELETE_FILE(directory_name => 'DATA_PUMP_DIR', file_name => rec.object_name);
  5      END LOOP;
  6  END;
  7* /

PL/SQL procedure successfully completed.

SQL> 
```
# 実行結果の確認
再度、DBMS_CLOUD.LIST_FILESファンクションを使用して、ディレクトリDATA_PUMP_DIRの内容を確認してみます。
```
SQL> SELECT * FROM DBMS_CLOUD.LIST_FILES('DATA_PUMP_DIR');

no rows selected
SQL>
```
ディレクトリDATA_PUMP_DIR内の全てのファイルが削除されていることがわかりました。

なお、FOR文の中のSELECT文に **WHERE object_name LIKE 'exp%'** 等の検索条件を付加することで、特定のファイル名を持つファイルのみを削除することも可能です。
Spring BootでJSON形式のリクエストを送ったらHTTP415errorになった際の原因と対処法

# 事象
SpringBootを使って実装したサーバーにJSON形式のcurlコマンドを投げたところ「HTTP 415エラー(415 Unsupported Media Type)」になった。

# 解決策
コントローラメソッドのリクエストボディの型を`MultiValueMap`から`Map`に変更したところ解消した。
```java:修正後
@PostMapping(value = "/a-resource", consumes = MediaType.APPLICATION_JSON)
public Object example(@RequestBody Map<String, String> request){/*処理*/}
```

# 原因
JSON形式のリクエストを受取った際、Spring内部でJacksonを用いてJSONをパースしているため、コントローラの型が`Map`であるべきのようだ。
コピペ元の引数が`MultiValueMap`だったのは`application/x-www-form-urlencoded`形式だったからであった。<br>
なかなか正解にたどり着けなかったためシェア。
GoでAWS S3を扱う

# aws-sdk-goの紹介
Goでs3を扱う方法を紹介します。
`aws`コマンドは有効化済みの想定です


## インストール
```bash
go get github.com/aws/aws-sdk-go/aws
go get github.com/aws/aws-sdk-go/aws/session
go get github.com/aws/aws-sdk-go/service/s3
```


## プログラム本体
```go
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

func main() {
	sess, err := session.NewSession(&aws.Config{
		Region: aws.String("ap-northeast-1")},
	)
	if err != nil {
		log.Fatal(err)
	}

	svc := s3.New(sess)

	// バケット一覧出力
	result, err := svc.ListBuckets(nil)
	if err != nil {
		log.Fatal(err)
	}
	for _, bucket := range result.Buckets {
		fmt.Printf("%s\n", aws.StringValue(bucket.Name))
	}

	// ファイルアップロード
	file, err := os.Open("file_name")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	_, err = svc.PutObject(&s3.PutObjectInput{
		Bucket: aws.String("bucket_name"),
		Key:    aws.String("file_name"),
		Body:   file,
	})

	if err != nil {
		log.Fatal(err)
	}
}
```
特別複雑なことは無く、他の言語でawsを操作したことがある人なら難なく扱えると思います。
Goでこういうことをしようとするともっと面倒なのかと思っていましたが、案外シンプルに収まったので、今後はこういう作業もGoでやってみようと思いました。
IchigoJamで鬼ごっこプログラム

今日、教室にきていたこどもたちと一緒につくりました！
IchigoJamの二人対戦型鬼ごっこゲームです。（コードはもっときれいにしたい）
友達と一緒にプレイして、ながくにげきろう！

### 遊び方
逃げる人 ： ←↑→↓キーで移動
鬼 ： WSADキーで移動

### コード
```
10 CLS:CLT:X=0:Y=1:A=30:B=20
20 K=INKEY()
22 IF K!=0 LCX,Y:?" "
24 IF K!=0 LCA,B:?" "
30 IF K=30 Y=Y-1
40 IF K=31 Y=Y+1
50 IF K=28 X=X-1
60 IF K=29 X=X+1
70 IF K=87 B=B-1
80 IF K=83 B=B+1
90 IF K=68 A=A+1
95 IF K=65 A=A-1
100 IF X<0 X=0
101 IF X>30 X=30
105 IF Y<1 Y=1
106 IF Y>20 Y=20
110 IF A<0 A=0
111 IF A>30 A=30
115 IF B<1 B=1
116 IF B>20 B=20
200 LCX,Y:?CHR$(250)
210 LCA,B:?CHR$(237)
220 LC0,0:?TICK()
800 IF X=A && Y=B GOTO1000
900 GOTO20
1000 LCA+1,B:?"ﾂｶﾏｴﾀ"
```
Blender で制作した3DモデルをWeb上に描画する

以前から3Dモデリングに関心があり、Three.js や fiber なんかを触っています。

今回は、**自作した3DモデルをWeb上に描画する**
ということをやってみました。

[**Three.js**](https://threejs.org/)
[**fiber**](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction)

### Blender チュートリアル
無料で使えるツール [**Blender**](https://www.blender.org/) を使って
[カサハラCG さん](https://www.youtube.com/@kasaharacg) のチュートリアルに沿って、実際に3Dモデルを作ってみました。
（めちゃくちゃ分かりやすく大変参考になりました）

https://www.youtube.com/watch?v=DC9j-4COXZA

### 完成品
そうして完成したのがこちら↓

![burger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418521/23973d9d-1177-dd52-95b9-6a740d9fae61.png)

動画の通りにやって、結構うまく作れました。
Blender 上でレンダリングしたもののスクショになります。

実際に3Dモデルを自分で作ってみて思ったことは、
モデリング用のツールは操作性がかなり独特で、感覚的に扱えるようになるには慣れが必要そうだということ。
今回のバーガーを作るのに数時間はかかりました。。

3次元を表す xyz の向きが Web（というか一般的な認識）とは違っている、など細かな違いもあります。
Blender では、Y軸が上方向ではなく奥に伸びていきます。
数学で出てくる平面のグラフをパタンと奥に倒したイメージですね。

Three.js コード上で3D空間を扱う場合は、横x 縦y 奥z になります。
（一般的な認識に近いはず）

Blender で作ったオブジェクトは色んな形式で書き出しが可能です。
今回は扱いやすいとされている、GLTF 形式で書き出しました。

### Three.js でロードして表示する
3Dモデルは、fiber の useLoader で読み込み、キャンバス上に描画できます。
gltf の他にも、fbx や obj 形式にも対応しています。

```
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

const Model = () => {
  const gltf = useLoader(GLTFLoader, "./Poimandres.gltf");
  return (
    <>
      <primitive object={gltf.scene} scale={0.4} />
    </>
  );
};
```
```
<Canvas>
  <Model />
</Canvas>
```

早速Webページに表示してみると、、

Blender 上では照明やら何やらで結構盛ることができたが、
Web上にレンダリングしてみると、いまひとつな仕上がりに。

![screencapture.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/418521/caa3efed-c71c-7268-df00-e40e1dcd3a08.gif)

今回の動画はチュートリアル用だったこともあり、表面の素材は細かく指定してなく、質感がプラスティックっぽくなっていました。
マテリアル（表面素材）の設定が重要だと気付かされました。
光の反射を設定するだけでも結構変わってきそう。
上に乗ってるゴマのパーティクルや、チーズの波形も消えてます。
Blender 上のエフェクトは書き出し時にすべて反映されるわけではないみたい。
この辺はもっと触ってみる必要がありそう。

### 所管
Three.js でコードで作り込むより楽にいくかと思ったが、そう簡単にもいかなそうでした。
Web上で見栄え良く表示させるには、結構作り込む必要があるなぁと。

ブラウザゲームとか作ってみたい。。
[Godot 4.2] 3種のMouseFilterの挙動解説

[`Control.mouse_filter`](https://docs.godotengine.org/ja/4.x/classes/class_control.html#class-control-property-mouse-filter) とは、その Control ノードがマウスの入力に対してどう振る舞うのかを決める enum プロパティです。
`MOUSE_FILTER_STOP`、`MOUSE_FILTER_PASS`、`MOUSE_FILTER_IGNORE` の3種がありますが、名前が曖昧でパッと見どういった挙動になるのか分かりづらいです。

とりあえず解説の前に一言で表すと、以下のとおりです。
* `MOUSE_FILTER_STOP`: そのノードでだけ処理して、誰にも渡さない
* `MOUSE_FILTER_PASS`: そのノードで処理しつつ、親にも渡す
* `MOUSE_FILTER_IGNORE`: 処理しない

# 解説の前提知識
画面上で複数の Control が重なっている箇所をクリックした場合、「手前にある」ノードが優先されます。
「手前にある」とは、そのノードがツリーのなかでより下に存在するということです(親子のことではなく、並び順のことです)。

MouseFilter は「手前にある」ノードが入力をどう扱うのか、後ろにあるノードは入力を受け取ることができるのか、というところに関わってきます。
本当は[イベントハンドリングの話](https://docs.godotengine.org/ja/4.x/tutorials/inputs/inputevent.html)が関わってきますが、ここでは一旦置いておきます。

# `MOUSE_FILTER_STOP`
一言で言うと「そのノードでだけ処理して、誰にも渡さない」です。
「誰にも渡さない」ってどういうこと？となりますが、これは他の2つの挙動を見ると理解できるはずです。

# `MOUSE_FILTER_PASS`
一言で言うと「そのノードで処理しつつ、親にも渡す」です。
はじめに自分で入力を受け取ります。
その後、 [`accept_event()`](https://docs.godotengine.org/ja/4.x/classes/class_control.html#class-control-method-accept-event) を呼んで入力を「処理済み」としなければ、親も入力を受け取ります。
入力を受け取った親でもまた `accept_event()` が呼ばれなければ、さらにその親が入力を受け取ることになります。
ただし、親が `MOUSE_FILTER_STOP` である場合には「誰にも渡さない」ため、そこから先へは進みません。

このように親には入力が伝播していきますが、無関係な別の Control には伝播しません。
"PASS" という名前だからといって、重なっている後ろのノードに貫通していくわけではないということです。

# `MOUSE_FILTER_IGNORE`
一言で言うと「反応しない」です。
このノードはマウスに対して存在しないかのように振る舞うので、重なっている後ろのノードがマウス入力を受け取ることができます。

ただし、子には影響を及ぼしません。
つまり、例えば `MOUSE_FILTER_STOP` に設定されているボタンの親に `MOUSE_FILTER_IGNORE` を設定したとしてもボタンをクリックすることができます。
透明な Control が画面を覆い尽くしているような場合、これを設定しないと何も押せなくなるので注意が必要です。


# 参考文献
[Control — Godot Engine (4.x)の日本語のドキュメント](https://docs.godotengine.org/ja/4.x/classes/class_control.html)
[Using InputEvent — Godot Engine (4.x)の日本語のドキュメント](https://docs.godotengine.org/ja/4.x/tutorials/inputs/inputevent.html)
【javascript】【jquery】のセレクタ関連

# イベント内で処理を行う
```javascript
クリックされたら
$('input[name="user_name"]').on('click',function(){ //処理 });

$('input[type="radio"]').on('change', function() {  //処理 });

$('label input[type="checkbox"]').click(function() {  //処理 });

$('button[name="img_select"]').change(function() {  //処理 });

ドロップリストやラジオボタンが変更されたら
$('select[name="user_name"]').on('change', function() { //処理 });

    

入力または削除がされたら
$('input[name="user_name"]').on('input', function() { //処理 });
    

```

# 判定
```javascript
チェックされていたら
if ($(this).is(':checked')) {
    //処理
} 
チェックが外れてたら
if (!$(this).is(':checked')) {
    //処理
}

```

# イベントを起こす
```javascript
チェックする
$('input[name="user_name"]').prop('checked', true);
チェック外す
$('input[name="user_name"]').prop('checked', false);

入力を不可にする
 $('input[name="user_name"]').prop('disabled', true);
入力を不可から可能
 $('input[name="user_name"]').prop('disabled', false);

idに値をセット
$('input[name="user_name"]').attr('id',0);

変数にidの値を追加
id = $('input[name="user_name"]').attr('id');

valueに値をセット
$('input[name="user_name"]').val();

変数にvalueの値を追加
val = $('input[name="user_name"]').val('0');

```
# セレクタ指定
```javascript
現在 選択されているドロップダウンリストを選択
$('select[name="user_name"]').prop("selectedIndex");

一つ目のセレクタ
$('input[name="user_name"]').eq(0);

$('input[name="user_name"]:not(:first)');

$('input[name="user_name"]:not(:eq(1))');

sagyo_の文字列を含むselectのname属性の個数
const sagyo_cnt = $('select[name*="sagyo_"]').length;

```


# セレクタ挿入
```javascript
後ろにtr挿入
$('#aaa_table').after('<tr><td style="width: 35%; text-align: left;" class="insert_deck">'+dragon_img+'</td></tr>');

前にtr挿入
$('#aaa_table').before('<tr><td style="width: 35%; text-align: left;" class="insert_deck">'+dragon_img+'</td></tr>');

```
CCoEメンバーイチオシ！読んでよかったオススメ書籍3選-AWS編-

# はじめに

「CCoEメンバーイチオシ！読んでよかったオススメ書籍7選」の続編！としまして、CCoEメンバーが選んだ読んでよかったAWS関連の書籍について紹介したいと思います！:robot:


https://qiita.com/mizuki_hori_POHD_CCoE/items/240f8ff1d2f536fd65fc


これからAWSを勉強したい方、AWSについてもっと学習したい方等、幅広い皆様の学習の一助になれば幸いです:relaxed:


## エバンジェリストの知識と経験を1冊にまとめた AWS開発を《成功》させる技術

:koala: 推薦者コメント
この本のコンセプトは「**時間が経っても形骸化しない考え方やノウハウを詰め込んだ本**」です！
AWSには様々なサービスがあり要件に応じたサービス選択や組み合わせが重要ですが、本書でアーキテクチャの考え方、サービスの選定のポイントを学ぶことができます。システムの安定稼働に影響する非機能要件のノウハウについても1章割かれていて参考になります。

:writing_hand:こんな人にオススメ！
・PJマネージャーからPJメンバーまでAWSを利用したシステム開発に関わる全ての方

https://www.sbcr.jp/product/4815617523/

## IAMのマニアックな話

:panda_face: 推薦者コメント
AWS界隈で有名な佐々木拓郎さんが執筆された、**IAMに関する一通りの知識と、権限設計のノウハウが満載**です。分かってるつもりでも新しい発見が得られる（はず）書籍だと思います！

:writing_hand:こんな人にオススメ！
・IAM Userで利用をしているが、Administrator権限しか付与したことがない方
・IAMについての理解を更に深めたい方

https://booth.pm/ja/items/1563844

## AWSエンジニア入門講座――学習ロードマップで体系的に学ぶ
:panda_face: 推薦者コメント
AWSに対する体系的なアプローチを促す一冊。初学者が効果的な学習パスを辿れるように、丁寧に解説されています！

:writing_hand:こんな人にオススメ！
・AWSをこれから学ぶ予定の方
・AWSに興味はあるけれど、どこから始めればよいか迷っている方

https://gihyo.jp/book/2022/978-4-297-12537-0



# 最後に

オススメ書籍として紹介させていただいた本は以下です。

:::note info
- エバンジェリストの知識と経験を1冊にまとめた AWS開発を《成功》させる技術
- IAMのマニアックな話
- AWSエンジニア入門講座――学習ロードマップで体系的に学ぶ
:::

弊社のCCoEメンバーがオススメするAWSに関連した書籍を3冊紹介致しました:triangular_ruler:
年末年始にお休みを取る方も多いと思いますので、この機会にぜひ手に取ってみてください:snowman2:


# 弊社では一緒に働く仲間を募集中です:sparkles::santa_tone1:
現在、様々な職種を募集しております。
カジュアル面談も可能ですので、ご連絡お待ちしております！

募集内容等詳細は、是非採用サイトをご確認ください。

https://engineer.po-holdings.co.jp/
オンラインホワイトボード「Miro」（私の）徹底活用術

[Software Design Advent Calendar 2023](https://qiita.com/advent-calendar/2023/softwaredesign) 23日目の記事です。

Software Designで連載されていた『オンラインホワイトボード「Miro」徹底活用術』ですが、2023年9月号で終了してしまいました。そこで、今年の一人アドベントカレンダー「[金子勇とWinnyの夢を見た](https://qiita.com/advent-calendar/2023/winny)」での使い方を紹介させていただこうと思います。

https://miro.com/ja/

 # マインドマップで年表

  何はともかく、マインドマップをよく使います。項目を並べていって、並べ直して、つなぎ直してとやりながら考えるのが楽しいです。

  まず最初に作ったのが、年表です。

  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/755a19e9-1a0e-94a3-46b1-b877e21bc89d.png)

　どんどん年月日順に並べていって、かなり長くなってしまいました。一部を右にクローズアップしてみてます。


 # マインドマップでテーマごとに書き出してみる

　気になったテーマを書き出して、枝葉のアイデアをくっつけてみました。どの辺りで話が膨らむかを考えていきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/c3950b68-1c61-0630-9295-4e1eab906233.png)

# カレンダーに割り振ってみる

  それらをずーっと眺めて、11月になって書きたいテーマをアドベントカレンダーの日付ごとに書き出してみました。

  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/90425d2b-1c35-c2fe-6815-c69c6d718568.png)

　12月が近くなるまでは、テキトーに調べているので、だいたいここまでで3ヶ月かけてます。

# カードを使う

　１１月末からは、毎日ブログを書き続ける事になります。最初の方は、ある程度の項目立てができてるので、ざーっと流して書いていたのですが、段々しんどくなってきます。最後の方で、カードを並べながら項目立てするようになりました。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/2cda5b88-b24e-58d0-4f91-acf65125e954.png)

 ごちゃごちゃしだすと、当然１日で書ききれませんから、カードを作って並べながら書きます。
 
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/4e492647-cc54-3b5b-7aae-2c3e87096f11.png)

　そして、試行錯誤の末、２３日目にしてたどり着いた最終形態が下図になります。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/649ddd15-eb73-6b71-29cc-f645ee688bee.png)

　なんか、頑張ってる感が見えていいですね。

# 作り方

　最後の図をどうやって作ったか、説明します。

　まず、カスタム・フレームを作成して、その中に図形を配置していきます。そして、図形はロックしてしまいます。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/d5b66a91-1431-4e31-5574-35a5813fb2c7.png)

  カードに書き込んで、図形の好きな場所に配置します。

  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/fce702d9-7b33-751a-d471-a00dda757845.png)

　関連があったら矢印を付けてみます。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/21a94f7a-d322-5554-ce5a-3c85769688fc.png)

 好きなタグをつけることもできます。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/58ea03f3-72bb-f72e-6cbb-87b412d6fd0f.png)

 「iFrameコードを埋め込む」を使って、Webページのリンク（左側）を入力したり、Youtubeのリンク（右側）を入力することができます。htmlタグを使う必要はありません。URLをそのまま入力します。Youtubeは、Miro上で動画の再生もできます。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/ecf23b34-099d-9657-8818-66dd6058dd8e.png)

表を使って枠を作る方法もありますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/bb8803d9-a721-5e4e-ad04-dc5bbc945cb8.png)

　もっと便利な使い方がありましたら、教えてください。よろしく。

# 役立つテンプレート

 便利なテンプレートが提供されています。

https://miro.com/ja/mind-map/
Node.jsでYouTubeのプレイリストから動画を削除する


動画自体の削除ではなく、再生リストから削除します。

ベースの記事はこちらです。

https://qiita.com/n0bisuke/items/75062256622d83f49df4

## 再生リストから動画削除コード

```js
'use strict';

const fs = require('fs');
const {google} = require('googleapis');

const googleAuth = () => {
    const CREDENTIALS_PATH = 'client_secret.json';
    const TOKEN_PATH = 'token.json';
    const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, 'utf8'));
    const token = JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));

    const {client_secret, client_id, redirect_uris} = credentials.installed;
    const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
    oAuth2Client.setCredentials(token);

    return oAuth2Client;
};

const oldPlayListId = `PLe~~~~~~~`; //再生リストID
const videoId = `XXXXXXX`; //再生リストから削除したい動画のID

(async () => {
    const auth = googleAuth();
    const youtube = google.youtube({version: 'v3', auth});

    try {
        const res = await youtube.playlistItems.list({
            part: 'snippet',
            maxResults: 50, // 一度に取得する最大動画数
            playlistId: oldPlayListId
        });

        const item = res.data.items.find(i => i.snippet.resourceId.videoId === videoId);
        if (!item) {
            throw new Error('指定された動画はプレイリストに存在しません');
        }

        //プレイリストから動画を削除
        await youtube.playlistItems.delete({
            id: item.id //おそらくプレイリスト+動画の連携ID的なもの
        });

    } catch (error) {
        console.log('The API returned an error: ' + error);
    }
})();

```

## playlistItems.deleteが気になった

今回もGPTに聞いて生成してもらったコードでした。

> ![スクリーンショット 2023-12-22 18.07.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/88b7cff4-c494-5de1-d777-1dc56d34936d.png "スクリーンショット 2023-12-22 18.07.13.png")

```js
await youtube.playlistItems.delete({
            id: item.id //おそらくプレイリスト+動画の連携ID的なもの
        });
```

これでIDだけ指定していて、再生リストと動画の連携がどうなってるんだろうって感じでしたが、ここのitem.idはどうやら`プレイリスト+動画の連携ID的なもの`となっている模様でIDを入れれば紐づいている動画と再生リストのリンクが解除される模様です。

> ![スクリーンショット 2023-12-22 18.08.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/760d198e-4a7c-90b3-6e3a-fadb74290126.png "スクリーンショット 2023-12-22 18.08.12.png")
> 再生リストのIDとも動画IDとも違ったIDが入っていた

疑ってごめん。
gitでリベースした時、sourcetreeでpushできないとき

developブランチからfeatureブランチを切って作業中、他の人の作業ブランチがdevelopへマージした場合、リベースしてから作業再開することがある。
その際、コミットが進んでなければリベースだけで済むが、こっちがある程度進んでるとプッシュしようとするとエラーがでる。

そんな時にターミナル開いて強制プッシュするコマンド備忘録。

```
git push --force-with-lease origin feature/xxxxx
```
社内LT会に出てみた話

# LT会ってなんぞ？？
当時のチームの方から社内でLT会やりましょう！企画するんで！！
って言われた時に「いいっすね～」とは言ったもののLT会・・・？
ってなってました。

LT（Lightning Talk）は短い時間でいい感じに話すやつで、
社内の人たちアウトプットしなさすぎだから気軽にできるLT会やりたいんだよね～とのことでした。

# 参加エントリーしてみて お？？お？？？
yaikawaさんなら大丈夫っす！DBネタとかやってください！
との勧めもあったり、新人さんも配属されて半年くらい経つのもあったりで、
やるかーと重い腰を上げて若い人向けに正規化についてLTしてみることにしました。

自分のチームが主催して開催することもありメンバーは6人全員参加する方針ですすみました。

最初は10人ちょいくらいの規模かな～と思っていましたが、約30組（部署が90人くらい）とかなりの
規模になってうひょ～となった記憶があります。

内容についても真面目なものから面白いものまでたくさんあり、チームメンバーと笑いながらエントリー表を見ていました。

# いざ開催、恐怖の初心者質問で恐縮なのですが・・・
開催の数日前から、どうせなら楽しいLTにしたいよねということで社内のメタバース環境で、発表スライドを表示しながらどうやったら盛り上がるかな～という議論が始まりました。

最終的にはニコ動みたいにスライド上にコメント流せたら発表者も視聴者もワイワイできるね！ということになり
[コメンタブル](https://tetsuakibaba.github.io/commentable/) の導入が決まりました。

初心者質問で恐縮なのですが・・・の質問をする人もいるかと思いましたがそういうこともなく、ワイワイとLT会は進みました。


# 発表してみての感想
5分枠での発表でしたが、スクリーンに表示されるコメントも拾いつつ参加者とも会話しながら進めることでほんとにライトニングや・・・！ってくらい一瞬で発表が終わりました。

会議やちゃんとした発表のように顔色をうかがいながらひえ～ってなる必要もなくかなり楽しく発表ができ、
結構自分の性格的にもLTはあってるんじゃないかと思います。

# おわりに
会社内でのイベント等で悩んでる場合、LT会やってみるのはかなりアリだと思いました。
割と他チームの人とはかかわりがない中、発表からこの人同じ匂いがする・・・といった感じで新たな交流が生まれたりでかなり満足度が高いです。
メンタルケアの方法がわからなかったので、ChatGPTにきいてみた

## はじめに
エンジニアとしてのキャリアを築くには、技術的スキルと同じくらい、メンタルヘルスの維持が重要だと感じています。
メンタルケアの重要性と実践方法について、ChatGPTに尋ねてみました。

## インタビュー
#### インタビュアー：
エンジニアとしてのメンタルケアで、特に重要なことは何ですか？

#### ChatGPT：
特に重要なのは、「ワークライフバランスの維持」と「自己認識とストレス管理」の２点です。

#### インタビュアー：
ワークライフバランスを維持するとは？
また、具体的な方法はどのようなものがありますか？

#### ChatGPT：
ワークライフバランスの維持
エンジニアは厳格な納期と複雑な課題に直面します。これらは長時間労働を促し、プライベートな時間やリラックスする時間を奪います。しかし、適切なワークライフバランスは、長期的なメンタルヘルスと生産性を維持するために不可欠です。

実践方法
・定時で仕事を終える: 毎日決まった時間に仕事を終えて、しっかり休むことが大切です。
・休暇を取る: 定期的に休暇を取り、リフレッシュする時間を確保します。
・趣味の時間を作る: 自分の好きな活動や趣味に時間を割り当て、仕事以外の楽しみを持ちます。

#### インタビュアー：
なるほど、仕事と自分の時間のバランスを見つけることが、ストレス管理にもつながるのですね。
では、自己認識とストレス管理を高めるためには、どんなことをすればいいのでしょうか？

#### ChatGPT：
自己認識とストレス管理
エンジニアリングの世界は常に変化しており、それに伴うストレスは避けられません。自己認識を高め、ストレスを効果的に管理することは、メンタルヘルスを守る上で極めて重要です。

実践方法
・日記をつける: 日々の感情やストレスの原因を記録し、パターンを認識します。
・マインドフルネス練習: 毎日数分間の瞑想や深呼吸を行い、心を落ち着かせます。
・運動を続ける: 定期的な運動はストレスを減らし、心身の健康を保ちます。
・専門家のサポートを求める: 必要ならば、カウンセラーやセラピストの助けを利用します。

#### インタビュアー：
自分の感情をきちんと理解することが、こんなにもストレス管理に役立つとは驚きました。
日々の意識と小さな行動が、長期的なメンタルヘルスに大きく影響するんですね！

その他にメンタルケアのためにできることはありますか？

#### ChatGPT：
はい、コミュニケーションをオープンに保ち、作業環境を整え、質の良い睡眠を取ることも大切です。小さな一歩から始めて、日々の習慣に取り入れることで、長期的にメンタルヘルスを維持する助けになります。

#### インタビュアー：
睡眠や環境がこんなに影響するとは思いませんでした。健康的な生活習慣が全体のバランスをとる鍵のようですね。ありがとうございました。これらのアドバイスを意識して、健康的なメンタルを保ちながら働くことができそうです。

#### ChatGPT：
はい、ぜひ実践してみてください。日々の小さな実践が、健康的なメンタルヘルスを維持するための基盤となります。いつでもサポートを求める勇気を持ち、自分自身の健康を最優先に考えてください。
TI-84 Plus CE Python について

# はじめに

また、モノ好きにもPython機能つきグラフ電卓を購入しました。「TI-84 Plus CE Python」です。

![スクリーンショット 2023-12-22 13.54.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/bdb98044-ebd0-b98f-a7e5-934cd9220b8c.png)

電源は乾電池ではなく、充電パッテリーでした。

※以前に購入済の NumWorks や fx-CG50 の記事は以下にあります。

* [NumWorks の MicroPython](https://qiita.com/inachi/items/40c683f65aede5df7697)
* [NumWorks の MicroPython (その後)](https://qiita.com/inachi/items/e418e8f1d45c5ebac32d)
* [カシオのカラーグラフ関数電卓 fx-CG50 に MicroPython がのったゾ](https://qiita.com/inachi/items/41dbe628be64cfea4193)
* [カシオのカラーグラフ関数電卓 fx-CG50 に MicroPython がのったゾ(その後)](https://qiita.com/inachi/items/8edf83212965e70a0ce8)

# Python を使うには

まずは [on] キーを押します。

![Capture2-1703225132256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/7cc9988a-20d7-fccb-7882-7f4d9c56271e.png)

2 で CONTINUE すると...

![Capture6-1703225215189.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/885bae33-8035-9538-205c-d601259bcfb7.png)

さて、Python を使うにはどうするのでしょう？ NumWorks　や fx-CG50 のようなアイコンメニューはありません。

マニュアルは[ここ](https://education.ti.com/html/eguides/graphing/84PlusCEPy/EN/content/home_84pluscepy_v6.0.0.HTML)で参照できます。

マニュアルながめてみても Python の起動方法はよくわからなkったのですが、[apps] キーを押すと、

![Capture3-1703228531360.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/56e5f601-96a8-305e-65c2-0fcedb7ba13e.png)

これっぽい。また、[prgm] キーを押してみると、

![Capture5-1703228581083.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/60784f6a-f86f-685c-a4d8-96b5f0b5579c.png)

結局、どちらでも Python アプリに入れました。

![Capture7-1703228681884.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/435b859a-e352-1ec0-dfd3-8ea9a56adea7.png)

外面下部に示されているように、たぶん[trace]キーがファンクションキーになっていて、これを押せば REPL に入れるのでしょう（8bitパソコンのBASIC使っていたころを思い出します）。

![Capture8-1703229072099.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/106817/fbc2a4ae-735a-2461-b51c-189054f2ae2a.png)

REPLに入れて、`help("modules")` でサポートしているライブラリも確認できました。電卓搭載の Python としては、なかなかによいです。 `cmath` もあるとよかったのですが。

マニュアルの[ここ](https://education.ti.com/html/eguides/graphing/84PlusCEPy/EN/content/eg_gsguide/m_apps/apppyfray.HTML)の説明によると TI-Python と命名されていて、CircuitPython をベースにしたものとのことです。もっとも CircuitPython 特有のライブラリはないので、ぜんぜん CircuitPython っぽくありません。

TI-84 Plus CE の CPU は [eZ80](https://ja.wikipedia.org/wiki/EZ80) のはずですが、Python のために ARM のマイコンも載せているみたいです。[Adafruit の記事](https://blog.adafruit.com/2021/06/25/adafruit-interviews-texas-instruments-education-about-python-on-calculators-and-more-ticalculators-ticodes/)によれば SAMD21 らしいです。

とりあえず、本記事はここまで。
Railsアソシエーション: 中間テーブルを使用した関連性の構築


## 多対多のアソシエーションとは？
多対多のアソシエーションは、2つのモデル間に直接の関連性を持たせる代わりに、中間テーブルを使用して関連を構築します。これにより、柔軟性が向上します。

## 例: 学生と科目の多対多のアソシエーション

Studentモデル
```
class Student < ApplicationRecord
  has_many :enrollments
  has_many :courses, through: :enrollments
end
```
Courseモデル
```
class Course < ApplicationRecord
  has_many :enrollments
  has_many :students, through: :enrollments
end
```
Enrollmentモデル
```
class Enrollment < ApplicationRecord
  belongs_to :student
  belongs_to :course
end
```
この例では、StudentとCourseが多対多のアソシエーションを持ち、中間モデルとしてEnrollmentが関連付けられています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623005/93042de3-1116-4ef0-1c57-ed7133429a9f.png)
これで、学生(Studentモデル)と科目(Courseモデル)が中間テーブル(Enrollmentモデル)を介して関連付けられ、柔軟で拡張性のあるデータモデルが構築されました。
コマンドを実行し終わったらデスクトップ通知を出すコマンドntをChatGPTに作ってもらった

## はじめに

ダウンロード、コンパイル、そのほかコマンドを用いた時間のかかる処理はいくつもある。コンピュータにそういった処理を行わせている間に、並行して他の作業を進めたいことがある。そのために、コンピュータが処理をし終わったらデスクトップ通知を出すコマンドラインツールをChatGPTに書いてもらった。

最初はそういうツールはとっくに誰かが公開しているはずだと思ったが、[思ったよりも少なそう](https://superuser.com/questions/345447/how-can-i-trigger-a-notification-when-a-job-process-ends)だったので自分で作ってみた。

最初はCrystalで作ってみたが、[クラスプラットフォームのですくとっｐ通知ライブラリ](https://github.com/notify-rs/notify)がなかったので、Rustで書いてみることにした。Rustは書けないので、全面的にChatGPTに書いてもらった。

## nt

https://github.com/kojix2/nt

インストール

```
cargo install --git https://github.com/kojix2/nt
```

使い方はとても簡単で、

```
nt wget https://large.file.com/largefile.tar.gz
```

などとすると、処理が終わるとデスクトップ通知が出るようになる。
標準入力にも対応している。

```
ls | nt wc
```

## おわりに

このコマンドラインツールには無数の改善策があると思う。
本当はCrystalで書きたいけど、流石にライブラリの豊富さはRustに勝てない。

また、この記事を書いてる途中で `ntfy` というコマンドラインツールを知った。

https://github.com/dschep/ntfy

今の `nt` は本当にミニマムなところが気に入ってるが、これを参考にして少し機能強化を図るかもしれない。

この記事は以上です。
【Swift学習備忘録2】時計アプリ制作編

## 時計アプリの仕様
時計アプリの仕様を下記に記載しました
1 画面に表示される時刻は常に更新され現在の時刻が表示されること
2 画面の中央に
　現在の時刻：
　⭕️時⭕️分と表示されること
3 Swift UIを用いること


## プロジェクトの作成
下記の順番で選択
File > New > Project > iOS > App

InterFaceでSwift UIを選択する

**ContentViewについて**
表示されるコンテンツの内容を記載できる部分のよう
```Text("Hello, world!")```
上記のHello, World!をGood Bye, World!に変更するために下記のようにソースを変更する
```Text("Good Bye, World!")```

変更した結果出力されるテキストの内容が変更された
![qiita01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659219/b23bee9b-38b8-53f2-a0d2-b7dbf02c2775.jpeg)




なので、まずContentViewに「現在時刻：」を追加する

次に、現在の時刻をContentView内で定義する
```var currentTime = Date()```

currentTimeにDateDate型のインスタンを付与している

現在時刻の用意ができたので、時間を文字列に変換しテキストとして表示するためのメソッドを作成します

```
private func formattedTime() -> String {
            // 日付を文字列に変換する
            let dateFormatter = DateFormatter()
            //timeStyleプロパティを利用してフォーマットをhh:mm:ssに変更する
            dateFormatter.timeStyle = .medium
            // currentTimeにフォーマットを適用した文字列を渡している
            return dateFormatter.string(from: currentTime)
        }
```

**VstackのTextにメソッドを渡して表示される結果を確認します**
![qiita02-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659219/3a2a5ca7-b072-8c36-9061-8837b249387c.jpeg)


現在時刻の表示ができましたが問題が発生しました

## 問題発生：時間が更新されない
表示したタイミングでの時刻のみが表示され更新されませんでした
常にメソッドを呼び呼び続けることが必要なようです

調べた結果、TimerライブラリのscheduledTimerメソッドを利用することで毎秒処理を行うことができるというものがありました
なので毎秒現在時刻の取得を行いcurrentTimeに渡すメソッドを作成します
また、画面を表示してから毎秒行うため、ロードイベントを作成します
ロードイベントの作成方法は下記です
```.onAppear```

下記にbodyへ追加するメソッドを記載します
```
// 画面が表示されたときに実行される処理(ロードイベント)
        .onAppear {
            // タイマーを起動して1秒ごとにcurrentTimeを更新
            let _ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
                currentTime = Date()
            }
        }’’’
```

**問題が再度発生**
謎のコンパイルエラーが、、、
「Cannot assign to property:'self' is immutable」


原因を調べていたらcurrentTimeに@Stateを使用しておらず、毎秒変数更新しても値が変わらないぞというエラーっぽい？ のでcurrentTimeを下記に変更
```@State var currentTime = Date()```

表示した結果は下記
![qiita02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3659219/e03a41f5-b93a-7dae-e695-3ecc18245623.jpeg)



仕様通り毎秒時間が更新されました、、、
今回はここまで
ChatGPTを使ってデータETL業務の属人化をなくした話

# はじめに
トラストバンクでふるさと納税事業におけるパートナーアライアンスの事業責任者をやっている@imorisと申します。

# サマリ
とあるシステムで数十万件を超えるデータのETLを行う必要がありました。継続的にETLが発生する場合、システム化して自動化するのが一丁目一番地ですが、時間軸的にどうにもならず、今までであればこれをSQL使える人がなんとかするというやり方だったところを、ChatGPTを使ってどんな人でもできるように標準化および属人化排除をしました。

# 背景
自身はエンジニア出身なので、CUIでデータ抽出して変換してAPI経由でアップロードしてみたいなことは自分でできるのですが、ビジネス側の人はCUIは基本使えないですし、スプレッドシートベースで処理したり、データ抽出班に依頼してETLを行うのが通常です。そのため以下のような問題が発生します。

- 自分でExcelやスプレッドシートで処理しようとするが、データが重すぎてスプレッドシートでは扱いきれない（フリーズしてしまう）
- じゃあということで、データ分析チームやDBを触れるエンジニアにお願いするが、彼らが非番の時や忙しい時に臨機応変に対応できない
- データ分析チームやエンジニアへの依頼の要件が正しく整理できていない場合があり、PDCAを何度も回せない（彼らに再作業をお願いすることになり迷惑をかけてしまうので気後れする）

# イシューとアプローチ
というわけで、ここで解かなければいけないイシューは「短い時間軸でビジネス側の人間が気軽かつ迅速にトライ＆エラーできるETL環境を作ること」です。

属人化をなすくためのアプローチは古来より色々あります。
1. マニュアルを作る
1. 勉強会をやる
1. GUI化する
1. 半自動化する
1. 完全自動化する

ここで私が選択したのが、1.と3.と4.の組み合わせで、3.と4.の手法として採用したのがChatGPT Plusでした。
奇しくもChatGPT PlusでちょうどCode Interpreterがロンチされ、データ処理・ビジュアル化・Pythonコード生成ができるようになったタイミングでした（このタイミングで出てくれてめっちゃ助かった）。

# 具体例
まずはデータ処理のためのプロンプトを書いていきます。下記はエンコーディングも異なり、ファイルの形式もcsvとtsvで異なり、さらにtsvにはヘッダ行がないというケースで、この2ファイルの差分を抽出するというプロンプト例になります。

```
・A.csvはshift-jisでエンコードされています
・B.tsvはutf-8でエンコードされています
・A.csvでダブルクォーテーションはすべて取り除いてください。
・B.tsvにヘッダ行を追加してください1列目はcol1、5列目をcol2という名前にしてください。6列目以降は読み込みをスキップしてください。
・A.csvとヘッダ行を追加したB.tsvの2つのファイルを比較して、条件にマッチする特定の行を抽出してCSVファイルとして出力してください。条件は以下の通りです。


条件
・A.csvの1列目のカラムcol1と、B.tsvの1列目のカラムcol1がマッチするか確認する
・マッチした場合、A.csvの7列目のカラムcol7が1になっている行をまとめて抽出する
・A.csvにcol1にデータが存在しておらず、B.tsvのcol1にしかデータがない場合は、抽出対象外とする
・出力CSVの名前は「A_B_diff_YYYYMMDDHHMM」とする。その際YYYYMMDDHHMMは、B.tsvファイルの作成時刻を使う。
・ファイル読み込み中にエラーが発生した場合その行はスキップして読み込みを続ける。ただしエラーが出た行だけ別のCSVファイルに出力する。

次に出力したCSVから2つ目のCSVを出力してください。
 条件は以下の通りです。
・CSVの出力カラムは、col1、col2の2つで、col2のデータにはすべて0を入れてください。
・出力CSVの名前は「C_upload_YYYYMMDDHHMM」としてください。YYYYMMDDHHMMは、B.tsvファイルの作成時刻を使ってください。
・出力CSVはshift-jisでエンコードしてください。

最後に抽出した件数を教えてください。
```

こうしたプロンプトを実際にChatGPT使ってトライ＆エラーしてプロンプトをチューニングしていき、再現性が高い状態にもっていきます。
その上で、利用マニュアル（データをどこから持ってくるのか、ChatGPTのNew Chatを新たに作るのか/作らなくてもいいのか、ChatGPTの結果は必ずしも正しくないので最後は人間の目で確認すること、などを書いたマニュアル）を用意しました。

# やってみた結果
プロンプト、利用マニュアル、ChatGPT Plusのアカウント（月20ドル）を整え、ビジネス側チームに展開したところ、入社1年目の非エンジニアの方でもマニュアルを見ながらコピペすれば数十万件のデータETLをすぐ実行できるようになりました。
これにより、SQLを使える数名の人しか対応できなかった状態が、社内にいる誰でもが同じ対応できる状態にすることができました。

一方でいくつかChatGPTならではの制限もありました。
- ChatGPTは1ファイルあたり最大で500MBのデータしかアップロードできない
- 全く同じファイル・プロンプトなのにうまく出力されない場合がある
- ChatGPTのモデルが変更されると動作が遅くなったり、ETLのロジックが勝手に変わったりする
- こうしたChatGPTの動きを理解してプロンプトを適宜チューニングするスキルがいる

一般的によく挙げられる制限ではありますが、それでもSQLをイチから覚えてCUIでデータ処理ができるようにするコストに比べると遥かにマシです。


# AIによって変わる業務
今回やったことは少しChatGPTを業務導入したことがある人なら誰でも考えたりやってみたりする内容かと思います。やっていることはめちゃめちゃ単純です。しかし誰でもが数十万件のデータ処理を何もインストールせずGUIベースで手軽にトライできるというのは大きなメリットでした。
この手のAIによる業務効率化と属人化をなくしていく展開は社内/社外で大量にあると思われます。機密情報を含む内容だとまだまだセキュリティ的にどう対処していくのかは課題として残りますが、日々進化するAIでより複雑な処理もできるようになってきており、社会全体でやれるところから大きな変化がすぐに訪れることを実感した次第です。
センシティブな文章を判定するAPIを作りました（NestJS+OpenAI API）


## はじめに
https://argos.tokyo/

社内でNestJSとOpenAI APIを用いてチーム開発をするという取り組みを実施し、
自分のチームではセンシティブな文章かを判定するAPIを作りました。
その中で、OpenAI APIでプロンプトをカスタマイズして気付いたことや学んだことを書こうかなと思います。

:::note info
株式会社mofmofは受託開発会社ですが、メンバー自らがオーナーとなってプロダクト開発にチャレンジしています。
この記事は、NestJS, 生成AIにチャレンジする「金曜日のチーム開発」の成果です。
:::
https://indie-dev.mof-mof.co.jp/

## サービス概要
文章に対してセンシティブかどうかを判定してくれるAPIです。
判定して欲しい文章をリクエストすることで、センシティブかどうかをBooleanで返却してくれます。
センシティブな内容であると判定された場合は判定された理由も返却されます。

## プロンプトカスタマイズ
判定の精度を高めるためにプロンプトをカスタマイズしましたが、
判定を厳しくするとセンシティブではない文章がセンシティブだと判定され、緩めると逆にセンシティブな文章がスルーされるという問題に苦労しました。
modelは`gpt-4-1106-preview`を使用しましたが、ChatGPT４と相談したりもしましたが、判定結果が異なったりとなかなかうまくいきませんでした。

`単語ではなく文章全体の文脈を重視して判定してください`というような文言を追加することで少し精度を高められましたが、
目標とする精度に達することはできておらず、まだまだカスタマイズが必要という状況です。

## token計測
プロンプトをカスタマイズすることで精度を高めることができますが、プロンプトの長さはコストに直結します。
- OpenAI APIのモデル毎の費用
https://openai.com/pricing#language-models
- tokenの簡易計測
https://platform.openai.com/tokenizer

正確に計測するには、レスポンスに含まれるusageを確認するのが良さそうでした。
https://platform.openai.com/docs/guides/text-generation/chat-completions-response-format
実際にtoken数を計測してみました。
```zsh
# gpt-4-1106-preview
{ prompt_tokens: 728, completion_tokens: 173, total_tokens: 901 }
```
completion_tokensはOpenAI APIが生成する内容なので、全く同じ内容のリクエストでもtoken数は変動します。

しかし、上記の計測方法だと実際にリクエストを送るのでtokenを消費します。
試していませんが、tokenを消費したくないという場合は、Pythonライブラリで計測用のものがありました。
https://cookbook.openai.com/examples/how_to_count_tokens_with_tiktoken

## その他

### レスポンス速度
GPT4-TurboよりGPT3.5-Turboの方が速かったです。
- GPT4-Turboにアクセスが集中している？
- GPT4-Turboの方が裏側の処理が重い？

### JSONモード
新しいモデルにJSONモードというレスポンスをJSONに変換してくれる機能が追加されていました。
プロンプトに「JSONで返却すること」のような命令分は追加する必要があるので、
できればそれも不要にして欲しい感がありました。

### token数
日本語でプロンプトを作っていくとどうしてもtoken数が嵩むので、最終的にプロンプトを英語に変換したり等の対策が必要だと感じました。
実際に変換してみるとtoken数は抑えることができました。
しかし、単純に英語に変換するだけでは判定の精度が下がったので、ニュアンスを変えずに変換する必要がありそうでした。

## さいごに
- 初めてOpenAI APIを触りましたが、プロンプトエンジニアリングの難しさを学びました
- 同じ文章でも結果が異なることがあるので、常に１００%の精度で判定し続けるのは難しいのかなと感じました
- GPT4のmodelだとturboでもレスポンス速度が遅い(数十秒かかる時もある)と感じることも多く、今後の新modelのリリースでその辺りの改善も期待したいです
- GPT3.5とGPT4を比較して精度の差は体感ですがそこまで大きくなかったのかなと思います。コストは約10倍差があるので、最新の情報を取得したいというのがなければ基本的にはGPT3.5を使用するで良いのかなと感じました。
kintoneの標準機能で年齢を自動計算したい

# kintoneの標準機能で年齢を自動計算したい

## TL;DR

|フィールド名|種類|計算式|結果|
|:--|:--|:--|--:|
| 更新日時     | 更新日時 | (自動計算)                                   | 2023-12-22 14:57 | 
| 生年月日     | 日付     | 手動入力/流し込み                            | 1987-01-01 | 
| 本日日付計算 | 計算     | DATE_FORMAT(更新日時,"YYYYMMdd","Asia/Tokyo")| 20231222 | 
| 生年月日計算 | 計算     | DATE_FORMAT(生年月日,"YYYYMMdd","Asia/Tokyo")| 19870101 | 
| 年齢計算     | 計算     | ROUNDDOWN((本日日付計算-生年月日計算)/10000) | 36 |
| 年代         | 計算     | ROUNDDOWN(年齢計算/10,0)*10                  | 30 | 


## プラグインだけではうまく行かないことも
年齢計算のプラグインは多々ありますが、レコード更新時のみ計算されてCSV流し込みでは計算されない場合もあります。
kinotneの標準機能の自動計算であれば、CSV流し込みでも計算されるので自動計算で対応したい状況になりました。

開発Tipsでも「[経過年数を表示する](https://cybozu.dev/ja/kintone/tips/development/customize/date-format/display-elapsed-years/)」の紹介はありますが、レコードに保存するのではなく、画面表示だけです。


ちなみに、kintoneヘルプの「[日数や週の計算をするには](
https://jp.cybozu.help/k/ja/utility/app/number_of_days.html)」でも日付計算方法の紹介がありますが、年齢で計算するには一年の日数で割る必要があり、閏年の日数を考慮する必要があります。
閏年の判定方法は「[日付の値に応じて月末日を表示したい（うるう年を考慮した表示）](https://jp.cybozu.help/k/ja/utility/app/leap_year.html)」にありますが、はっきり言ってめんどくさいです。


## じゃぁ、計算してみよう

|フィールド名|種類|計算式|結果|
|:--|:--|:--|--:|
| 更新日時     | 更新日時 | (自動計算)                                   | 2023-12-22 14:57 | 
| 生年月日     | 日付     | 手動入力/流し込み                            | 1987-01-01 | 
| 本日日付計算 | 計算     | DATE_FORMAT(更新日時,"YYYYMMdd","Asia/Tokyo")| 20231222 | 
| 生年月日計算 | 計算     | DATE_FORMAT(生年月日,"YYYYMMdd","Asia/Tokyo")| 19870101 | 
| 年齢計算     | 計算     | ROUNDDOWN((本日日付計算-生年月日計算)/10000) | 36 |
| 年代         | 計算     | ROUNDDOWN(年齢計算/10,0)*10                  | 30 | 


### 詰まったところ
 - 数値形式(19870928)の日付は日付型に変換できないので、流し込む元データでYYYY/MM/DDにしておく必要があります。
 - DATE_FORMAT(更新日時,"YYYYMMdd","Asia/Tokyo")に対して演算はできず、昨日の日付といった計算はできません。


## さくっと解説

|フィールド名|種類|計算式|結果|
|:--|:--|:--|--:|
| 本日日付計算 | 計算     | DATE_FORMAT(更新日時,"YYYYMMdd","Asia/Tokyo")| 20231222 | 
| 生年月日計算 | 計算     | DATE_FORMAT(生年月日,"YYYYMMdd","Asia/Tokyo")| 19870101 | 

生年月日と本日の経過日数を計算するために、数値化する。


|フィールド名|種類|計算式|結果|
|:--|:--|:--|--:|
| 年齢計算     | 計算     | ROUNDDOWN((本日日付計算-生年月日計算)/10000) | 36 |

日付数値-生年月日数値の上２桁が年齢に該当する。

上4桁部分の差分が誕生日以降の年齢 x 10000になる。
上4桁で誕生日を迎えた後の年齢計算をし、誕生日を迎えたのかを下4桁で判定する。

誕生日の前日なら下4桁の差分が-1（月またぎならもっと小さい値）になる。
誕生日と同日なら下4桁の差分が0になる。
誕生日の翌日なら下4桁の差分が1（月またぎならもっと大きい値）になる。
結果、閏年を考慮した年齢となる。


|フィールド名|種類|計算式|結果|
|:--|:--|:--|--:|
| 年代         | 計算     | ROUNDDOWN(年齢計算/10,0)*10                  | 30 |

下1桁を0に丸めて年代にする。
Treeviewで項目ごとにデータをソートする

## はじめに
tkinterのTreeviewを使用すると，データを表形式のGUIで表示することが可能です．ただ，これらのデータは追加した順に表示されます．
今回はヘッダーをクリックすることで，項目ごとにデータをソートする機能を追加した際の備忘録です．

## 動作環境
- Python 3.12.0
- tkinter 8.6.12

## ソースコード
```sample.py
import tkinter as tk
from tkinter import ttk

def select_header(tree, reverse_flag):
    # クリックされたヘッダー列取得
    x = tree.winfo_pointerx() - tree.winfo_rootx()
    select_column_str = tree.identify_column(x)
    select_column_int = int(select_column_str[1:]) - 1

    l = [(tree.set(k, select_column_int), k) for k in tree.get_children("")]
    l.sort(reverse=reverse_flag)

    for index, (val, k) in enumerate(l):
        tree.move(k, "", index)

    tree.heading(select_column_int, command=lambda:select_header(tree, not reverse_flag))

main = tk.Tk()

style = ttk.Style()
style.theme_use("clam")

datas = [
    ["1", "4", "b"],
    ["2", "3", "d"],
    ["3", "2", "a"],
    ["4", "1", "c"],
]

columns = 0
for data in datas:
    if columns < len(data):
        columns = len(data)
clmn = tuple(range(columns))

tree = ttk.Treeview(main, columns=clmn, show="headings")
tree.grid(row=0, column=0, sticky="nsew")

for i in range(columns):
    tree.heading(i, text="Data"+str(i+1), command=lambda:select_header(tree, False))
for data in datas:
    tree.insert("", "end", value=(data))

main.mainloop()
```
---
### クリックしたヘッダー列取得
「どの列をソートするか」を判別するために，クリックしたヘッダーを識別します．
```python
x = tree.winfo_pointerx() - tree.winfo_rootx()
select_column_str = tree.identify_column(x)
select_column_int = int(select_column_str[1:]) - 1
```
画面に表示しているx座標をもとに```identify_column()```でヘッダ名を#~の形で特定します．```select_column_int```の値は左のヘッダーから順に0,1,2...となります．

### データをソート
```python
l = [(tree.set(k, select_column_int), k) for k in tree.get_children("")]
l.sort(reverse=reverse_flag)
```
```get_childran()```でクリックしたヘッダー列のデータのリストを取得します．```sort()```で取得したデータをソートします．オプションの```reverse```は，```True```が降順ソート，```False```が昇順ソートとなります．

### Treeviewにデータをソート順に表示
```python
for index, (val, k) in enumerate(l):
    tree.move(k, "", index)
```
Treeviewの```move```メソッドは```k```を```index```の位置に移動させます．

今回のソースコードでは，関数内の```heading```で```select_header(tree, not revers_flag)```を設定することで，クリックするごとに昇順ソート，降順ソートを繰り返すようになっています．

## おわりに
Treeviewでソートは基本装備していい気がするので便利なメソッドがあればいいなと切実に思いました．

## 参考文献
http://notwodaily.hatenablog.com/entry/2018/06/15/003411

https://teratail.com/questions/357207
スプリントゴールは大切だってチームメンバに訴えた話

# 概要
社内でスクラムチームを組んで、スプリントを回しています。
チームメンバはスクラムの基礎知識を持っており、スクラムイベントにも慣れています。私は開発者と兼任のスクラムマスターです。

プロジェクトも終わりが見えてきた頃のスプリントプランニングで、次のような発言がありました。

「あとは残りのプロダクトバックログアイテムを消化してバグを修正して、最終期限に間に合うようにリリースするだけだから、いちいちスプリントゴールを考える必要はないんじゃない？」

それに対して私は、スクラムの考え方に基づいて「スプリントゴールは絶対必要！」と主張しました。その際に、チームメンバーと「なぜスプリントゴールが必要か」を話し合い、合意を得ました。端的に言えば、スクラムの三本柱とイベントを効果的に行うためには必須だと思ったからです。

その際に改めて、「具体的にどうして必要なの？」「スプリントゴールは何の役に立つの？」というのを整理したので以下にまとめました。
他のイベントの話をするとまとまりがなくなるので、本記事ではスプリントゴールにフォーカスして進めます。

# 詳細
## スクラムの三本柱
スクラムでは「透明性」「検査」「適応」の三本柱が重要です。
詳細はスクラムガイドに記載があるのですが、ざっくり言うと「あるものに対して、内容が誰でもわかるようになっていて（透明性）、変化を見逃さないようになっていて（検査）、目的に沿う形に軌道修正できるようになっている（適用）」状態を維持することだと理解しています。

## スプリントゴールがないとどうなる？
スクラムの三本柱を、十分に機能させることができません。
言い換えれば、そのチームのスクラムイベントは十分な価値を生み出せなくなります。

たとえば、スプリントゴールがない場合のチームの動きを考えてみます。

日々のデイリースクラムや開発において、「何のための作業をしているのか」を照らし合わせる到達目標がありません。このため、「透明性」を担保できなくなります。

また、スプリント中に新たな作業が見つかった場合、「何から手を付ければよいか」「それはいま本当にすべきことか」を判断することができません。
プロダクトゴールは必要な作業を教えてくれるかもしれませんが、プロダクトバックログアイテムの優先順位を決めるには不十分です。これは、「検査」と「適応」ができない状態であることを示しています。
 
## やり方はチームに依る
スクラムのイベントはチームによって進め方が変わることがあると理解します。
ただ、あくまでもスプリントゴールは必ず設定し、その内容が適切でないと感じるなら、次のスプリントで改善する、という形が良いと考えます。
では、どんなスプリントゴールを立てればいいのか？　という疑問も出てきます。

「今回のスプリントでやると決めたスプリントバックログアイテムを全て消化する」というのは、スプリントゴールにはなりません。いわゆるハリボテのスプリントゴールです。

## スプリントゴールの決め方

2020年度版のスクラムガイドでは、スプリントゴールに明確な説明が追加されているので、そちらを引用します。

> 確約（コミットメント）：スプリントゴール
> スプリントゴールはスプリントの唯⼀の⽬的である。スプリントゴールは開発者が確約するものだが、スプリントゴールを達成するために必要となる作業に対しては柔軟性をもたらす。スプリントゴールはまた、⼀貫性と集中を⽣み出し、スクラムチームに⼀致団結した作業を促すものでもある。
> スプリントゴールは、スプリントプランニングで作成され、スプリントバックログに追加される。開発者がスプリントで作業するときには、スプリントゴールを念頭に置く。作業が予想と異なることが判明した場合は、スプリントゴールに影響を与えることがないように、プロダクトオーナーと交渉してスプリントバックログのスコープを調整する。

スプリントゴールは、トップダウン的に考えれば、**プロダクトオーナーと交渉して決めた今回のスプリントのスコープを表現するもの**です。
あるいはボトムアップ的に考えれば、**今回のスプリントのインクリメントによってユーザが得られる価値を表現するもの**です。

そして、**その達成を開発者は確約します**。達成できない見込みが出た場合、スプリントの中止も視野に入れるほどの、とても重要なものです。

現在の私たちのチームでは、実はほとんどプロダクトオーナーが機能しておらず、開発者がその役割を担っています。
そのため、ボトムアップ的にスプリントゴールを決めることが多いです。
プロダクトオーナーからの要望はありませんが、エンドユーザはレビューに参加してくれるので、スプリントゴールは「エンドユーザが価値を実感できる形」になるように決めています。

インクリメントを作ることだけを重視すると、ともすれば「スプリントバックログアイテムをたくさん消化すること」が目標になってしまいます。
そうではなくて、「今回のインクリメントでユーザに与えられる価値は何か」をスプリントプランニングで意識することで、チーム活動の「透明性」を担保し、ゴールが達成できるかを「検査」し、必要があれば「適応」することができると思います。


## 余談：私のチームの話
社内でスクラムチームを組んで、スプリントを回しています。
私は開発者と兼任のスクラムマスターです。スクラムマスターとして、チームが良い感じにスクラムを実践できるように、縁の下の力持ちをしたいと思って頑張っています。
チームメンバはスクラムの基礎知識を持っており、スクラムイベントにも慣れています。

そんなチームでも、「スクラムのルールではこうだけれど、いまのチームの状態には即していないから変えよう」ということが起こります。チームメンバが全員納得してそれを行うなら、チームの課題解決の一つとして良いことだと思います。

プロジェクトも終わりが見えてきた頃のスプリントプランニングで、次のような発言があり、私は正直驚きました。

「あとは残りのプロダクトバックログアイテムを消化してバグを修正して、最終期限に間に合うようにリリースするだけだから、いちいちスプリントゴールを考える必要はないんじゃない？」

それに対して私は、スクラムの考え方に基づいて「スプリントゴールは絶対必要！」と主張しました。その際に、チームメンバーと「なぜスプリントゴールが必要か」を話し合い、合意を得ました。端的に言えば、スクラムの三本柱とイベントを効果的に行うためには必須だと思ったからです。
その後、スプリントゴールは設定され、現在のスプリントでもスプリントゴールの設定は継続しています。

いろいろな考え方があり、やり方もチームによってさまざまだと理解します。
その中で私は、「スプリントゴールを作らない」以外の方法で、スクラム活動の改善が実現できると信じます。

# 参考
- [スクラムガイド 2020年度版(日本語)](https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf)
- [AGILE STUDIO:スプリントゴール](https://www.agile-studio.jp/post/apm-sprint-goal)
- [スクラムの資格](https://www.scrumalliance.org/)はリンク先をご参照ください（詳細割愛）
MVCモデルについて

### MVCモデルとは
プログラムの役割を
・Model
・View
・Controller
の3つに分ける考え方です。

---
#### Controllerとは
ユーザからリクエストを受けてレスポンスを返す役割です。

#### Viewとは
画面表示（HTML）を担当する役割です。

#### Modelとは
DB関連の処理を担当する役割です。

---

### -処理の流れ-
例：特定のブログ記事1件を表示するリクエストが送られてきた場合

ブラウザから記事を閲覧したい[http://xxx/blogs/1]とリクエストが送られてきました。
※blogs/1なのでid=1になります。
↓
**Router**というところが呼び出されます（RouterはControllerを呼び出す役割）。
↓
「ブログ記事1件を表示したい」とリクエストされたため、RouterはControllerの中の**blogsContorollerというクラス**を呼び出します。
↓
今回は「ブログ記事1件を表示したい」とリクエストを受けたため`show`アクションが呼び出されます。
※Controllerはblogの情報を
・投稿したいのか(`new`)
・表示したいのか(`show`) 
などによって取るべきアクションが変わります。
↓
Controllerはid=1の情報をくださいと**Model**に依頼します。
↓
Modelはid=1の情報を**DB**から取得してきます。
↓
DBで取得してきた情報をControllerに返します。
↓
ControllerはModelから渡された情報をもとに処理をし、**View**に渡したい情報をインスタンス変数に代入します。
↓
Viewは渡されたインスタンス変数をもとに画面に表示するHTMLを作成してControllerに返します。
↓
Controllerは受け取ったHTMLをレスポンスとしてブラウザに返します。

※MVCは3つの役割を表しているだけで全ての処理がこの3つに該当するわけではありません。
Azure Functions のタイマートリガーは RunOnStartup 設定に注意

## 結論
関数の `run_on_startup` 設定は True ではなく基本的に `False` にすること

### Python TimerTrigger 例

```python
@app.schedule(schedule="0 0 15 19 * *", arg_name="myTimer", run_on_startup=False, #ココ
              use_monitor=False) 
def timer_trigger_sample(myTimer: func.TimerRequest) -> None:
    # 以下略
```

## 調べたこと
実際に関数の run_on_startup 属性を True 設定にすると、指定した時間以外に実行されてしまったり、ローカル環境からのデプロイ時に動作してしまうなどの状況が見られました。

Microsoft 自体もドキュメントにて、run_on_startup 属性を True に設定することはしないよう以下のように注意喚起しています。

> 運用環境では、runOnStartup を true に設定しないでください。 この設定を使用すると、まったく予期できないときにコードが実行されます。 特定の運用環境の設定では、これらの追加の実行によって、従量課金プランでホストされるアプリのコストが大幅に高くなる可能性があります。 たとえば、runOnStartup を有効にすると、ご利用の関数アプリがスケーリングされるたびに、トリガーが呼び出されます。 runOnStartup を運用環境で有効にする前に、ご利用の関数の運用環境での動作を十分に理解しておく必要があります。

https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-timer?tabs=python-v2%2Cisolated-process%2Cnodejs-v4&pivots=programming-language-python

例外的に、Azure Functions Core Tools を使ったローカル環境での開発時には、True 設定にすることで、func start コマンドによる Functions 環境立ち上げ時に実行されるなど、即時確認ができるため便利ですが、設定に注意が必要なポイントです。

## なぜ書いたか
私は、作業環境として、VSCode の拡張に Azure Functions、WindowsPC に Azure Functions Core Tools を入れて、関数の実行テストが行えるような状況を構築していました。

`func new` コマンドにより、Pythonのタイマートリガーテンプレートを選択し、作成されたテンプレートが以下です。

### Python TimerTrigger テンプレート例
```python
@app.schedule(schedule="0 0 15 19 * *", arg_name="myTimer", run_on_startup=True,
              use_monitor=False) 
def timer_trigger_sample(myTimer: func.TimerRequest) -> None:
    if myTimer.past_due:
        logging.info('The timer is past due!')
    logging.info('Python timer trigger function executed')
```

見ていただくと、`run_on_startup=True`になっているのです。

これを`run_on_startup=False`に直すことで、推奨されている設定になるわけですが、気づかずに大規模な処理をデプロイしてしまうと、予期せぬ実行により多額の請求に繋がる可能性もあります。

私の場合、従量課金ではなく検証に用意しているAppServiceプラン上で動作させていたため、影響はありませんでした。

デフォルトで`False`となっているというのも目にしたので、環境によるのかもしれませんが、私の環境上ではこのようにテンプレートが出力されたため、どなたかの役に立てば幸いです。

## ご参考
https://blog.kokoni.jp/2020-01-04-145254/
watsonx.aiでより良い結果を生成させるための方法 

## はじめに
近年話題となる生成AIは、文章生成、要約やコード生成など様々なユースケースに応用できますが、たまに生成された結果が理想的ではなくて、チューニングする必要があります。

ここでwatsonx.aiのプロンプトラボで生成された結果を改善するいくつかの方法をご紹介させていただきます。


## 日本語要約の例
まず、watsonx.aiのプロンプトラボで日本語の文章を要約してみましょう。
ここで結果が正常に生成されましたが、要約結果の質が高くないです。
![生成ー要約１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/da270633-891e-8154-909e-2b6ab21d5998.png)

ここで、要約結果を改善するための三つの方法を紹介します。

## パラメーター調整
生成された結果が理想的ではなくて、よりよくするために、パラメーターを指定して、生成された出力内容を調整することが可能です。

調整方法としては、watsonx.aiプロンプトラボのUIで右上の「モデル　パラメータ」を押すと、調整可能なパラメーターがUIで表示されます。
![パラメータ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/d92bdc82-acc4-a809-b70f-314427c133d5.png)

ここでよく使われているパラメーターを説明します。![スクリーンショット 2023-12-22 17.21.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/3cb6ad43-af7a-bb36-4989-d35b85cf4084.png)

各パラメータについてもっと詳しく知りたい場合、

こちらのリンクをご参考してください：

https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-model-parameters.html?context=wx&locale=ja&audience=wdp


先ほどの要約例については、最大トークン数を200にし、反復ペナルティを1.2にし、デコードをGreedyにしたら、結果が良くなったことがわかりました。

![生成-パラメータ調整.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/175f6fe1-71c1-2a52-8a28-22b7e1aa0d41.png)

このように、パラメーターの設定を通じて、

モデルが出力を生成する方法を制御して、出力結果の調整が可能です。

## プロンプトエンジニアリング
まず、言葉の定義について説明します。

プロンプトとは、ユーザーがAIモデルに入力する質問や指示のテキストを指します。

そして、プロンプトエンジニアリングとはAIサービスに望ましい結果を得るために、適切なプロンプトを設計するスキルです。

ここで、プロンプトエンジニアリングのいくつかのテクニックをご紹介させていただきます。

### Zero-shot＆One-shot&Few-shotsプロンプティング

まずは、Zero-shotプロンプティング、One-shotプロンプティングとFew-shotsプロンプティングを紹介します。

Zero-shotプロンプティングとは、モデルが具体的な方法の例を示さずにタスクを完了するプロンプト手法です。

One-shotプロンプティングは、1つだけ例を与えてあげるプロンプト手法です。

Few-shotsプロンプティングは、複数の例を与えてあげるプロンプト手法です。

watsonx.aiでより良い結果を生成させるために、いくつかの具体例を与えて、AIに例のフォーマットや文脈を学習させて、結果を改善することが可能です。

例の追加方法としては、プロンプトラボの画面で、例（オプション）のところで具体例を追加することが可能です。

![oneshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/04364343-67ac-1be1-b044-8c3b6d771e9d.png)


先の要約例で二つの具体例を追加することで、試してみました。![few-shots.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638137/d41d44a0-ac85-7d15-b7f2-f971e7e93f27.png)

few-shotsプロンプティングにしたら、要約結果も箇条書きで生成されました。

このように、具体例を追加することで、生成結果をよくすることもできます。

## プロンプト作成Tips


その他にプロンプトを作成するときのいくつかのTipsを紹介します。

プロンプトをできるだけ詳細に作成した方がいいです。

・命令に、LLMに具体的な役割を追加することで結果を良くすることも可能です。

例としては、「あたなは要約に非常に優れているエージェントです」みたいな記載ができれば、結果も改善できます。

・命令を明確に具体的に書くこと

例えば、「一行当たり100文字ぐらいで要約してください」などの記載を加えれば、結果も良くなります。

## まとめ


今回は、watsonx.aiのプロンプトラボでの要約を例として説明しました。いつくかの方法を工夫することで、要約の結果をよくすることを示しました。

以上でありがとうございます。


## 参考資料


https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-model-parameters.html?context=wx&locale=ja&audience=wdp

https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-prompt-tips.html?context=wx&audience=wdp

https://eu-de.dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-prompt-tips.html?context=wx&audience=wdp

https://www.sra-tohoku.co.jp/it-daily-brief-0829?fbclid=IwAR2Zt5ZC0HiM664sXJ9SaVmjppPjc8aMsdc0XHTenFc7nGzJrKfq3iFWZN4

https://www.ibm.com/blogs/solutions/jp-ja/what-is-alignment-ai/

https://www.ibm.com/blogs/solutions/jp-ja/future-of-ai-alliance/