OmniAICreator/Qiita-1.07M huggingface dataset:
【Day 13】BFF 環境構築 - NestJS GraphQL

## はじめに

![スライド14.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/05992929-c734-1c77-3585-d6cdac6e1a9c.png)

---

**2023年アドベントカレンダー13日目**です。

https://qiita.com/advent-calendar/2023/life-sync-app

今回はBFFの開発を始めるために環境構築をしていきます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/e36b1dc4-814d-49b3-43bc-61c9f978400f.png)

## BFF

### BFFとは

> From ChatGPT
> BFF（Backend For Frontend）は、ソフトウェア開発のアーキテクチャパターンの一つです。このパターンでは、フロントエンド（例えば、ウェブアプリケーションやモバイルアプリ）専用のバックエンドを提供します。これにより、フロントエンドのニーズに特化したAPIを実現し、フロントエンドの開発とメンテナンスが容易になります。

フロントエンドのために存在し、バックエンドとの仲介をするものと理解しています。

今回はBFFをGraphQLを利用して構築していきたいと思います。

### GraphQLを採用した理由

主な採用した理由は２つです

1. クライアントがリクエストしたデータのみを返す
1. 少ないリクエストで複数のリソースにアクセスできる

::::note info
#### 1. クライアントがリクエストしたデータのみを返す

GraphQLはクライアントが要求したデータだけを取得できるように設計されていて、過剰なデータ取得を防止できます

#### 2. 少ないリクエストで複数のリソースにアクセスできる

クエリライクで複数リクエストなしに情報を取得できます。
これによりRESTful APIにありがちな問題である「情報にアクセスするために必要なエンドポイント多くなる」問題を防ぐことができます。

参考) https://qiita.com/hirac/items/d4b470afa14e1741ab99
:::

ではNestJS上にGraphQLのサーバーを作成していきます

## NestJS

### NestJSとは

NestJSは、Node.js上で動作するバックエンド開発のフレームワークです。

https://nestjs.com/

https://techtionary.jp/3534

### 環境構築！

実は公式のリファレンスにGraphQL + TypeScriptの構築方法が乗っているので、それに則って進めていきます

https://docs.nestjs.com/graphql/quick-start

まずは`NestJS`環境をinstallしていきます

```bash
npm i -g @nestjs/cli
nest new bff

? Which package manager would you ❤️  to use? npm
CREATE bff/.eslintrc.js
...

✔ Installation in progress... ☕

🚀  Successfully created project bff
👉  Get started with the following commands:

$ cd bff
$ npm run start
                                         
                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.
                                         
                                         
               🍷  Donate: https://opencollective.com/nest
```

次にNestJSでGraphQLサーバーを立てるために必要なものをそろえていきます

```bash
cd bff
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
```

これで準備は整いました

次回はテストで使うWireMockのセットアップをしていきます！
【Unity】自作例外クラスでパターンごとに例外を検知しつつ処理は止めない

# やりたいこと
業務アプリなどでは自作のExceptionクラスを作成し、それをthrowすることで呼び出し元が例外に応じて何か処理する（対応するエラーメッセージを表示する）などがあります。
（都度都度エラー処理をそこに細かく書かなくても、正常系の処理がスッキリするから）

Unityでも自作クラスで、「攻撃対象がいなかったらExceptionA」「行動できない状態だったらExceptionB」を投げよう、と思い同じように自作クラスのthrow、tyr-catchをさせようとしました。

ただし、呼び出し元へthrowしてしまうと、Editor上ではcatchの度に処理が止まってしまい、止めるほどの例外のつもりではないのになぁと悩んでいました。

今回は自作クラスをthrowする代わりに、子メソッドで戻り値を返し、最終的な判断を呼び出し元で処理する方法を作成してみました。
※拡張性などはあまり考慮していません・・・

# 実装


``` C#
///呼び出される方
    public override async UniTask<object> ActionSkill(List<BattleUnit> party, List<BattleUnit> enemyParty, BattleUnit owner)
    {
        int damage = await base.ConvertDataTable(party, enemyParty, owner);
        damage = owner.CalcBaseDamage(damage);

        if (!(enemyParty.Count > owner.PartyIndex && owner.PartyIndex >= 0))
            return new TargetNotAwakenException(damage);

        if (!(enemyParty[owner.PartyIndex] != null && enemyParty[owner.PartyIndex].isAwaken()))
            return new TargetNotAwakenException(damage);


        Debug.Log($"ダメージ処理：{actionName}=>{damage}");
        enemyParty[owner.PartyIndex].GetDamage(damage);
        return null;
    }

```

親子継承関係で、共通してasyncになっていますがいったん細かいところは無視します。
このメソッドでは、戻り値にとりあえず「object」を返すよう定義しています。
※処理の内容によっては、intだったり自作例外クラスだったりするため。

そして何もなければ共通して「null」を返すようにします。

``` C#
///呼び出し元
 object result = await actions[i].ActionSkill(party, enemy, owner);
 if (result != null)
 {
     if(result.GetType() == typeof(TargetNotAwakenException))
     {
         UIManager.Instance.OpenDialog1("エラー", (result as TargetNotAwakenException).Message);
         owner!.CalcScore((int)(result as TargetNotAwakenException).scoreEffect);
     }
 }
```

「if(result.GetType() == typeof(TargetNotAwakenException))」では、「攻撃対象がいなかったので戦闘計算するまえに例外クラスを返す、スコアの加算処理だけ」を行ってます。
スキルアクションとして、アクションの処理の中で本来はダメージ処理までするのですが、対象がいなかった場合は共通して呼び出し元にスコア計算だけなげてしまうという方法です。

苦肉の策です。
Crystalでウィンドウを表示する (gtk4.cr)

## はじめに

さあ、アドベントカレンダーなのですが、今日もネタがありません。
なので、ものすごく薄いネタでいこうと思います。
[gtk4.cr](https://github.com/hugopl/gtk4.cr) を使ってウィンドウを表示するのをやります。

![Screenshot from 2023-12-09 23-03-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/b7bf5d26-9aae-f13a-da03-b75df48f30d9.png)![Screenshot from 2023-12-09 23-03-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/0e25a76b-1ec4-c4e1-7c64-2b8396410781.png)![Screenshot from 2023-12-09 23-03-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/4d1cb6cd-67d0-9337-af51-be6df93a2a77.png)


## GTK4

まずはプロジェクトを作ります。

```sh
crystal init app piyo
cd piyo
```

設定ファイル `shards.yml` を開いて、

```
vi shards.yml
```

依存 `gtk4` を追加します。

```diff
+ dependencies:
+   gtk4:
+    github: hugopl/gtk4.cr
```

終わったら依存 `gtk4` をインストールしましょう。

```sh
shards install
```

それではコードを編集していきます。

```
vi src/piyo.cr
```

`module Piyo` とか `VERSION` とか書いてありますが、今回は全部消して次のように書きました。

```cr
require "gtk4"

app = Gtk::Application.new("hello.example.com", Gio::ApplicationFlags::None)

app.activate_signal.connect do
  window = Gtk::ApplicationWindow.new(app)
  window.title = "🔮"
  window.set_default_size(200, 200)

  button = Gtk::Button.new_with_label("クリスタルの世界へ\nようこそ！！")
  button.clicked_signal.connect do
    button.label = "じゃんけんしましょう！\n\n" \
                   "#{["グー　✊", "チョキ　✌", "パー　🖐"].sample}"
  end
  window.child = button
  window.present
end

exit(app.run)

```

ここで、さっそく

```
shards build
```

とすると、必ず次のようなエラーメッセージが出るので、

```
Error target piyo failed to compile:
Showing last frame. Use --error-trace for full trace.

In lib/gtk4/src/gtk4.cr:2:11

 2 | GICrystal.require("Gtk", "4.0")
               ^------
Error: Bindings for Gtk-4.0 not yet generated, run ./bin/gi-crystal first.
```

言われるがままに `./bin/gi-crystal` を実行しましょう。

```
bin/gi-crystal
```

これで `shards build` が通るようになります。

```
shards build
```

これで実行できます。

```
bin/piyo
```

テキトウに作っているので、あまりゲームになっている感じがしませんが、とりあえずクリックするたびにランダムで、グーチョキパーが表示されます。はい、Windowが表示されました。

![Screenshot from 2023-12-09 23-03-28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/b7bf5d26-9aae-f13a-da03-b75df48f30d9.png)![Screenshot from 2023-12-09 23-03-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/0e25a76b-1ec4-c4e1-7c64-2b8396410781.png)![Screenshot from 2023-12-09 23-03-58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144608/4d1cb6cd-67d0-9337-af51-be6df93a2a77.png)

GUIが表示されると、私は子供の頃憧れていた「ソフトウェア」を作れるようになったんだなぁという気持ちになります（個人差があります）。

こんな感じでとても簡単にできるので試してみてください。MacOSを使っている場合でも、homebrewなどでgtk4をインストールすればできるはずです。

この記事は以上です。
画像生成の元になったダジャレ当てクイズを作った


この記事は[クソアプリ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/kuso-app) の10日目の記事です。

# 概要

この1年で世の中はLLMによって大きく変化しました。
ですが、まだLLMでも代替するのが難しいものが世界には存在するのではないかと私は考えました。

それは**親父ギャグのセンス**です！！

試しにChatGPTにダジャレを生成してもらいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521898/b887243b-f89d-862a-0a47-0c633c9d615b.png)

......なかなかのセンスだと思います。
（私がケチって今でもGPT3.5を使っているからかもしれませんが）

ということで、AIが古くから伝わる親父ギャグまでカバーできるのかを確かめられるようにしました。

# 作ったもの

完成品がこちらになります。

https://joke-ai-quiz-frappuccino2316.vercel.app/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521898/0ca070e4-bcc5-9d5f-5798-a11f296df05d.png)

見ての通り、奇妙な画像とフォームだけが存在します。
表示されている画像は、MicrosoftのBing Image Creatorを使って誰もが知っている親父ギャグをもとに画像生成したものです。

これは皆さん一目見てわかるでしょう。
どう見ても「**アルミ缶の上にあるミカン**」ですね！！

テキストボックスに「アルミ缶」と「あるミカン」を入力すると、、、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521898/64beb7e6-9a20-c65f-debf-882643c3108a.png)

見事、生成AIも鉄板の親父ギャグはきちんとカバーしているようです！

しかし画像パターンはいくつか用意しており、いくつかはぱっと見ではわかりにくいものもいくつかありました。
ぜひ全ての親父ギャグをその目で確かめてみてください！
（なお正誤判定は手動で想定解を用意して比較しているので、正解にならないときは表記を変えてみるなど試してみてください）

# やってみた感想

親父ギャグをもとに画像生成を試してみた結果、上手く表現できてるなーというものもあれば、何だこれ？という結ものもありました。
きっと探したら芸人AIとかすでに出てきているのかな？とか考えたり、将来どうなっていくのか思いを馳せました。

そして仕事でバタバタして一昨日までネタを全く考えられていなかったのですが、ChatGPTに何度も助けてもらいながら何とか突貫でまとめることができました。

便利な世の中になってLLM様々です。

# まとめ

来年はもうちょっと時間かけてもっといいもの作りたい。
[Go]パート3

# はじめに
訳あってGo言語を勉強することになりました。
完全に自分のメモ用です。
馴染みのない書き方を中心にメモしていきます。

## if文

```go:main.go
package main

import "fmt"

func main() {

	var hoge string = "apple"

	if hoge == "apple" {
		fmt.Println("○")
	} else {
		fmt.Println("×")
	}

}

```
結果
```
○
```
条件部分に()を付けなくて良いです。

## Switch文
```go:main.go
package main

import "fmt"

func main() {

	var hoge string = "apple"

	switch hoge {
	case "apple":
		fallthrough
	case "coffee":
		fmt.Println("りんご")
		fmt.Println("コーヒー")
	default:
		fmt.Println("その他")
	}
 
}

```
結果
```
りんご
コーヒー
```
breakがありません。
代わりにfallthroughを付けることでbreakが無いこととして扱うことができます。

```go:main.go
package main

import "fmt"

func main() {

	var hoge bool = false
	var piyo bool = false

	switch {
	case hoge:
		fmt.Println("hoge")
	case piyo:
		fmt.Println("piyo")
	default:
		fmt.Println("other")
	}

}

```

結果
```
other
```

caseに条件式を書くことができました！

## for文

```go:main.go
package main

import "fmt"

func main() {

	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}

}

```
結果
```
0
1
2
3
4
5
6
7
8
9
```

これもまた、()が不要です。
```go:main.go
package main

import "fmt"

func main() {

	arr := [...]string{"hoge", "piyo", "poyo"}
	for _, val := range arr {
		fmt.Println(val)
	}

}

```
結果
```
hoge
piyo
poyo
```

配列やスライスをぶん回したいときに使いましょう。

以上。

https://qiita.com/mojapico/items/4f2ce6829d3bcaf2011f

https://qiita.com/mojapico/items/2cc87cf8eca6a804866a

https://qiita.com/mojapico/items/019204b2aa77b15659cd
VitePressの環境構築をしてGitHubPagesへ展開する

## この記事は何

VitePress環境構築ログをまとめたものです。

## まずは成果物

[https://github.com/watame/vite_press_demo](https://github.com/watame/vite_press_demo/tree/main)

できること

- 下記環境でVitePressがホットリロード状態で起動
    - ローカルのnode
    - Docker
- GitHubActionsを利用したGitHubPagesへのドキュメント展開

## VitePressって？

[VitePress Vite & Vue Powered Static Site Generator](https://vitepress.dev/)

- Vueで作成されている静的サイト生成フレームワーク

個人的な推しポイント

- Markdownのファイルを元にHTMLコンテンツを作ってくれる
    - 文書管理に利用したいので、内容にだけフォーカスできるのがGood :thumbsup: 
- GitHubPagesに簡単にデプロイできる
    - GitHubでのPRレビューをしてOKだったら、即座に文書として確認できる環境を構築できるのがGood :thumbsup: 

## なぜ使いたいと思ったの？

[ドキュメント管理を制する 陳腐化を防ぐための実践事例 Lunch LT](https://findy.connpass.com/event/302508/) 内で紹介されていて、Markdownでサクッとドキュメントかけるのが便利そうだと思ったから。

資料紹介のPost

- https://x.com/wako00sa/status/1732242359193317639?s=20
- 発表者 Yuhei FUJITA さんの資料
    - https://github.com/YuheiFUJITA/slide-20231206-findy?tab=readme-ov-file
    - VitePressのサンプルも作成いただいているので、最高にわかりやすくて親切な資料です

## 環境構築のおはなし
VitePressの構築 / GitHubPages展開 いずれも公式ドキュメントのページを見ればすぐにできました。
（公式ドキュメントが丁寧すぎて震えました :sob:  ）

一部ドキュメントを探すのに苦労した箇所があったので、ちょっとだけメモを残します。

### GitHubPagesへの展開
GitHubPagesへの展開自体は [GitHub Pages](https://vitepress.dev/guide/deploy#github-pages) を本当にそのまんまやればできます（すごい）****[](https://vitepress.dev/guide/deploy#github-pages)****

ただ、GitHubPagesではレポジトリの名称でサブパスが区切られるため、追加で `base` 設定を行う必要があります。

- 参考：[site-config base](https://vitepress.dev/reference/site-config#base)

VitePressの `./vitepress/config.mts` に `base` 設定を追加し、レポジトリ名を設定することで正常に表示することができました。
```typescript
import { defineConfig } from 'vitepress'

// https://vitepress.dev/reference/site-config
export default defineConfig({
  base: '/GitHubPagesを展開するレポジトリ名/', // <- これを追加しないとGitHubPagesで正常に表示されない
  title: "VitePress Demo",
  description: "A VitePress Site"
  // .... 省略 ....
})
```
GCP Duet AI for Google Cloud機能と実装

## GCPのDuet AI for Google Cloudの概要

GCPのDuet AI for Google Cloudは、Google Cloud上で提供されるAIを活用したデュエット型のサービスです。ユーザーは、自身のアプリケーションにDuet AIを組み込むことで、音声やテキストなどの入力に対して自然な対話応答を行うAIチャットボットを作成することができます。

## 機能

Duet AI for Google Cloudには以下のような機能が備わっています。

1. デュエット型AI: Duet AIは、対話の流れを理解し、質問に応じた自然な回答を生成することができます。AIモデルは、大量のデータを学習しており、高い応答品質を実現しています。

2. マルチ言語対応: Duet AIは、複数の言語に対応しています。ユーザーは異なる言語での対話インターフェースを簡単に作成することができます。

3. カスタムトレーニング: ユーザーは、独自のデータセットを使用してDuet AIをカスタムトレーニングすることができます。これにより、特定のドメインや業務に適したAIモデルを作成することが可能です。

4. 確かなプライバシー: Duet AIは、高いプライバシー保護を提供します。ユーザーのデータは厳格に管理され、不正アクセスやデータ漏洩のリスクを最小限に抑えます。

## サンプルコード

### Java

```java
import com.google.cloud.duet.DuetAI;
import com.google.cloud.duet.DuetAIOptions;
import com.google.cloud.duet.DuetAPIException;

public class DuetAISample {

  public static void main(String[] args) throws DuetAPIException {
    // Create DuetAI client with project ID
    DuetAI duetAi = DuetAI.create(DuetAIOptions.newBuilder()
        .setProjectId("your-project-id")
        .build());

    // Perform a conversation with Duet AI
    String response = duetAi.converse("こんにちは");
    System.out.println("Duet AI response: " + response);
  }
}
```

### Go

```go
package main

import (
	"context"
	"log"

	duetai "cloud.google.com/go/duetai"
)

func main() {
	ctx := context.Background()

	// Create DuetAI client
	client, err := duetai.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}

	// Perform a conversation with Duet AI
	response, err := client.Converse(ctx, "こんにちは")
	if err != nil {
		log.Fatal(err)
	}

	log.Println("Duet AI response:", response)
}
```

### C#

```csharp
using Google.Cloud.DuetAI.V1;

public class DuetAISample
{
    public static void Main(string[] args)
    {
        // Create DuetAI client
        DuetAIClient client = DuetAIClient.Create();

        // Perform a conversation with Duet AI
        ConverseResponse response = client.Converse(new ConverseRequest
        {
            Query = "こんにちは"
        });

        System.Console.WriteLine("Duet AI response: " + response.Response);
    }
}
```

以上は、Java、Go、C#のそれぞれでDuet AI for Google Cloudを使用するための簡単なサンプルコードです。詳細な使い方やパラメータの設定方法については、公式ドキュメントをご参照ください。
sqlboilerの使い方

# sqlboiler
アドベントカレンダー9日目は`sqlboiler`について。
sqlboilerはGoにおけるORMです。

https://github.com/volatiletech/sqlboiler

GoのORMhはGORMやsqlxなどがありますが、sqlboilerの特徴としてデータベースファーストであることが挙げられます。

## インストール
```bash
go install github.com/volatiletech/sqlboiler/v4@latest
go install github.com/volatiletech/sqlboiler/v4/drivers/sqlboiler-mysql@latest
```

## sqlboiler設定ファイル
ここで用いている値は先日のアドベントカレンダーGoでsqlを扱うのものに基づいています。

https://qiita.com/shuyaeer/items/8911c8ea25f4208f9cee

```toml
[mysql]
  dbname="golang"
  host="mysql"
  port=3306
  user="golang"
  pass="golang"
  sslmode="false"
```

## モデル生成

```bash
sqlboiler mysql 
```

生成されたファイルを見てみます。
```bash
root@8f93343dfdb9:/app# ls models
boil_main_test.go     boil_suites_test.go  boil_view_names.go	 mysql_upsert.go
boil_queries.go       boil_table_names.go  mysql_main_test.go	 users.go
boil_queries_test.go  boil_types.go	   mysql_suites_test.go  users_test.go
```

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

import (
	"context"
	"database/sql"
	"fmt"
	"log"

	"github.com/shuyaeer/learn-go/models"

	_ "github.com/go-sql-driver/mysql"
	"github.com/volatiletech/sqlboiler/boil"
)

func main() {
  // データベースへの接続設定
  db, err := sql.Open("mysql", "golang:golang@tcp(mysql:3306)/golang?parseTime=true")
  if err != nil {
      log.Fatal(err)
  }
  defer db.Close()

  // データベース接続のテスト
  if err = db.Ping(); err != nil {
      log.Fatal(err)
  }

  // sqlboiler用の設定
  boil.SetDB(db)

  // usersテーブルから全ユーザーを取得
  users, err := models.Users().All(context.Background(), db)
  if err != nil {
      log.Fatal(err)
  }

  // 取得したユーザー情報を表示
  for _, user := range users {
      fmt.Printf("User: %+v\n", user)
  }
}
```

### 実行
```
root@8f93343dfdb9:/app# go run app/day9/main.go
User: &{ID:1 Username:test Password:test Email:{String:test Valid:true} CreatedAt:2023-12-07 14:06:11 +0000 UTC R:<nil> L:{}}
User: &{ID:2 Username:test Password:test Email:{String:test Valid:true} CreatedAt:2023-12-07 14:06:22 +0000 UTC R:<nil> L:{}}
User: &{ID:3 Username:test Password:test Email:{String:test Valid:true} CreatedAt:2023-12-07 14:06:22 +0000 UTC R:<nil> L:{}}
```
よさそうです。
ORMなだけあって、DBの取り扱いはとても楽ですね。
migrationは別途用意する必要がありますが、既にDBのスキーマが存在する場合等に実力を発揮しそうです。
JavaScript学習の記録

# はじめに
先週からJavaScriptの学習に入りました．新たに学んだことを箇条書きでアウトプットしていきます．

# JavaScript学習内容
- const変数は，上書きも再宣言もできないが，中身がオブジェクトや配列の場合は，プロパティ・要素の書き換えや追加が可能．
- 「**分割代入**」を使うことで，オブジェクトのプロパティや配列の要素を取り出す際に，記述量を少なくすることができる．
```javascript
const myprofile = {name:"taro", age:30};
const {name ,age} = myprofile; /**分割代入を使用*/
const message = `名前は${name}です．年齢は${age}です`;
console.log(message);
```
実行結果
```
名前はtaroです．年齢は30です
```
毎回，`オブジェクト.プロパティ`という書き方をしなくて済むようになる．
- 「**デフォルト値***」は，undefinedと出力されるのを防ぐために，引数が渡されなかった時に表示させるようにする値．
```javascript
const myprofile{age:31};
const {age, name="guest"}=myprofile; /**デフォルト値の設定*/
console.log(age);
console.log(name);
```
実行結果
```
31
guest
```
引数`name`にデフォルト値`guest`を設定したことで，`undefined`ではなく`guest`が出力されている．

# VSCodeでJavaScript単体で実行させるために
JavaScriptはブラウザ上で動作するプログラミング言語のため，通常は単体で実行することができない（HTMLなどとセットになる）．
そこで「**Node.js**」という，JavaScriptを実行するためのアプリケーション（実行環境）をインストールすることで，VSCode上でJavaScript単体で実行できるようになる．

https://qiita.com/non_cal/items/a8fee0b7ad96e67713eb
【SwiftUI】メニューにPickerを横並びで配置する（iOS17）

# はじめに
iOS17からPickerに`palette`というスタイルが追加されました。
これはメニュー内で使用すると効果が出るようです。

https://qiita.com/SNQ-2001/items/97f0f379a696f0ec8ecc

以前、ボタンを横並びで配置できる方法を紹介しました。
似ていますが、今回はボタンではなくPickerです。

# サンプルアプリ
![Simulator Screen Recording - iPhone 15 - 2023-12-09 at 22.37.43.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/e0fcc15b-3d4b-6f00-4071-a3fea6f77df1.gif)

# 実装
```swift
import SwiftUI

enum Reaction: Identifiable, CaseIterable {
    case heart, star, bookmark

    var id: Self { self }
    
    var tint: Color {
        return switch self {
        case .heart: .red
        case .star: .yellow
        case .bookmark: .blue
        }
    }
}

struct ContentView: View {
    @State private var selection: Reaction = .heart

    var body: some View {
        Menu {
            Picker("", selection: $selection) {
                Label("heart", systemImage: "heart")
                    .tag(Reaction.heart)
                    .tint(Reaction.heart.tint)
                Label("star", systemImage: "star")
                    .tag(Reaction.star)
                    .tint(Reaction.star.tint)
                Label("bookmark", systemImage: "bookmark")
                    .tag(Reaction.bookmark)
                    .tint(Reaction.bookmark.tint)
            }
            .pickerStyle(.palette)
            .paletteSelectionEffect(.symbolVariant(.fill))
        } label: {
            Text("リアクション")
        }
    }
}
```

# おわり
結構実用性ありそうですね

# 公式ドキュメント
https://developer.apple.com/documentation/SwiftUI/PickerStyle/palette
Goのinternalパッケージを使ったPackage by Featureを意識した構成

この記事は カオナビ Advent Calendar 2023(シーズン2) 11日目です。

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

---

最近はinternalパッケージを使用した開発をしているので、どのような構成でやっているかを書きます。

## internalパッケージ

Goでは先頭を大文字にした関数・interface等はどこからでも参照できます。
ただモジュール開発などをしていると内部で共通化のためにパッケージを切り出したいが、外部から参照はさせたくないということはよくあります。

そこで以下のようにinternalパッケージを使用することで、配下パッケージの実装を外部パッケージから参照できなくすることができます。

以下の構成の場合は`a/b/b.go`からは`a/b/internal`を参照できますが、
`a/a.go`からはinternalパッケージ配下は参照できなくなります。

```
a/
├ b/
│  ├ internal/
│  │  ├ d/
│  │  │  └ d.go
│  │  └ c.go
│  └ b.go
└ a.go
```

## 過去のプロジェクト構成

当初はPackage by Layerと言われるパターンになっており
以下のようなcontroller等のレイヤーでパッケージを切って開発が行われていました。

```
./
├ controller/
│  ├ company.go
│  └ user.go
├ model/
│  ├ company.go
│  └ user.go
└ service/
   ├ company.go
   └ user.go
```

この構成は極端ですがパッケージは以下に全ファイルが配置されるので、時間経過とともに全体の見通しが悪くなってしまいます。

以下のように更に機能ごとのパッケージを作成して進めることもできますが、
これだとパッケージ名の重複が頻発して結構開発しにくいなーと感じました。

```
./
├ controller/
│  ├ company/
│  │  └ create.go
│  └ user/
│     ├ create.go
│     └ update.go
├ model/
│  ├ company.go
│  └ user.go
└ service/
   ├ company/
   │  └ create.go
   └ user/
      ├ create.go
      └ update.go
```

## internalパッケージを使った構成

以下がinternalパッケージを使った構成イメージになります。

```
./
├ company/
│  ├ public.go
│  └ internal/
│     ├ controller/
│     │  └ controller.go
│     ├ model/
│     │  └ company.go
│     └ service/
│        └ service.go
├ core/
│  └ io
│     └ io.go
└ user/
   ├ public.go
   └ internal/
      ├ controller/
      │  └ controller.go
      ├ model/
      │  ├ company.go
      │  └ user.go
      └ service/
         └ service.go
```

各機能単位でパッケージを作成し、直下にinternalパッケージを配置することで
各機能の内部実装を他のパッケージから隠蔽して開発ができます。

この構成で例えばAPIを作る場合、最低でもControllerのinterfaceはパッケージ外に公開する必要があるため、Controllerのinterfaceを返す関数をpublic.goに実装します。

もし機能間で共通利用したい関数があればcoreのように別パッケージに切り出します。

### メリット

#### 各機能が独立

companyとuserの実装は独立するので、各internalパッケージ内の変更は他パッケージに影響を与える事はありません。

#### interface名等を簡潔にできる（かも）

レイヤー単位でパッケージを切る場合、`service.CampanyCreate` や `service.UserCreate` のように
重複回避と、機能を識別のためprefix(もしくはsuffix)をつける必要があると思います。

internalを使用することで各機能が独立するため、他のパッケージとの重複を考慮する必要がないため`service.Create`のような命名で良くなります。

#### IDEで余計なものが補完されない

userの機能実装中はuserから使用可能な実装のみが候補に上がるので快適:open_hands:

#### IDEでディレクトリツリーのスクロールが少なく済む

レイヤーごとに分けられていると、開発が進んでファイル数が多くなった状態でcontrollerからserviceパッケージに移動するときにそこそこスクロールが必要な場合があります。
基本的にinternal配下に必要なコードがまとまっているので、そんなにスクロールしなくてもコードの全体を見通せます:open_hands:

### デメリット

#### 各パッケージに重複コードが生まれる

例えばcampanyの存在チェック処理のようなものがcanmapy, userの両パッケージに実装されるなど。

これに関しては以下のように進めています。

- パッケージ間でのコード重複は許容する
- ある程度進めながら、本当に共通にすべきと判断したら共通化する

#### パッケージ間で実装方法が異なる場合がある

パッケージの切り方や、interfaceの切り方など、実装者によって多少ブレが発生する場合がある。

これに関しては以下のように進めています。

- パッケージに閉じていれば多少の違いは許容する
- パッケージに閉じていれば程度コードの全体把握はできる

---

https://corp.kaonavi.jp/recruit/recruitment/
生成AI活用に伴うリスクを軽減するTips

# はじめに
さて今年の2記事目の投稿になります渡邊です。(諸般の事情により今年はいっぱい登場します・・・！)
皆さん、生成AIは活用されていますか？私は普段コーディングをする人ではないので、アイデアを出したい時や言葉が浮かんでこない時にChatGPTさん(正確に言うと、私が聞きたいことをChatGPTさんに聞いてくれる夫)に助けてもらう、というのが主な生成AIとのお付き合いです。先日は電車の中で、隣に座った人が「退職しますメールへの返信」をChatGPTさんに書いて貰っているのを目撃しました。「もっと泣ける感じで」とかいうリクエストにもちゃんと答えてくれていました。素晴らしい！

# 生成AIを活用する際に気を付けたいリスク
生成AIにより業務効率が劇的に向上すると言われていますが、リスクについても注意したいところ。生成AIを活用する際に気を付けたいリスクを以下に纏めてみました。

:::note warn
AI倫理の問題
バイアスされたデータを学習した生成AIにより、生成されたデータに差別的な表現や極端な思想などが含まれてしまうことがあります。
:::
:::note warn
虚偽情報が混入する問題
AIが誤ったデータを生成してしまうことがあります。[Qiitaのコミュニティガイドライン](https://help.qiita.com/ja/articles/qiita-community-guideline#ai)にも、今年から「AIが生成した内容は正確性を確かめよう」と書かれていますね。
:::
:::note warn
著作権やライセンス侵害の問題
コピーレフト系ライセンスのOSSのソースコードを学習した生成AIが、OSSのコード(またはその一部)を「コピー」してコードを生成するかもしれません。
:::
OSSコンプライアンスについて活動している私としては、「ライセンス侵害」が気になります！！


# マイクロソフト社の Copilot Copyright Commitment
マイクロソフト社は、著作権侵害により訴えられてしまうかもしれないというユーザーの心配を払拭するため、「[Copilot Copyright Commitment](https://news.microsoft.com/ja-jp/2023/09/12/230912-copilot-copyright-commitment-ai-legal-concerns/)」の中でこのように述べています。
> (略) 第三者がマイクロソフトの Copilot または Copilot が生成する出力結果を使用した法人のお客様を著作権侵害で訴えた場合、お客様が製品に組み込まれたガードレールとコンテンツフィルターを使用しているという条件の下で、マイクロソフトはお客様を弁護し、訴訟の結果生じた不利な判決または和解により課された金額を支払います。

ユーザーとしては大変ありがたい&安心できそうなステートメントではありますが、「お客様が製品に組み込まれたガードレールとコンテンツフィルターを使用しているという条件の下で」とある以上、100%なんでもカバーしてくれるという性質のものではないでしょうし、ユーザー側でも合理的な範囲内で自衛しておく必要がある、と言わざるを得ないでしょう。

# 生成AI活用に伴うリスクを軽減するTips
### Suggestions matching public codeの設定
GitHub Copilot には、GitHub のパブリックコードと一致するコードをフィルタリングする機能があります。この機能が有効になっている場合、周囲の約150文字分のコード候補が、GitHub上のパブリックコードに一致していないかをチェックしてくれます。このフィルタリング機能はSettingから有効 / 無効を切り替えることができます。意図しないライセンスのOSSの一部が混入するリスクを減らすことができるかもしれませんので、ライセンス違反のリスクが心配な方は活用してみると、良いかも。

![Suggestions matching public codeの設定画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528217/ebaa52a7-ea30-376c-1c2c-1c54d23043c0.png)

### タグ付け
「自分で書いたオリジナルのコード」と「生成AIを活用して書いたコード」を明確に区別するべく、タグ付けなどしておくことで、万が一、後からAIが生成した部分のコードを置き換えなければならなくなった時に役立ちます。タグ付け以外にも、ファイル単位で分けておくという方法もありますね。

### 各種解析ツールの活用
フィルタリングを有効にしていたとしても、本当に大丈夫・・・？と心配になる場合は、AIが生成したコードをプッシュする前に、静的解析ツールを活用するのも一案です。SAST (Static Application Security Testing)ツールやSCA (Software Composition Analysis)ツールの活用により、セキュリティリスクやライセンスコンプライアンスリスクを低減することができます。

# おわりに
生成AIの活用と並行して、著作権の保護やユーザーの心理的安全を高めるような、技術面での進化が進んでいくと良いですね。これからに期待したいところです。

# English Summary
Generative AI is getting more populer. We need to understand there are many risks to use generative AI for your development. I recommend you to use Suggestions matching public code setting(only on Github Copilot), tagging, using static analysis tools to reduce your risks.
処理をロック、"Lock"

[Symfony Component Advent Calendar 2023](https://qiita.com/advent-calendar/2023/symfony-component)の12日目の記事です。


## 処理をロック、"Lock"

[Lock](https://symfony.com/doc/current/components/lock.html)は、ある処理を重複して実行させない（排他処理）ようにします。Symfony以外でも使えます。

### インストール

```shell
composer require symfony/lock
```

### 使い方

`LockFactory`オブジェクトを使り、`createLock('{ロック名}')`メソッドを実行してロックオブジェクトを取得します。'acquire()`メソッドでロックが取得できればtrueを返します。falseを返せば他で処理が実行されているということになります。

```php
use Symfony\Component\Lock\LockFactory;
use Symfony\Component\Lock\Store\SemaphoreStore;

$store = new SemaphoreStore();
$factory = new LockFactory($store); // Factory作成

$lock = $factory->createLock('sample'); // ロック

if ($lock->acquire()) { // ロックされてない
    // 排他処理したいロジック

    $lock->release(); // ロック解除
} else { // ロックされてる
    
}

```

### 有効期限

`createLock()`時にttlを渡すことで有効期限が設定できます。この際、同じく引数に`autoRelease`を設定することで、処理が終わっても自動でロックを解除しないようにできます。（通常は自動でロック解除されます）
また、`acquire(true)`と引数にtrueを渡すと、処理が終わるまで待機します。これをブロッキングと呼びます。

```php

$memcached = new \MemCached();
$memcached->addServer('localhost', 11211);
$store = new MemcachedStore($memcached);

$factory = new LockFactory($store); // Factory作成

$lock = $factory->createLock(
    'sample',
    ttl: 30, // 有効期限（秒）
    autoRelease: false // 自動で解除するか
);

if ($lock->acquire(true)) { // 別の処理でロックが解除されるまで待機
    // 排他処理
}
```

### 共有ロック

ロックは共有ロックがあり、読み込みはできるけど書き込みはできないと言った処理が可能になります。`acquireRead()`で読み込みロックを取得できます。書き込みロックにしたい場合は今まで通り`acquire()`を呼び出します。いずれもブロッキングが可能です。

```php

$lock = $factory->createLock('sample');
$lock->acquireRead(true); // 読み込みモードでロック取得。ブロッキングなので他のロックが解除されるまで待機

// 読み込み時にやりたい処理

$lock->aquire(true); // 書き込みモードに変更。同じくブロッキング

// 書き込みモードでやりたい処理

$lock->relase();

```

### ストア

ロックは`Store`と呼ばれる箇所にロック情報が作成されます。この`Store`により、できることが変わってくるのでシーンに合わせた`Store`を選択する必要があります。

| Store | Scope | Blocking | Expiring | Sharing |
|---|---|---|---|---|
| FlockStore | local | yes | no | yes |
| MemcachedStore | remote | no | yes | no |
| MongoDbStore | remote | no | yes | no |
| PdoStore | remote | no | yes | no |
| DoctrineDbalStore | remote | no | yes | no |
| PostgreSqlStore | remote | yes | no | yes |
| DoctrineDbalPostgreSqlStore | remote | yes | no | yes |
| RedisStore | remote | no | yes | yes |
| SemaphoreStore | local | yes | no | no |
| ZookeeperStore | remote | no | no | no |

※MemcachedStoreはBlockingいけると思う

## まとめ

今回は`Lock`でした。排他処理ではお世話になる機能ですが、ストアやサーバの構成などの設定を誤るとロックされない状況になるので、使う際は必ず本番環境（や同等の環境）で試してからお使いください。
Arc Browserを使ってみたら便利すぎた話


# はじめに

この記事は、Qiita Advent Calendar 9日目の記事です。
以下の記事より、すべての記事をご覧になれます。

https://qiita.com/0_terarin_0/items/0f367210e2465f7772c9

# 筆者について

- 現在高校２年生
- 水泳部マネージャー
- 生徒会長
- とある団体の代表

# 本編

ある日の話、Qiitaを見漁っていると以下の記事を見つけました。

https://qiita.com/leo_rkc/items/7db010933f2af8437285

今までFirefoxを使っていた僕にはこのおしゃれなUIとSwiftを使っているっていうのが気になっていざ使い始めてみました！なので、皆さんに紹介したいと思います。

![Screenshot 2023-12-09 at 11.29.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/4a5886f4-ef51-8b21-7819-e1aba056d089.png)

## 個人的好みポイントその1：ホームディレクトリに戻るのが楽

ピン留めしたURLのルートに戻るのがめちゃめちゃ楽で便利です。スラッシュの左のアイコンをクリックすればピン留めしたURLにすぐに戻ることができます。

![Screenshot 2023-12-09 at 11.36.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/f825a6f2-cd10-7940-adf9-126b0d0d7b62.png)

## 個人的好みポイントその2：ピクチャインピクチャが万能

確かデフォルトであったPiPの機能が超絶便利です。別のウィンドウを開いてもPiPが保持されます。

![Screenshot 2023-12-09 at 11.47.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/b879db9f-512f-ff9a-c47f-1b2eaf0c5b68.png)

## 個人的好みポイントその3：プロフィールを切り替えるのが楽

プロフィールを切り替えるのが楽です。ChromeやFirefoxのようにプロフィールごとにウィンドウを変えることもできるし、複数のプロフィールを一つのウィンドウで管理することもできます。

![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/6f0e1859-0ba3-dbca-fd12-0553ab11f20a.gif)

## 個人的好みポイントその4：全画面表示が楽ちん

全画面表示がめっちゃ楽です。また、各種ツールにアクセスするのもめっちゃ楽です。

![out2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/d7895f5e-5b01-a288-726f-ecc7ea9cc2b5.gif)

## 個人的好みポイントその5：ポップアップウィンドウ

ポップアップでリンクが開きます。これはめちゃめちゃ便利です。

![out3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/61a0cad5-8f89-0c53-696b-a4d243791fcf.gif)

## 個人的好みポイントその6：スプリッドビュー

Webサイトの分割表示がとても楽です。とても便利ですね！

![Screenshot 2023-12-09 at 17.55.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/583428/6685b201-213b-9a1e-690b-a4f9372d7d27.png)

ちなみに、いかに筆者のTwitterとThreadsをリンクをつけておきますので、フォローされる方は是非！

https://twitter.com/_0_terarin_0_

https://www.sokfi.net/@_0_terarin_0

## 個人的好みポイントその7：Chromeの拡張機能をインストールできる

当たり前っちゃ当たり前なのですが、BlinkなのでChromeの拡張機能をインストールすることができます。

https://chromewebstore.google.com



# 最後に

今回は軽く、僕が出会った中でお気に入りだったブラウザについて語りました。今はEarly Accessシステムも解除されて全員がダウンロード、利用することができますのでみなさんぜひ利用してみてください！それでは、引き続き今後のアドカレ 23 の記事をお楽しみにしてください。

https://arc.net/

https://qiita.com/0_terarin_0/items/0f367210e2465f7772c9
SlackとExcelを用いた簡易出勤管理表

## はじめに
SlackとExcelを利用して簡単な出勤管理表を作りました。
Slackに出勤と退勤と入力するだけでエクセル上に出勤・退勤時間を表示させることができます。

## 環境
M1 MacBook
python3
SlackAPI

## プログラム
```python
import os
from dotenv import load_dotenv
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from datetime import datetime
import openpyxl

load_dotenv()
slack_app_token = os.environ['SLACK_APP_TOKEN']
app = App(token=os.environ.get("SLACK_BOT_TOKEN"))

excel_file_path = '<使用するエクセルのファイルパス>'
earnings_per_hour = 1000  

def record_attendance(action, timestamp):
    workbook = openpyxl.load_workbook(excel_file_path)
    sheet = workbook.active
    row = 2
    while sheet.cell(row, 1).value is not None:
        row += 1

    if action == "punch_in":
        sheet.cell(row, 1, timestamp.strftime('%Y-%m-%d')) 
        sheet.cell(row, 2, timestamp.strftime('%H:%M'))  
    elif action == "punch_out":
        sheet.cell(row - 1, 3, timestamp.strftime('%H:%M')) 
        punch_in_time = sheet.cell(row - 1, 2).value
        punch_out_time = sheet.cell(row - 1, 3).value
        if punch_in_time and punch_out_time:
            punch_in_datetime = datetime.strptime(punch_in_time, '%H:%M')
            punch_out_datetime = datetime.strptime(punch_out_time, '%H:%M')
            work_duration = punch_out_datetime - punch_in_datetime
            sheet.cell(row - 1, 4, str(work_duration))
            work_hours = work_duration.total_seconds() / 3600
            estimated_earnings = work_hours * earnings_per_hour
            sheet.cell(row - 1, 5, round(estimated_earnings, 2))

    workbook.save(excel_file_path)

@app.message("出勤")
def message_punch_in(message, say):
    timestamp = datetime.now()
    say(f"出勤しました。あなたの出勤時間は {timestamp.strftime('%H:%M')} です。")

    record_attendance("punch_in", timestamp)

@app.message("退勤")
def message_punch_out(message, say):
    timestamp = datetime.now()
    say(f"退勤しました。あなたの退勤時間は {timestamp.strftime('%H:%M')} です。")

    record_attendance("punch_out", timestamp)

if __name__ == "__main__":
    SocketModeHandler(app, slack_app_token).start()

```

**注意：ファイルパスのコピー方法はFinder→optionキーを押ししながら使用するExcelを右クリック→「"<ファイル名>"のパス名をコピー」を左クリックする**

## まとめ
このプログラムによって日時、出勤時間、退勤時間、労働時間、推定給料などを表示させることができました。ぜひ何かの参考にしていただけると幸いです。
[Swift Regex] 正規表現で マッチ・検索・置換 など

Swiftの正規表現について調べました。
旧式の NSRegularExpression を使う方法もありますが、今回は iOS 16.0 から使用可能な **Swift Regex** を使った方法についてまとめます。

# マッチするか
文字列やRegexの `wholeMatch(of:)` メソッドを呼び出します。
```swift
let text = "18時30分"
let regex = /\d+時\d+分/

if let match = text.wholeMatch(of: regex) {
    print(match.0)
} else {
    print("マッチしなかった")
}
// [出力]
// 18時30分
```
<details><summary><b>マッチ に関する主要なメソッドを表示</b></summary>

```swift
let text = "18時30分", regex = /\d+時\d+分/
var match: Regex<Substring>.Match?
var bool: Bool

// 全体がマッチするか
match = text.wholeMatch(of: regex)

// 先頭がマッチするか
bool = text.starts(with: regex)
match = text.prefixMatch(of: regex) ?? nil
```
</details>



# 検索
文字列の `matches(of:)` メソッドを呼び出します。
```swift
let text = "現在の時刻は18時30分、ロンドンでは9時30分です。"
let regex = /\d+時\d+分/

let matches = text.matches(of: regex)

for match in matches {
    print(match.0)
}
// [出力]
// 18時30分
// 9時30分
```
<details><summary><b>検索 に関する主要なメソッドを表示</b></summary>

```swift
let text = "18時30分", regex = /\d+時\d+分/
var matches: [Regex<Substring>.Match]
var match: Regex<Substring>.Match?
var bool: Bool

// マッチするもの全て
matches = text.matches(of: regex)

// 最初にマッチするもの
match = text.firstMatch(of: regex) ?? nil

// マッチするものを含むか
bool = text.contains(regex)
```
</details>


# 置換
文字列の `replace(_:with:)` メソッドを呼び出します。
```swift
var text = "現在の時刻は18時30分、ロンドンでは9時37分です。"
let regex = /\d+時\d+分/

text.replace(regex, with: "xxx")

print(text) // 現在の時刻はxxx、ロンドンではxxxです。
```
キャプチャした値を使って置き換えることもできます。
```swift
var text = "現在の時刻は18時30分、ロンドンでは9時37分です。"
let regex = /(\d+)時(\d+)分/

text.replace(regex) { match in
    "\(match.1):\(match.2)"
}

print(text) // 現在の時刻は18:30、ロンドンでは9:37です。
```
<details><summary><b>置換 や その他 の主要なメソッドを表示</b></summary>

```swift
let text = "18時30分", regex = /\d+時\d+分/
var string: String
var subString: String.SubSequence
var subStrings: [String.SubSequence]

// 置換
string = text.replacing(regex, with: "xxx")

// マッチした前半部分を取り除く
subString = text.trimmingPrefix(regex)

// 分割
subStrings = text.split(separator: regex)
```
</details>

# Regexの生成について
この記事では `/` で囲ったリテラルで正規表現を生成しましたが、他の書き方についても別の記事でまとめたので、ぜひ読んでみてください。

https://qiita.com/Se1getsu/items/838350b82d62b6613639

# 参考
https://www.youtube.com/watch?v=bLizhJKMpzw

https://www.fuwamaki.com/article/359

https://developer.apple.com/documentation/swift/regex

https://developer.apple.com/videos/play/wwdc2022/110357/
[Swift Regex] 正規表現 Regex の生成方法３選

Swift Regex における Regex の主な生成方法について調べたので、3つ紹介します。

## リテラル
最もシンプルな生成方法は、リテラルで書くことです。
リテラルなら 正規表現の構文 や キャプチャの数 などが静的にチェックされます。
```swift
let text = "現在の時刻は18時30分です。"
let regex = /(\d+)時(\d+)分/

if let match = text.firstMatch(of: regex) {
    match.0 == "18時30分"
    match.1 == "18"
    match.2 == "30"
}
```
`(?<名前>正規表現)` という構文で、キャプチャに名前を付けることもできます。
```swift
let text = "現在の時刻は18時30分です。"
let regex = /(?<hour>\d+)時(?<minute>\d+)分/

if let match = text.firstMatch(of: regex) {
    match.0      == "18時30分"
    match.hour   == "18"
    match.minute == "30"
}
```

## 文字列から変換
文字列で生成すると、構文チェックが実行時に行われるので **`try` が必要**になります。また、キャプチャをするなら**Regexの `Output` の型**もわざわざ書く必要があるので面倒です。
「ユーザーが入力した正規表現で検索する」といった場面であれば使えそうです。
```swift
let text = "現在の時刻は18時30分です。"
let string = #"(?<hour>\d+)時(?<minute>\d+)分"#
let regex = try! Regex(string, as: (Substring, hour: Substring, minute: Substring).self)

if let match = text.firstMatch(of: regex) {
    match.0      == "18時30分"
    match.hour   == "18"
    match.minute == "30"
}
```

## Regex Builder
Regex Builderは、可読性を重視して作られた、独自の正規表現の記述方法です。正規表現の構文は複雑なので、大きくなると保守が難しくなってしまうという欠点を克服します。
Xcode上で、**リテラルで書いた正規表現を右クリックし、Refactor > Convert to Regex Builder** を押下すると、Regex Builder を使った書き方に変換できます。
```swift
import RegexBuilder

let text = "現在の時刻は18時30分です。"
let hour = Reference(Substring.self)
let minute = Reference(Substring.self)
let regex = Regex {
    Capture(as: hour) {
        OneOrMore(.digit)
    }
    "時"
    Capture(as: minute) {
        OneOrMore(.digit)
    }
    "分"
}

if let match = text.firstMatch(of: regex) {
    match.0 == "18時30分"
    match[hour] == "18"
    match[minute] == "30"
}
```
見ての通り、かなり冗長なので、簡単な正規表現であればリテラルで済ましたほうがスッキリするでしょう。

ちなみに、このブロックの中にはリテラルの正規表現を書くこともできるので、自由に併用もできます。
```swift
let regex = Regex {
    Capture(as: hour) {
        OneOrMore {
            /\d/
        }
    }
    /時\d+分/
}
```

# まとめ
どの方法にも利点・欠点があることが分かりました。目的に応じて選ぶのが大切そうです。

# 参考
https://www.youtube.com/watch?v=bLizhJKMpzw

https://www.fuwamaki.com/article/359

https://developer.apple.com/documentation/swift/regex

https://developer.apple.com/videos/play/wwdc2022/110357/
【Udemyお勧め講座】エンジニアのためのプレゼンテーション講座

　私は本はよく読みますが動画はあまり見ません。Udemyは資格取得関連しか受講しないことが多いです。しかし、この講座はさすがプレゼンテーションの講座だけあってとても分かりやすく、講師の話し方も感じが良かったので、途中で飽きたりせずに最後まで楽しく視聴出来ました。
　そして、自分自身、受講前と受講後とで多少は人前で話すことに抵抗感が減ったのと、自分なりに考えて資料作成できるようになり、確実に効果を実感しています。
 　では、早速ご紹介して参ります。

# お勧めするUdemy講座
　口ベタで話すのが苦手でも、デザインセンスがなくても大丈夫。エンジニアにとって効果的なプレゼンテーションの方法が身に付きます。プレゼンを体系的に学び今よりもずっと伝わるプレゼンターになろう！

https://www.udemy.com/course/se-presentation/

 * 価格：15,000円（セール期間中は1,500円程度で購入可能）
 * 時間：11時間26分
## こんな方にお勧め
 * プレゼンテーションの経験が少ない若手。
 * 寡黙で1人で作業する方が好きで、人前で話すのは苦手。
 * 説明が苦手で上手く話せず”何を言いたいのか分からない説明"になりがちな技術者タイプ。
 * これまで体系的にプレゼンテーションを学んだことがなく我流でプレゼンしている。
 * 一通り説明後に「何か質問はございますか？」と言った時、反応が薄い。言いたいことが伝わっていないような不安がある。
 * プレゼンテーション技術で周囲に差をつけたい。
 * 講座で学んだ知識を積極的に実践できる方。

# 講師「しゅうマナビジネス氏」のご紹介
 * 5000人超にプレゼン指導をした現役コンサル。
 * プレゼン講師やYouTuberとしても活動中。
 * 経済産業省プロジェクトマネージャ試験、PMP(Project Management Professional)保持。
 * Youtubeチャンネル [マナビジネス【コンサル仕事術】](https://www.youtube.com/channel/UCmVMV8vgaNv5Yzpv-yX2gIg)運営中。

https://www.youtube.com/channel/UCmVMV8vgaNv5Yzpv-yX2gIg


# おすすめしたい理由
 * プレゼンテーションの全体的な流れとポイントの基礎的な部分を体系的に学べる。
 * システムエンジニア向けの構成内容になっているので、理解しやすい。
 * ゆくゆくはコンサルタントを目指すことを視野に入れている方にもお勧めできる内容です。
 * 口下手でも説明下手でも、聞き手に伝わる話し方ができるようになる。
 * 何故、何のためにその作業が大切なのか、とても丁寧に説明されていて腑に落ちる。
 * プレゼンテーションだけでなく、コミュニケーションの基本も学べる。
 * 単に「分かりやすく説明する」ことに重点を置いておらず、「相手を前向きに行動してもらう」点に重点を置いている事。この技術は目標面談、上司との1on1、報連相等様々なシーンで自身の身を助けるでしょう。
 * 将来プロジェクトリーダーやコンサルタントへキャリアアップを目指す時にも重要な素養の基礎となるし、目標面談や上甲プレゼン等でもプレゼンスキルがあるのとないのでは確実に差がつくので、学んで損はないスキルだと言える。

# ものたりない部分
 * 当たり前ですが、せっかく学んだ知識を獲得するだけで満足する方にはお勧めしません。実践して初めて価値が生まれます。
 * プレゼンテーションの基礎をすでに学んだことある方には、真新しいテクニカルな技はないのでお勧めしません。
 * 日常業務では、プレゼンテーションと同時に1人でファシリテーションもこなす必要があるケースが多いので、ファシリテーションも苦手という方は別途ファシリテーション講座も別途学んだ方が良いです。
 * そもそもコミュニケーションが苦手な人は会話術なども別途学んだ方が良い。
 　　　：
…と、あれもこれも必要と挙げればキリがなく、この講座内容だけで十分事足りるというわけにはいかない。
　しかし、私の考え方ですが、勉強会や講座や本は更なる学びを獲得しに行くきっかけやヒントという側面もあり、プレゼンテーションの基礎としてこの講座が出発点になるのなら、とても質の高い講座と言えます。
 
# 私の受講後の変化
　雑談が苦手、人前で話すことも苦手、会議で発言するのも苦手、素の自分はこんな感じです。今も中身は何ら変わりませんし、気を抜くと以前のプレゼンがド下手な一面も出ます。しかしこんな性格の私でもこの講座のお陰でプレゼンテーションやってみよう！という勇気をもらいました。理由は、プレゼンテーションはセンスがなくても口下手でも正しい知識と技術を知って実践すれば決して難しくない技術だと思えたこと。自己研鑽本等もそうですが、講座は受講して終わりではなく、実践して初めて意味をなすもの。講座内容がとても良かったので学んだ知識を実践してみたい！！と思ったのです。
 そこで、まずプレゼンテーションの機会を作るべく、このUdemy講座受講のフィードバック会をテーマに社内勉強会を開催しました。まずまずの反響があり、自己肯定感低めの私ですが僅かながら自身がつきました。
　決してすらすらと言葉が出て話せるわけでなく、とちる、どもる、時々日本語おかしい…。でも、相手の反応を見れば、以前より確実に伝わるようになった実感があります。今は、○○の勉強会をやって！とお願いされれば、内容によっては引き受けることが出来ています。昔の自分を思い返すと、とても信じられません。ですが実際に95点を100点にするのは難しいが50点を70点にするのは意外と簡単なように、人前で話すことや分かりやすく説明するのが苦手だと感じている人ほど効果を実感しやすいのではないかと思いました。

# さいごに
　エンジニアの方は口下手であっても論理的思考は得意な方が多いです。口下手でもコーディングをさせたら周囲のプログラマの2倍、3倍のスピードで動くものを作るような頭脳の持ち主だっています。
　職場にはいろんなタイプの人がいて、相性が合う人も合わない人もいます。相性が合わない上司と1on1や成果面談をしなければならないこともある。お互いに話の焦点が合わずかみ合わないメンバー、クライアント、ユーザーと折衝したり業務を説明しなければならないこともある。一通り説明したのに何故か伝わってない‥‥？とか、一通り説明して最後に「何か質問等あればどうぞ」と言っても反応薄い。連絡もくれない。お願いしたのにやってもらえない。
 そんな悩み解決の糸口にもなりえる講座だと思います。
　人は苦手を克服してこそ成長できます。プレゼンテーション技術はある程度磨けば十分というものではなく、磨けば磨くほど、より高度なプレゼンテーションにチャレンジしていけます。あなたも、今より何倍も伝わるプレゼンメソッドを実践して、今の殻を打ち破り新しい自分を開拓してみませんか？
タブーの小技　ドロップアンブロッカー

##タブーの小技　ドロップアンブロッカー

職場などで使うと便利だけども
見つかると怒られる技術

そんなこつ言ったって面倒くさいものは面倒くさい
そのパスワードに意味ある？ほんとに？
ということはたびたびありますよね

:::note info
Excel VBAでフォーム作って、
そこにファイルをドロップすることで
パスワードをアンロックして開きます
:::

①まずExcelのVBAでフォームを作成します。呼び出しボタンをシートに作ります
②そこにツールボックス上で右クリックし「その他のコントロール」-「Microsoft Web browser」にチェックを入れます
③ツールボックスの最後に追加されたWebbrowserをフォーム上に作成します
　こいつがファイルドロップを受け止める皿になります
~~~
'WorkSheet上のボタン押下時の処理
Private Sub CommandButton1_Click()
    ufDrop.Show
End Sub
~~~
~~~ufDrop.frm
Private Sub WebBrowser1_BeforeNavigate2(ByVal pDisp As Object, _
 URL As Variant, Flags As Variant, TargetFrameName As Variant, PostData As Variant, Headers As Variant, Cancel As Boolean)
On Error GoTo Er
Dim obApp As Object
Const stPass As String = "your password"

    Debug.Print URL
    If Right(URL, 4) = "xlsx" Then  '>>>Excel
        Set obApp = CreateObject("Excel.Application")
        obApp.Workbooks.Open Filename:=URL, Password:=stPass
        obApp.Visible = True
    
    ElseIf Right(URL, 4) = "docx" Or Right(URL, 3) = "doc" Then '>>>Word
        Set obApp = CreateObject("Word.Application")
        obApp.Documents.Open Filename:=URL, PasswordDocument:=stPass
        obApp.Visible = True
                
    ElseIf Right(URL, 4) = "pptx" Then '>>>Power Point
        Set obApp = CreateObject("PowerPoint.Application")
        obApp.Presentations.Open Filename:=URL & "::" & stPass
        opapp.Visible = True
    Else
        CreateObject("Shell.Application").ShellExecute URL
    
    End If
    Cancel = True   'ドロップしたファイルの処理をブラウザにさせない
    Exit Sub
    
Er:
    Debug.Print Err.Description
    Cancel = True   'ドロップしたファイルの処理をブラウザにさせない
End Sub
~~~

:::note info
以下はパスワードロックを外して開く
　拡張子 xlsx　エクセルとして開く
　拡張子 docx,doc　ワードとして開く
　拡張子 pptx　パワーポイントとして開く

以下はそのまま開く
　その他の拡張子　Windowsの定義通りに開く

※パスワード保護掛かってないファイルもドロップで開く
:::

前回はファイル→クリップボードや
html→クリップボードで、[大量のパスワードの管理]を良くない技術で簡略化しましたが、

今回は[大量のロックされたファイル]をあれ見てこれ見てというシチュエーションです
一括ドロップには対応してないので、そこは各自
ちゃんとわかってる？~gitを復習しよう1~

## この記事について
- どんな記事？：gitの復習として、基本用語、コマンドなどについてまとめた記事の第一回です！
- 対象者はどんな方？：gitをしっかり理解できていない方、　gitをなんとなく使えるけど説明はできない方

## 基本用語の復習
### リポジトリについて
  ファイルやディレクトリの状態を記録する __データベース__ のこと。作業内容を変更履歴として保存できる。具体的には変更履歴を管理したいディレクトリをリポジトリの管理下に置くことで、そのディレクトリ内のファイルやディレクトリの変更履歴を記録することができる。また変更履歴を遡ることで、バグの原因特定やそれまでの開発の経緯が確認できる。

### リモートリポジトリについて
  専用のサーバに配置して複数人で変更履歴を __共有、管理するための__ リポジトリのこと。具体的にはリモートリポジトリに自らが編集したファイルやティレクトリをアップロードすることで、他のメンバーにそれらを共有することや、反対に、リモートリポジトリから他のメンバーがアップロードしたファイルやディレクトリを取得することができる。
  
### ローカルリポジトリについて
  各メンバーが __自らの変更履歴を管理するため__ に、自分の手元のマシン上に配置するリポジトリのこと。個人の作業内容を変更履歴として管理できる。具体的には自らが編集したファイルやディレクトリの状態を変更履歴として保存できる。また特定の変更履歴に移ることで、デバック作業の効率化などもできる。

### ブランチとは
  変更履歴を分岐して記録していくための[参照](https://qiita.com/ymzkjpx/items/00ff664da60c37458aaa#:~:text=%E3%81%A7%E3%81%99%E3%81%AD%EF%BC%88%E7%AC%91%EF%BC%89-,Branch%E3%81%A8%E3%81%AF,-%E3%81%BE%E3%81%9AGit%E3%81%AE)のこと。また変更履歴を分岐することを __ブランチを切る__ と言う。[トピック](https://tracpath.com/bootcamp/learning_git_github_flow.html#:~:text=%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%EF%BC%88master%E3%80%81-,topic,-%EF%BC%89%E3%81%97%E3%81%8B%E4%BD%BF%E3%81%84%E3%81%BE)と変更履歴を紐づけることができる。具体的には機能の実装やバグの修正ごとに変更履歴を保存できる。また各ブランチが互いに影響しないことから、 __作業分担の効率化や問題発生時の原因特定の効率化__ ができる。

### リモートブランチとは
  リモートリポジトリにあるブランチのこと。通常各メンバーがローカルで開発した内容を定期的にリモートブランチに反映することで、各トピックの進度がそれに紐づくリモートブランチを通して把握できる。

### ローカルブランチとは
  ローカルリポジトリにあるブランチのこと。通常各メンバーは自身が開発、レビューを担当しているトピック以外のブランチをローカルブランチとしてもたない。そのため担当箇所に集中することができる。ローカルブランチの変更履歴は都度リモートブランチに反映することが求められる。

### デフォルトブランチとは
  リポジトリ作成時に自動で作成される最初のブランチのこと。リポジトリにアクセス、クローン、プルリクエストするときに、基準となるブランチである。またGitHubではmainと言う名前で作成される。

### ワークツリーとは
  実際に作業をしているディレクトリのこと。[ワークツリーの変更はインデックスを通してのみ変更履歴として記録される](https://qiita.com/sparklingbaby/items/e749249d366c7487bbbf#working-tree%E3%81%A8index%E3%81%AE%E6%A6%82%E8%A6%B3:~:text=Working%20Tree%E3%81%A8Index%E3%81%AE%E6%A6%82%E8%A6%B3,-%E3%81%BE%E3%81%9A%E3%81%AF%E7%B0%A1%E5%8D%98%E3%81%AB)ので、コードを修正する実際の作業を行ったり、採用するか分からない修正を試すことができる。

### インデックス / ステージとは
  ワークツリーで変更したディレクトリやファイルのうち、変更履歴として記録したいものを登録する領域。インデックスを通してのみ変更履歴として記録されることから、　__必要のないディレクトリやファイルを除くことができる。__

### その他参考文献
- [git公式リファレンス](https://git-scm.com/)

[次の記事はこちら](https://qiita.com/morizoe/items/55d1bb1c36606629f55b#:~:text=%E8%A8%98%E4%BA%8B%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89-,%E5%9F%BA%E6%9C%AC%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AE%E5%BE%A9%E7%BF%92,-clone%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)
ChatGPTを使って韓流好きな母のためハングル翻訳してみた。

おつかれさまです。
金融業界で働いている事務員兼主婦です。

先日、母からハングル講座の本買ってきてー！と頼まれました。ヨン様の時代から毎月購入している模様。もう何十年も経つのにいまだに覚えられないの？と思いましたが、見てみるとハングルってよく分からない:worried:


# Let'sチャレンジ！ChatGPTを使ってハングル翻訳Bot作成
いつも韓国ドラマばかり見ているので、ある程度の韓国語は耳で聞いて理解できるけれども、どうやらドラマに出てくる周囲のハングルを読みたかったらしいので、今回は韓流好きな母のため、身近に使えるハングル翻訳Botを作成しました。

# 完成品



https://x.com/kiku_2005/status/1733111856565477570?s=20




# 使用ツール
・[ChatGPT](https://chat.openai.com)
・[Make](https://www.make.com "Make Home")
・LINE
・[LINE Developers](https://developers.line.biz/ja/ "LINE Developers")


# 製作開始
## １. Messaging APIを準備する。
LINE Developersでコンソール作成するか、既存のLINE公式アカウントに追加します。下記の方法を参考にしました。

https://developers.line.biz/ja/docs/messaging-api/getting-started/






## ２. ChatGPTの設定

基本的な使い方は下記を参考にしました。

https://excelcamp.jp/ai-bot/media/howto/13992/

APIキーを取得しておきます。

## ３. Makeでシナリオを作る
今回は①LINEでメッセージを受け取り→②OpenAIに指示文章を設定→③LINEで翻訳回答を返すパターンです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570716/54de429a-054d-3d66-fb92-005a2404f30c.png)



左から
①LINEモジュールはLINE Developersで作成したLINEアカウントと紐づけ。
②OpenAIモジュールにはMessage Contentにプロンプト（指示文章）を設定します。

:::note info
**あなたは翻訳者です。ユーザーが日本語で話しかけるので、それをハングル語に簡潔に翻訳して読み方を日本語カタカナで回答してください。**
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570716/e4a245ed-d4de-faed-6e5b-2d2b8df77277.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570716/98fcb687-837c-f467-573b-fd2774c422fb.png)

  
③一番右のLINEモジュールに応答部分の設定をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570716/7327927b-a55c-8ec8-2285-ace484b61c79.png)


# 感想
今回は空いた時間でご自宅用を作成してみました。母は「えーっ、スゴイ！！でも小さくてよく見えない:disappointed_relieved:」とのこと。それは画面を大きく設定して、あとはメガネをかけてもらうことで問題は解消しました:sweat_smile:少しは親孝行出来たかな～。

![obaasan_woman_tasukeai.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570716/ab20b893-c13b-d436-8e2f-904c4da1ee0f.png)






ここまでお付き合い頂きまして、ありがとうございました。
ThinkPadのACアダプターを抜き差しすると画面が一瞬消えるときの解決方法

# はじめに
みなさんいかがお過ごしでしょうか。えか太郎です。本記事では私のthinkpadがACアダプターを抜き差ししたときに画面が1秒ほど真っ暗になる問題が発生したのでこの解決方法を記録として残しておきます。いまいちググってもこれといった解決策が見つからなかったのでまあ今後の人類史において３回くらい役に立つと思います。
# 私のThinkPadの環境について
## PCのスペック
* モデル：ThinkPad13 Gen2
* CPU：第七世代Celeron
* RAM：４GB（増設予定）
* Lenovoの独自端子のACではなくType-CのPD充電で

# 解決した方法
いろいろ試行錯誤した結果、Lenovoの電源管理ドライバーを当てたら治りました。これどうして自動でインストールしといてくれないんですかね。LenovoVantage君は自動インストールの時お勧めしてくれんかった。

https://support.lenovo.com/jp/ja/downloads/ds541411-lenovo-settings-power-manager-for-windows-10-version-1703-or-later-thinkpad

Lenovoのドライバーは使っているデバイスのシリアルコードを要求してきました。LenovoVantageが入っていればそちらからも確認できます。また以下のコマンドをwindowsのコマンドプロンプトで入力しても表示できます。
~~~
wmic bios get serialnumber
~~~
実行結果
~~~
C:\Windows\SysWOW64>wmic bios get serialnumber
SerialNumber
LR0ALQJZ
~~~
この「LR0ALQJZ」っていうのが私のシリアル番号です。

# おわりに
私の場合はドライバを当てたら治りましたが環境によってはほかの原因があるかもしれません。電源接続の有無によって動作を変える設定にしてるとか、省電力設定をしているとかその辺見てみるといいのではないでしょうか？
Okta - Admin Console Session Time

# はじめに

今回は2024/01/08に全テナントにリリースされる予定の管理コンソールのセッション時間について記載していきたいと思います。
この機能は2023/11/01に一度リリースされた後いったんロールバックされており、改めてリリースされてきました。
改めて挙動など確認していきたいと思います。

※本来は別の記事を予約していましたが、直近で影響大きいので先に投稿してみました。

# 機能の展開スケジュール
プレビュー環境と本番環境で以下の通りスケジュールが異なります。

- プレビュー環境
    - 2023/11/29　Early Access
    - 2024/01/04　Generally Available
- 本番環境 
    - 2023/12/04　Early Access
    - 2024/01/08　Generally Available

細かいことが気になるようであれば、テナントのPrimary ITとして登録されている宛先に以下のようなメールが届いているかと思うのでそちらを参照してください。

- 件名
    - `Okta Tech Comm Notification | Improving Your Security Posture – Admin Session Authentication`
- 内容（一部省略）
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350435/672c619c-a0db-bd24-b37c-e6fecdb62d5f.png)

# EA機能の有効化

Settings > Features > Early access > Admin Console Session Management をONにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350435/7db061aa-1e54-a327-3b51-732a40c8f1ec.png)


# 初期値

機能を有効化すると Okta Admin Console アプリの Sign On タブに以下の設定項目が追加されます。
こちらが初期値です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350435/2dd571fd-2086-9415-49a0-0c79914ebd28.png)

各項目の違いは以下の通りです。

#### Maximum app session life time

管理コンソールに最後にログインしてからの経過時間です。
初期値ではログイン後、12時間経過するとセッションが切断されて再度ログインを求められます。

#### Maximum app session idle time 

管理コンソールにログインしてから無操作での放置を許可する時間です。
初期値では無操作で15分経過するとセッションが切断されて再度ログインを求められます。


# 設定値の変更

設定画面のEditから時間を変更することができます。
各項目の設定可能な時間範囲は以下の通りです。

- Maximum app session life time
    - 最小値： 1分
        - 設定は可能ですが1分は短すぎるので止めておきましょう。
    - 最大値： 24時間
- Maximum app session idle time 
    - 最小値：1分
    - 最大値：2時間

また、life time >= idle time の関係になるように設定する必要があります。
この関係を満たさない場合、Saveすることができません。


# セッションアウトした場合の挙動

セッション時間を初期値(15分)のまま挙動を確認してみます。
最後に操作してから10分経過した時点で以下の警告が表示されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350435/2cb433d6-7469-2777-9c6f-b8aac71623e6.png)

Keep me signed in を選択すると再度15分から計測になるようです。
何もせずに放置します。
残り時間がゼロになった時点で以下が表示されて管理コンソールからサインアウトされました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350435/04327058-b5cb-82ca-5b81-62b4ed874a16.png)

Sign in を選択するとAdmin Consoleに再度ログインでき、直前までアクセスしていたページが再度開かれました。


# 気になったことなど
- 再ログインする際にMFAは求められませんでした。
    - 今回の検証環境では、DashboardとAdmin Consoleで同じ設定値のAuthentication Policyを使用していました。
    - この場合、Dashboard側のセッションが残っているため、再度MFAを求められるということはないようです。
    - これは、強制サインアウトされてから再ログインする際は、あくまでAdimin ConsoleのAuthentication Policyのみ評価されるためではないかと思います。
    - 再ログイン時のログを確認しましたが、Admin consoleへのサインオンポリシーの評価ログしか記録されていませんでした。
- 管理者のエンドユーザーダッシュボードアクセス
    - 11月時点では管理コンソールが強制サインアウトされると、エンドユーザーダッシュボードからもサインアウトされていました。
    - 今回のリリースではエンドユーザーダッシュボードからはサインアウトされることがなくなったようなので安心しました。
- 動作を検証するときは設定値を変更後、一度再ログインしましょう。
    - 再ログインせずに検証していたら、強制サインアウトされなかったり、サインアウトされるまでの時間がおかしかったりしました。
    - 設定値を変更したら一度Oktaからサインアウト->再度サインインすることをお勧めします。

# おわりに

idle timeの推奨値は15分ですが、15分って結構短いですよね。
ちょっと話しかけられて相談に乗っている間に割と過ぎてしまいそうです。
ということで、今回は直近でリリースされる予定の機能について記載してみました。
ありがとうございました。