OmniAICreator/Qiita-1.07M huggingface dataset:
相手がパワハラしてきたときは、うん🔳が漏れそうだと思え

# 背景

会社で久しぶり？に、パワハラモドキの人とかかわることがあった
どうした方がよかったか？を振り返る為にも、備忘録

# 環境

- 会社内のある研修講座が好きだったので自主的に講師に立候補し、今年度から参加
- 今年度はサポート役。見て覚える、とのこと
- 最初の集まりで年度計画で、作業割り振り
    - まぁ、簡単じゃなくても平気だったんで、二つ返事
    - 後々問題になる作業についても、口頭で「年度変えて、指示するだけだから、簡単だよー」と言われ承諾

この時点で、分からない作業について、以下確認すべきだったのかもしれない

- **簡単という作業の具体的内容**

とはいえ、「簡単」という言葉を鵜呑みにして、見せられた指示書の「年度を変える」程度と信じ切っていた


# 一回目講座、準備と実施

- 特に問題もなく完了
- 振り返りの記録を書いておいて、とのことだったんで、書いたが、振り返りされず・・

いつかやるんだろうけど、鉄は熱いうちに打てと考えているので、色々書いたことをさっさと消化したかったが、まぁ、新人講師なんで、まずは郷に入っては郷に従えってことで、沈黙

# 二回目講座、準備

- 事前準備は終わり
- 期日のあるもう一つの作業は、期間になっても連絡来ず
    - まぁ、最終期日はそこから３週ほどある様子だったんで、慌てず騒がず

事の始まりはここから・・

## 期日当日

- 講座長：「例のやつ、最終版ここにあるから指示しておいてねー」

- 私　　： :thumbsup: 


指示書の、年度、講師人数、受講生人数、日付、などを修正して完了・・っと

あれ？ 言われたファイルが不足してるんだけど？分割するってこと？　:thinking: 
もしそうなら、この指示書で分割しないように記述した方が楽じゃない？と

- 私：「○○というやり方で変えるのはありなんですか？　ダメなら従来通りやりますが？」
- 講座長：「なんで分割するの？分割する利点ってなに？」
- 講座長：「以前のファイルとかあるんだから、それみてわからない？」
- 私：「過去ファイルみて考えてみます。」

勘違いしてそうなので、昨年度の入力＆出力ファイルっぽいものを見比べてみる。
数十点のファイルな上に、ページ数もあるものもあり・・聞いてた話と違うやんと思い始める


で、わかったこと
1. ファイルは分割せず、必要なファイルを、過去フォルダから漁ってきて修正
1. 最終的にはPDF 化が必要
1. PDFしかないものもあり、原本が不足しているように見える

- 私：「○○という認識であってますか？」
- 講座長：「あってる」
- 私：「その場合、Adobe ライセンス持っていないので、編集可能な原本はどこにありますか？」
- 講座長：「PDFあるでしょ？」
- 私　　：「adobe ライセンス無いので、編集できないんですが？」
- 講座長：「cube PDF とかでできないんですか？」
- 私　　：「フリーソフトを使うって前提なんですね。」

社内WFで、フリーソフト申請提出。

- 私：「利用申請して進めますので少し時間がかかります」
- 講座長：「なに使ってもいいから結合してくれればいいんだけど」

- 私：「え？ Script とかそういった方向ですか？」
- 講座長：「そういうの詳しくないけど、なに使ってもいいですって言ったんです。
　　　　「今日は定時まで不具合対応しているので時間が無いんです！」

:sweat:  知らんがな・・仕事抱えてるのは誰しもだろ・・と思いつつも・・進める

この後、救いの神のおかげで、python でPDF 編集することになり、他にも紆余曲折あったものの、まぁ完了

## 他の問題
- 版数管理がファイル名でやってて最新版どこよ状態のファイル群
- 突然出てくる結合に必要な名称不明のファイル
- 追加指示「キーワード全部含まれてるか教材で確認しておいて」・・自動化しないと・・
- 当然、誤字脱字、表記揺れなんかもそこかしこ・・
- PDF 化したらリンクが切れる、けど誰も対処策知らない
- 急げと言ってたのに、完了して、これでいいですか？と投げても無反応な講座長


# 自戒

- 余裕がなくなったら誰しも応対が酷くなる、と考え、最初に具体的な話をすすめるべきだった？
- 「簡単」という語に騙されて、「知ってる人にとっては」というよくある落とし穴を見落としていた
- 知らない世界（PDF）であっても、ソフトでなんとかしよう、という気持ちが薄かった

なんにせよ、久しぶりに業務上で「圧」を感じる物言いに苛つきましたが、
次からは以下を思い出すようにします！

## 相手がパワハラしてきたときは、うん🔳が漏れそうだと思え

そしたら、少しは優しい目で見てあげられます。 :laughing: 

# あとがき

叫びたい気持ちを抑えきれず、少々愚痴を聞いていただいた同僚の皆さま
本日はご協力ありがとうございました :sweat_smile:
【Laravel】メソッドインジェクションを利用してRequestを偽装しよう！

:::note
Laravel v8での話です。化石を使っています。
:::

Laravelでリクエストを受け取ってゴニョゴニョしているメソッドをテストしたいとき、役に立つのが**メソッドインジェクション**です。
ここでは詳しく話しませんが、「サービスコンテナ」で調べると色々情報が出てきます。

https://laravel.com/docs/11.x/container

（Laravel8ですが）

https://readouble.com/laravel/8.x/ja/container.html

今回はこのメソッドインジェクションを利用して、`Request::xxx()` 系の処理をテストする方法を紹介したいと思います。

## Request::root

`Request::root()` と `request()->root()` の出力は一緒です。

```php
$this->assertEquals(
    request()->root() . "/sample",
    Request::root() . "/sample"
);
# => OK
```

## Request::get

リクエスト偽装の方法として、`Request::create`を使用します。create するだけでなく、作成したrequestインスタンスをバインドし直して、インスタンスを偽装します。

```php
$request = Request::create('/', 'GET', ['page' => 3]);
$this->app->instance('request', $request);
$this->assertEquals(3, Request::get('page'));
# => OK
```

以下のような「クエリパラメータの値を参考に前後ページへのリンクタグを生成する」と言ったメソッドのテストに利用できます。

```php:クエリパラメータの値を参考に前後ページへのリンクタグを生成する
function get_pager_link()
{
    $tag = '';
    $current_index = (int)Request::get('page') ?: 1; // <- コイツを偽装したい
    if ($current_index > 1) {
        $tag .= '<link href="'. Request::url() .'?page='. ($current_index - 1) . '" />'. PHP_EOL;
    }

    if ($paginate_object->lastPage() > $current_index) {
        $tag .= '<link href="'. Request::url() .'?page='. ($current_index + 1) . '" />';
    }

    return $tag;
}

public function test_get_pager_link__ページネーションタグ生成テスト()
{
    $request = Request::create('/', 'GET', ['page' => 1]);
    $this->app->instance('request', $request);
    $expectedResult = '<link href="' . Request::root() . '?page=2" />';

    $this->assertEquals($expectedResult, get_pager_link());
    # => OK
}
```

## Request::server
```php
$request = Request::create('/', 'GET', [], [], [], [
     'HTTP_USER_AGENT' => "Mozilla/5.0 (Linux; Android 6.0.1;..."
]);
$this->app->instance('request', $request);
$this->assetEquals(
    "Mozilla/5.0 (Linux; Android 6.0.1;...",
    Request::server('HTTP_USER_AGENT')  // <- コイツを偽装したい
);
# => OK
```

## Request::query

練習でやってみてください。ここまでで説明した内容でできます。

# Request::query('utm_source')
```php
$request = Request::create('/', 'GET', ['news' => "smartnews"]);
$this->app->instance('request', $request);
$this->assertEquals("smartnews", Request::query('utm_source'));  // <- コイツを偽装したい
# => OK
```

## Request::ip

IPアドレスの偽装は、REMOTE_ADDR パラメータに値を入れます。

```php
$request = Request::create('/', 'GET', [], [], [], ['REMOTE_ADDR' => '182.161.76.40']);
$this->app->instance('request', $request);
$this->assertEquals('182.161.76.40', Request::ip());  // <- コイツを偽装したい
# => OK
```
AzureをIaCでデプロイしよう！Azure Developer CLIチートシート！

# はじめに
生成AI/LLMアプリを開発するためにここ1年間くらいAzureを利用をしています。
IaCからAzureの各種リソースをデプロイしたり、アプリをデプロイする際にAzure Developer CLIを日常的に使っています。
もうそろそろ夏休みなので、休み明けの自分に向けた忘備録としてチームでAzureリソースやアプリをデプロイする際によく使うコマンドについて書いておきます。

# Azure Developer CLI(azd)とは？
以下、MS Learnからの引用です。
>Azure Developer CLI (azd) は、Azure の使用を開始するのにかかる時間を短縮するオープンソース ツールです。 azd には、ワークフローの主要なステージ (コード、ビルド、デプロイ、監視) にマップする開発者向けコマンドのセットが用意されています。 CLI を使用すると、ターミナル、エディター/統合開発環境、GitHub Actions パイプラインなど、繰り返しが可能な方法で一貫して作業できます。

## GitHub.comリポジトリ
https://github.com/Azure/azure-dev

# チートシート
## 初期化
`azd init`

## 認証関連
### Azureにログイン
`azd auth login`

### Azureからログアウト
`azd auth logout`

## 環境関連
### 一覧の表示
`azd env list`

### 環境の選択
`azd env select [env name]`

## デプロイ関連
### azd up/provision/deployの概念イメージとデプロイ対象例
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536303/ca5c8fc0-ccdd-2be2-f17e-19d689133648.png)


### インフラもアプリもまるっとデプロイ
`azd up`

### Azureリソースのみデプロイ
`azd provision`

### Azureリソース上のアプリのみデプロイ
`azd deploy`

## 削除関連
`azd down`


# 参考
* Azure Developer CLI (azd)
  - https://learn.microsoft.com/ja-jp/azure/developer/azure-developer-cli/
* Azure Developer CLI リファレンス
  - https://learn.microsoft.com/ja-jp/azure/developer/azure-developer-cli/reference
Side Effect（副作用）とはなんぞ？

## ある日のコードレビューコメントにて
> 新しくインスタンスを作って返さないとSide Effectが発生するので修正しました。
今回のコードだと発生しても問題ないですが基本的にSide Effectは発生させない方がバグになりにくくて良いです。
もし発生させる場合はコメントで明記した方が良いです。

**エンジニア6年目わい：Side Effectって知らん...何それ...怖...**
ってなったので自分の理解のために簡単に調べたことをまとめました。~~プレパラートハートなので6年もエンジニアやっててそんなことも知らないのかって言わずに生温かい目で見守ってください。~~

## Side Effectとは
日本語だと「副作用」と呼ぶようです。

https://thejs.dev/jmitchell/what-are-side-effects-and-what-you-can-do-about-them-jws

こちらの説明をGoogle先生に翻訳してもらうと以下のようなものを副作用と呼ぶそうです。
> 副作用とは、関数または式の呼び出しによる状態の変更です。関数または式に副作用を持たせるには、変更する状態がローカル スコープ外である必要があります。たとえば、変更する関数を介してオブジェクトを参照渡し、I/O 操作を実行するなどです。

副作用を抱えている関数や式を呼び出すことで何らかの意図しない変更が発生してしまい、それがバグの温床になりえるんだな〜と解釈しました。

例えば以下のようなコードは副作用が起きているコードになります。（コードレビューでもらったコメントも似たような処理してました）
```javascript
const item = {
  name: 'hoge',
  age: 26
};

const changeName = (item, value) => {
  item.name = value;
  return item
};

const changedItem = changeName(item, 'fuga');
```
`changeName`は受け取ったオブジェクトの`name`プロパティの値を`value`の値で更新して返します。
一見これで問題ないじゃん、と思うのですが、ここで以下コンソール出力の結果を見てください。
```javascript
console.log(item); // {name: "hoge", age: 26}

const changedItem = changeName(item, 'fuga');

console.log(changedItem); // {name: "fuga", age: 26}
console.log(item); // {name: "fuga", age: 26}
```
はい、元の`item`も更新されています。これがSide Effect（副作用）です。ようは関数により意図しない更新がされている状態です。

## なんで起きるの
Javascriptのデータ型には
- 基本型
- 参照型

の2つがあります。これらの違いについては他の方々が説明してくださっているので省略します。
今回問題となっているobjectは参照型にあたります。参照型では関数などの引数には参照渡し、つまりは**メモリ上のアドレスを渡す**ような動きになります。そのため、元の`item`オブジェクトがなぜか更新される、という事象が発生していました。
これを避けるためには受け取った`item`を深いコピーで別アドレスにコピーして、それに対して更新をかける必要があります。
```javascript
const changeName = (item, value) => {
  const copy = JSON.parse(JSON.stringify(item));
  copy.name = value;
  return copy
};

// 省略

console.log(changedItem); // {name: "fuga", age: 26}
console.log(item); // {name: "hoge", age: 26}
```

今回のように特にオブジェクトがネストされていなければ以下のようにプロパティを展開して新しいオブジェクトに詰め直してもOKです。
```javascript
const changeName = (item, value) => {
  const copy = { ...item };
  copy.name = value;
  return copy
};

// 省略

console.log(changedItem); // {name: "fuga", age: 26}
console.log(item); // {name: "hoge", age: 26}
```

ただし、ネストされている場合にはそのネストされたオブジェクトは浅いコピーになるので注意が必要です。
```javascript
const item = {
  name: {
    sei: 'hoge',
    mei: 'fuga'
  },
  age: 26
};

const changeName = (item, value) => {
  const copy = { ...item };
  copy.name.sei = value;
  return copy
};

console.log(item); // Object { name: Object { sei: "hoge", mei: "fuga" }, age: 26 }

const changedItem = changeName(item, 'piyo');

console.log(changedItem); // Object { name: Object { sei: "piyo", mei: "fuga" }, age: 26 }
console.log(item); // Object { name: Object { sei: "piyo", mei: "fuga" }, age: 26 }
```

## なんで避けた方がいいの
今回のコードレビューの対象部分は、更新後にすぐそのオブジェクトを呼び出し元に返す動きをしていたので特に問題ありませんでした。しかしそうではなく、後続の処理があり、その処理は変更前の値を使うことを想定されていたらどうでしょう？明示的に値を変更したわけではないのに、なぜか想定外の値が入っているという謎の現象に直面することになります。
こういったバグの温床をなくすために、副作用はなるべく避ける、もしくはコメントとして残して他の人が改修などをした際に予期しない挙動をしないようにする必要があるんだなと思いました。

## まとめ
エンジニア6年目のくせに、「今回のコードだと発生しても問題ないですが」を繰り返しまくったせいでこの辺りの仕組みを意識することなく使ってるなと反省したので、今後もこういうことは備忘録的にまとめていきたいなと思いました。

他にも6年目ならこの辺知っとけよ、ってことあればコメントに**優しく**書いてくれると喜びます。
Linux サーバー化させたノート PC を Web サーバーにしてみる

# はじめに
どうも！サーバーいじいじがたのしくなってきてるわたしです。この記事は連載シリーズ（？）おふるのノート PC を Linux サーバーにしていろいろ遊んでみよう！の Web サーバー編です。今回はこの子をタイトルの通り Web サーバーにしたてあげたいと思います。いよいよサーバーって感じがしてきたね。わくわく

ちなみに前回まではこちら。前回までで SSH できるようにしたので、もうメインで使ってるパソコンから遠隔操作ができちゃいます。あれやこれやできちゃう。

第 1 回： [使わなくなったノート PC をサーバーにしてみた](https://qiita.com/gj_5752/items/fc4e3c9fa1d6073adba9)
第 2 回： [Windows から Linux サーバーに公開鍵認証で SSH する](https://qiita.com/gj_5752/items/14bf2b84354e63fa67cd)

# 環境

サーバー
OS: Ubuntu Server 24.04 LTS
ローカルIP: 192.168.1.50

わたし
Web サーバーって Apache っていうのがあるんでしょ？あと ngix みたいな「ンぐっくす」みたいなやつ。HTML は高校の授業で書いたことはある。CSS は知らない子。ネットワークはわかってる気になっている。HTTP の通信も GET とか POST とかわかるよ！Web アプリケーションはつくったことない、のでいろいろ遊びたいんだ

# Apache のインストール

サーバー君を Web サーバーにしたてあげるにはソフトウェアのインストールが必要です。Web サーバーの機能をサービスしてくれるものがないとだめなんですね。SSH のときもインストールしたでしょ、OpenSSH というソフトを。で、そのソフトウェアは「**Apache**（アパッチ）」とか「**nginx**（エンジンエックス）」とかがあります。今回はアパッチを使ってみたいと思います。違いとかはまあわかんないのでとりあえず。

アパッチをインストールするには次のコマンドをうちます。

```shell-session
$ sudo apt install apache2
```

すると `sudo` のパスワード認証が入って、ガーって流れて `Y` をうったらまたガーってながれて、インストール完了です。これでもう Web サーバーが機能しています。つまり、もう Web になっています。マジ？ほんと！？

# Web ページにアクセス

ではこいつに Web ブラウザからつないでみましょう。みなさん例えば Google のホームページって `https://www.google.com` みたいなのが `URL` の部分に打たれていますよね。この `//` 以降の部分が **ドメイン** といってサーバーの名前みたいなものになります。これはあくまで人間サマにわかりやすいようになっていて本当は **IPアドレス** という形でコンピューターは読み取っています。例えば Google 様だったら `172.217.175.4` みたいな感じで住所が割り当てられています。ここにアクセスすると、Google のトップページが映るのです。

つまり、この子、この今 Apache をインストールしたこの子がもう Web サーバーになっているとするなら、この子の IP アドレスを指定すればアクセスができるということになります。このサーバー君の IP はローカルで `192.168.1.50` に設定してあります。なので Chrome でもなんでもいいですがブラウザを開いて URL に `192.168.1.50` といれてみます、すると...

![1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3810010/8e54f46c-1aa2-5a81-665b-dadabe90b9b0.png)

こんなページが映ります！そうです、Default Page とあるように Web ページを表示させることができました。素晴らしい！！

# 自分でページを作ってみる

さてこのホームページは `192.168.1.50` にアクセスすると映りました。つまりこのサーバーにこのファイルがあります。どこにあるかというと `/var/www/html` ディレクトリにあります。見てみましょう。

```shell_session
gj5752@gj-server:~$ cd /var/www/html
gj5752@gj-server:/var/www/html$ ls
index.html
```

`index.html` がありますね。これがさっき表示されたサイトのファイルになります。ここにページを保存していけばよいのです。では試しに何か HTML ファイルを作ってみよう！

```shell-session
gj5752@gj-server:/var/www/html$ sudo touch test.html
]gj5752@gj-server:/var/www/html$ ls
index.html  test.html
gj5752@gj-server:/var/www/html$ sudo vi test.html
gj5752@gj-server:/var/www/html$ cat test.html
<!DOCTYPE html>
<html lang="ja">
<html>
<meta charset"utf-8"
<head>
<title>テスト</title>
</head>
<body>
<h1>こんにちは世界</h1>
</body>
</html>
```
`touch` コマンドで `test.html` ファイルを作り、`vi` で少し HTML を書いて編集しました。これを開いてみましょう。`192.168.1.50/test.html` にブラウザからアクセスしてみます。すると

![2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3810010/68f6ad38-be89-3aef-9165-c0681acdfde4.png)

作った html ページを開くことが出来ました！！！感動してる。localhost とはちがったうれしさがある。

# まとめ

こんな感じでサーバー君を Web サーバーに返信させることが出来ました。もうこれで何でもし放題になった！Web アプリの作成とかを学んでいきたいね。今回はここまで！ファイアウォールとか、ドメイン取得とかはまた今度やっていきましょ

# 参考

[第8回　Webサーバーを作る（Apache）](https://shell-mag.com/8th_ubuntuserver/)
エンジニアが意識すべき 「質問するコツ」 と 「質問に答えるコツ」

# はじめに

チームで開発をしていると、誰かに質問をしたり、質問に答えたりすることがあると思います。対面やZoomで質問をすることもあれば、Slackやコードレビューなどテキスト上で質問することもあります。

**あなたの質問は相手に伝わっていますか？**

**あなたの回答は相手の求める形式になっていますか？**

この記事では、チーム開発を3年以上経験した僕が気づいた、エンジニアが意識すべき **「質問する」コツ**  と **「質問に答える」コツ** を紹介します。

# 「質問する」コツ

コツは **「相手に何を求めているか」を伝える** ことです。

エンジニアが質問する際にありがちなのは「始めに前提となる背景の共有を長々と行い、最後に質問する」というパターンです。この方法でも伝わることもありますが、答える側は「相手が何を求めているか」がわからない状態で長々と説明をされても、何に注意して聞けばいいかわかりません。

逆に、おすすめの質問方法は「最初に何を求めているかを話す」です。質問により相手に求めることの例として、以下が考えられます。

- 「はい」 or 「いいえ」 で答えて欲しい
- 自分の理解が正しいか確認してほしい
- 設計について意見が聞きたい
- 考えの整理・壁打ちがしたい
- など

特にエンジニアがチーム開発で扱う要件は複雑で説明が難しいことも多いため、相手との前提をそろえるための説明を優先してしまいがちです。最初に何を求めているか伝え、相手への負担を減らしましょう。

# 「質問に答える」コツ

コツは **「相手が何を求めているか」を理解する** ことです。

よくある例として、レビューで質問されたときに、勝手に修正したのち「修正しました」と答えてしまうパターンがあります。これは過去 X(Twitter) でも話題になりましたね。

https://togetter.com/li/2078491

「ここはどういう意味ですか？」と聞かれたら、「こういう意図でこう実装しました」のように答えるのが適切だと思います。（それに追加して、「考え直したら変だったので修正しました」とかはOK）

「質問する」コツで紹介した「質問により相手に求めることの例」に対する適切な答えは以下です。

- 「はい」 or 「いいえ」 で答えて欲しい
    -  「はい」 or 「いいえ」 で答える
- 自分の理解が正しいか確認してほしい
    - 正しい or 間違っているところを教える
- 設計について意見が聞きたい
    - 意見を述べる
- 考えの整理・壁打ちがしたい
    - アドバイス等があれば伝える

「相手が何を求めているか」がわからなかったり、「相手が何を求めているか」の認識があっているか自信がない場合は、それを確認してから答えましょう。

# 「質問する側」「回答する側」どちらも意識したほうがいいこと

上記で紹介した「質問する」コツ、「質問に答える」コツは、どちらも基礎的なことです。

ですが、レビューの質問の件しかり、できていない事例もよくあります。（少なくとも、僕の周囲ではよく発生するので、この記事を書いています。）

なぜ「レビューで質問しただけなのに、それに回答せず修正してしまう」が発生してしまうのでしょうか？

理由は事例ごとに様々だと思いますが、要因は **感情** だと思っています。

「レビューで質問しただけなのに、それに回答せず修正してしまう」のは、質問された側が「指摘されたので、直さないといけない」と感じてしまったからではないでしょうか？（僕も新卒入社してすぐの頃に、そう思ったことがあります）

個人差はあると思いますが、質問する側と回答する側に何らかの立場の差があると、感情が原因で深読みしてしまったり、反射的に回答してしまいます。立場の差の例は以下です。

- 上司と部下
- 社歴の差
- 年の差
- reviewer と reviewee

たとえ「ウチの会社はそんなこと誰も気していないよ！全員平等だよ！」と謳っていても、たとえ頭で理解していても、無意識のうちに忖度してしまうこともあります。感情とはそういうものです。

では、どうすればよいのでしょうか？感情をコントロールすることは難しいですが、できることはあると思います。それぞれの立場で可能な工夫の例として、以下が思いつきました。

- 立場が上になりやすい人
    - 責めていないことを伝える
    - 声の大きさや言葉遣いを意識する
- 立場が下になりやすい人
    - 質問している人は責めているわけではないことを理解する
    - 質問している人の機嫌を伺わず、素直に答える

特に、理路整然にきっぱり物事を言いがちな人は、意図せず相手を委縮させてしまうことがあります。また、**論破** なんてものは百害あって一利なしです。論破された側は気分が悪いですし、他の人からも「この人と話すと論破される」と思われてしまいます。

また、こういった話題でよく出てくる単語として **心理的安全性** や、Qiita のガイドラインにも出てくる **HRT** （謙虚・尊敬・信頼）があります。

https://help.qiita.com/ja/articles/qiita-community-guideline#qiita-hrt

要するに、メンバーを信頼・尊敬して、誰でも安心して発言できる状態を作ろう、という話です。

# おわりに

これは僕の経験ですが、質問をしないエンジニアよりも、たくさん質問をするエンジニアの方が成長は速い気がします。何もわからないときに「何もわからないです！」と気楽に聞ける状態が理想かもしれないですね。
WindowsでSourcetreeが起動しない時の対処法

# 現象
Sourcetreeを起動するポっと出て消える

![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505768/a45f9e8c-52ed-1994-2da6-55d4c3d9174c.gif)


# 結論
C:\Users\ユーザ名\AppData\Local\Atlassianに行く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505768/1ff9dfba-7f89-f538-d65e-4358e0ac2017.png)
この長い名前のやつを消す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505768/678a13f2-57d2-4ecf-7369-59aff0e6040f.png)

無事起動！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505768/ced86336-fe44-e241-969e-ad893f3bc8c0.png)


# 何が起きているの？
結構各所で発生するバグのようで、コミュニティにもたくさん投稿が見つかった

https://community.atlassian.com/t5/Sourcetree-questions/sourcetree-stop-launching-window10/qaq-p/2393905

曰く、
>During windows update installation sometimes following cache files get corrupted and cause this issue: Assemblies.cache,  Composition.cache
These files are present at following location: C:\Users\<User>\AppData\Local\Atlassian\SourceTree.exe_<random_string>\<version_number>
Deleting above 2 files can resolve the issue.
Sourcetree will regenerate these files when you re-run Sourcetree.
*（Windowsアップデートのインストール中に、以下のキャッシュファイルが破損し、この問題を引き起こすことがあります：Assemblies.cache、Composition.cache。これらのファイルは、以下の場所に存在します：C:\Users<User>\AppData\Local\Atlassian\SourceTree.exe_<random_string>。これら2つのファイルを削除することで問題が解決する可能性があります。SourceTreeを再実行すると、これらのファイルが再生成されます。）*

とのことです。
僕自身この現象に直面するのが3回目だったのでいい加減対処法を覚えるために記事になってもらいました。

# 参考

https://community.atlassian.com/t5/Sourcetree-questions/SourceTree-will-not-launch/qaq-p/181518
Gitコマンド:中級編

# はじめに
こんにちは！
前回、[Gitコマンド:初級編](https://qiita.com/yam_dev/items/18078f0b53bda3c6dd6e)を書きました。

初級編をマスターしたら、さらにGitを使いこなせるように、中級編のコマンドを解説します！

### git branch

Gitでは、複数の開発ラインを「ブランチ」として管理している。
`git branch`コマンドで、現在のブランチ一覧を表示できる。

```bash
git branch
```

新しいブランチを作成するには、`git branch <ブランチ名>`コマンドを使用。

```bash
git branch new-feature
```

作成したブランチに切り替えるには、`git checkout <ブランチ名>`コマンドを使用。

```bash
git checkout new-feature
```

### git merge

異なるブランチの変更をマージするには、`git merge`コマンドを使用。
これは main ブランチに new-feature ブランチをマージするケース。
```bash
git checkout main
git merge new-feature
```

### git remote

リモートリポジトリを管理するには、`git remote`コマンドを使用。
リモートリポジトリを追加するには、`git remote add <リモート名> <URL>`コマンドを使用。

```bash
git remote add origin https://github.com/username/repository.git
```

### git fetch

リモートリポジトリの変更をダウンロードするには、`git fetch`コマンドを使用。
`git pull`と違い、ローカルのブランチにはマージされない。

```bash
git fetch origin
```

### git stash

開発中に、急ぎのタスクが発生したり、他のブランチに切り替えたい時がありますよね。
そんな時、現在の作業を中断し、後で再開したい場合に便利なのが `git stash`コマンド。

`git stash`コマンドを実行すると、現在の作業中の変更内容を一時的に保存します。
後でまた取り出せるように、Gitが変更内容を保管してくれるイメージです。

```bash
git stash
```

作業を再開する際は、`git stash pop`コマンドを実行すると、最後に保存した変更内容が元に戻ります。

```bash
git stash pop
```

`git stash`コマンドは、作業中のブランチをクリーンな状態に戻したい時にも便利です。
例えば、新しい機能の開発中に、バグ修正が必要になった場合、`git stash`で機能開発の変更を一時保存し、バグ修正ブランチに切り替えることができます。

### git rebase

Gitでは、複数のブランチをマージしたり、コミットを分割したりすると、履歴が複雑になり、分かりにくくなることがあります。

`git rebase`コマンドは、コミット履歴を整理整頓するためのツール。
例えば、`feature`ブランチで開発した機能を、`main`ブランチにマージする際に、`git rebase`コマンドを使用すると、`feature`ブランチのコミットが`main`ブランチの上に移動し、履歴が一本化されます。

```bash
git checkout feature
git rebase main
```

これにより、`main`ブランチから見たときに、`feature`ブランチの変更が連続して反映されているように見えます。
履歴が整理されることで、変更内容を理解しやすくなります。

ただし、`git rebase`コマンドは、コミット履歴を書き換えるため、注意が必要。
他の開発者と共同作業している場合は、事前にコミュニケーションを取り、全員が同意の上で使用しましょう。


# まとめ

中級編、ちょっと難しいですね。
しかし、上記コマンドを理解すれば、より複雑なGit操作にも対応できるようになります。
積極的に試して、Gitの力を最大限に活用しましょう！
「reduce関数を使ってオブジェクト配列の中身をマージする」のもっと簡単な方法

# 前の記事
https://qiita.com/KyongminS/items/93ab7a2fb6786104e544

# 結論
```ts:reduceメソッド/groupByメソッドを使ってもっと簡単になる
type Order = {
  name: string;     // 商品名
  price: number;    // 値段
  quantity: number; // 個数
}

const orders: Order[] = [
  { name: 'トマト', price: 100, quantity: 1 },
  { name: 'トマト', price: 100, quantity: 2 },
  { name: 'マンゴー', price: 200, quantity: 1 },
  { name: 'イチゴ', price: 250, quantity: 3 }
];

const reduceSameProductsKai = (orders: Order[]) => {
  return Object.values(
    Object.groupBy(orders, ({name}) => name)
  )
  .map( e => (e as Order[]).reduce(
    (acc, e) => 
      ({...acc, quantity: acc.quantity + e.quantity})
    )
  )
}

console.log(reduceSameProductsKai(orders));
```

```ts:結果
[{
   "name": "トマト",
   "price": 100,
   "quantity": 3
 },
 {
   "name": "マンゴー",
   "price": 200,
   "quantity": 1
 },
 {
   "name": "イチゴ",
   "price": 250,
   "quantity": 3
}] 
```
この方法だと、indexが隣同士でない同じ種類のデータもマージできます。

```ts:トマトのindexがバラバラ
const orders: Order[] = [
  { name: 'トマト', price: 100, quantity: 2 },
  { name: 'マンゴー', price: 200, quantity: 1 },
  { name: 'トマト', price: 100, quantity: 2 },
  { name: 'トマト', price: 100, quantity: 3 },
  { name: 'イチゴ', price: 250, quantity: 3 },
  { name: 'トマト', price: 100, quantity: 1 },
];
```

```ts:結果
[{
  "name": "トマト",
  "price": 100,
  "quantity": 8
}, {
  "name": "マンゴー",
  "price": 200,
  "quantity": 1
}, {
  "name": "イチゴ",
  "price": 250,
  "quantity": 3
}] 
```

`groupBy`なんていう超便利なインターフェースが実装されているとは知らなかった。
@ttatsfさん、教えてくださり、ありがとうございました！

https://qiita.com/silane1001/items/5f2a4f06e964e7443433
非エンジニア（クリエイター）が知っておくと良さそうなツールを書き出していく

## 記事投稿の背景
縁あってエンジニアの方と近い距離でお仕事をしています。
日々の業務を円滑に進めるために、非エンジニア（クリエイター）が知っておくと良さそうなツールや知識を書き出していきたいと思い、記事を作成しました。
まだまだ勉強中であり、覚えていく必要がある内容なので、先ずは理解しておくと良さそうな基本的なツールを書いてみました。

### GitHub
プログラムやデザインデータなどを公開、管理できるオンラインサービスです。
各開発やデザインのタスク・進行管理としても使用されていることも多く、非エンジニアも必要な基本知識を理解し使えるようになると進行管理がしやすくなると思います。

- [**GitHub**](https://github.co.jp/)

### Figma
サイト制作においてウェブブラウザ上でデザイン制作（ワイヤーフレーム・プロトタイプ）が可能なツールです。
コメントの編集など使い方をマスターすると、進行管理がしやすくなると思います。
※PhotoshopやAdobe XDを使用されることもあります。

- [**Figma**](https://www.figma.com/ja-jp/)

### Googleアナリティクス4（GA4）
サイトへアクセスしたユーザーの属性や行動データを分析してサイト改善につなげるためのアクセス解析ツールです。
サイト改善のための情報収集ツールとして解析方法などを習得するとサイト改善の話をするときに役立ちます。
基本知識を習得したい方には、Googleアナリティクス認定資格(GA4対応)のスキルショップで勉強後、試験を受けてみるのも良さそうです

- [[GA4] スキルショップ](https://support.google.com/analytics/answer/15068052#zippy=%2Cget-started-using-google-analytics-introduction%2Cgo-further-with-advanced-features-in-google-analytics-advanced%2Canswer-business-questions-with-google-analytics-intermediate%2Cuse-google-analytics-for-your-business-beginner)

### WordPress
無料のCMSツールです。
元々は個人ブログの作成に利用されていることが多かったのですが、現在ではホームページ制作などでも使用されることの多いツールとなっています。
HTMLやCSSも自動生成されるため、プログラム経験のない初心者の方でも比較的使いやすいツールとなっています。
無料でもあるので、サイト制作を体験してみたい方にはお勧めです。

- [**WordPress.com**](https://wordpress.com/ja/)

### 言葉の壁をなくすために
円滑なコミュニケーションを取るために非エンジニア（クリエイター）であったとしても日常会話がスムーズにできるよう、基本的な技術用語やツールは使いこなせるよう努めていきたいです。インプット→アウトプットの繰り返しがツールや知識を習得するうえで必要なことだと思います。
Qiita Engineer Festa 2024 投稿マラソンを完走した

# Qiita Engineer Festa 2024　投稿マラソンを完走した

## はじめに

エンジニア2年目の私は、これまで技術記事を書いたことがありませんでした。アウトプットの重要性は理解していたものの、なかなか実行に移すことができていませんでした。しかし、Qiita Engineer Festa 2024投稿マラソンという素晴らしいイベントに参加する機会があり、チャレンジしてみることにしました。この記事では、そのマラソンを完走した感想を共有したいと思います。

## アウトプットを避けてきた自分

これまでの私は、技術の習得や実践においてインプットに重きを置いていました。新しい技術を学ぶことは楽しく、自己満足感も大きいのですが、アウトプットの場を自分から設けることはなかなかありませんでした。自分の知識を他人に伝えることの難しさや、正確さを求められるプレッシャーが原因だったと思います。

## チャレンジを決意

そんな中、Qiita Engineer Festa 2024投稿マラソンというイベントが開催されることを知り、「せっかくこんなイベントがあるのだから、チャレンジしてみよう」と決意しました。毎日少しずつ記事を書くことで、アウトプットの経験を積みました。

## 記事執筆の習慣

マラソンを完走するために、毎日少しずつ記事を書くように心がけました。最初は何を書けばいいのか迷いましたが、徐々に執筆のリズムが掴めてきました。記事を書くためには、自分自身も新たな知識を得る必要があるため、勉強（インプット）とアウトプットが良いサイクルを生み出しました。

:::note info
結構描き慣れるとどんどん書けるようになりました(質は低いかもですが、、)
:::


## 勉強とアウトプットの相乗効果

今回書いた記事の多くは、自分が勉強して得た知識を元にしたものです。新しい技術や概念を理解する過程で、記事としてまとめることで、自分の理解が深まりました。アウトプットすることで、単なる知識の習得だけでなく、実際に使えるスキルとして身につけることができました。

## GPTの活用

また、執筆中にGPT（例えば、ChatGPT）の助けを借りることも多くありました。記事の内容を確認してもらったり、文章を添削してもらったりすることで、記事を効率的に執筆することができました。

## まとめ

Qiita Engineer Festa 2024マラソンを通じて、アウトプットの楽しさを実感することができました。。今後もこの経験を活かして、アウトプットを続けていきたいと思います。
List型で限られたユーザー（Group内）だけを出したい時の実装

## 目的：ユーザーの複数指定
ユーザーを指定するとき、単一ユーザーを指定するのにUser型を使用します。
一方で複数ユーザーを指定したいシチュエーションも存在し、その時はList型を使用します。
List型で参照型をsys_userとすることで、ユーザーを複数指定可能です。


### 制約１.「限定されたユーザー」を指定する際の制約
ただ、この時にはすべてのユーザー（厳密には全てのアクティブユーザー）が選択肢に表示されるのですが、
これでは不便です。システムに落とすときにはある程度限定されたユーザー一覧から表示したいのが要望です。


### 解決１.「限定されたユーザー」⇒グループメンバーテーブルの使用
この時には例えばある集合（グループ）に存在するユーザーを指定するようにすれば解決します。
具体的には、グループメンバーTable(sys_user_grmember)を参照型に指定します。
そうすれば集合内のユーザーを表示します。


### 制約２.指定時の初期表示がsysIDである
ただグループメンバー型を実装した場合には、選択肢の表示が初期ではsysIDのみとなります。
これではユーザーの選択による役に立ちません。


### 解決２.リストレイアウト指定によるユーザー表示
この解決としては列ヘッダーを右クリックし、[リスト レイアウト] を選択して必要な列を追加すると、
それぞれの値を表示できるようになります。
ここでたとえばユーザー名とグループ名を指定することで、
ユーザビリティが向上した複数指定リストが作成できます。


### 制約３.指定後の表示値がsysIDである
また、もう一つ検討事項が出てきます。
選択後の表示値がsysIDにデフォルト設定ではなります。
これはsysIDではなくユーザー名で表示したいはずです。


### 解決３.Reference先のTableのDisplay fieldの値の変更
この場合の原因は以下によります。
FormのReference fieldに表示される値は、Reference先のTableのDisplay fieldの値になります。Table定義で、特定のFieldがDisplay指定されていれば、そのField、あるいは、どのFieldもDisplay指定されていなければ、nameやu_nameフィールドがDisplay valueになります。

つまり、sys_user_grmemberのテーブルの列のユーザーの表示をTrueに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190712/728b4c67-3fa8-3acf-76da-374a3a7402b3.png)

## 結論
上記の変更を適用することで、
選択肢に表示ユーザーを限定した複数リストを表示し、かつ選択時と選択後にユーザー名が表示される、利便性の高いユーザー選択が可能となります。

参考資料
[参照フィールドに特定のグループのユーザーのみを選択する方法
](https://www.servicenow.com/community/developer-forum/how-to-select-only-users-of-a-specific-group-into-a-reference/m-p/2619838/page/2)
[リスト フィールドにシステム ID が表示されるのはなぜですか?
](https://www.servicenow.com/community/developer-forum/why-is-my-list-field-displaying-sys-ids/m-p/2093288)
[[ServiceNow] Reference Fieldに表示されるDisplay ValueをUserの選択言語で切り替える
](https://qiita.com/koburn/items/cc6a536de3f5492ac17a)
C#でIoTプログラミング on RaspberryPi #2

## C#でIoTプログラミング
[前回](https://qiita.com/linyixian/items/068e74f288bbf9970511)は初めてのプログラミングということで環境構築とLチカまでを紹介しました。
今回はGPIOへの入力処理を説明します。具体的にはGPIOにスイッチを接続してオンオフの状態を調べるプログラムになります。

## 今回使用するハードウェア
・RaspberryPi4BもしくはRaspberryPi5
・Grove Button or Grove LED Buttonなど
・GROVE - 4ピン-ジャンパーメスケーブル

## 今回使用するソフトウェア
・Raspberry Pi OS 64-bit bookworm(2024/03/15)
・.NET SDK 8.0.302

前回と同じです。

## ハードウェアの準備
RapberryPiとButtonを次のように配線します。

RPi　　　Grove Button
5V---------VCC（赤）
GND--------GND（黒）
18Pin------SIG（黄）

必要な配線は3本になります。

## プロジェクトの作成
.NETではテンプレートを利用してプログラミングを始めるのが一般的です。
今回はコンソールアプリを作成していきます。

```console
$ dotnet new console -o button
$ cd button
```
dotnet new "テンプレート" -o "アプリ名" とすることでアプリのひな型が生成されます。

## パッケージの追加
GPIOを制御するためにはGPIOライブラリが必要になります。
.NETでは必要なライブラリをパッケージとしてインストールします。
```console
$ dotnet add package System.Device.Gpio
```

## Program.csの書き換え
Program.csの内容を以下のように書き換えます。
```C#
//パッケージの読み込み
using System;
using System.Device.Gpio;
using System.Threading;


class Program
{
    private static int Pin=18;         //ボタンの状態を受け取るPin
    private static DateTime lastDebounceTime=DateTime.MinValue;     //チャタリング制御用
    private static readonly int debounceDelay=100;    //チャタリング制御用

    static void Main(string[] args)
    {
        //GPIOコントローラーのインスタンスを作成
        using var gpio=new GpioController();

        //ボタン用Pinの設定
        gpio.OpenPin(Pin,PinMode.InputPullUp);

        //ボタン用Pinのイベント設定
        gpio.RegisterCallbackForPinValueChangedEvent(
            Pin,    //対象となるPin
            PinEventTypes.Falling | PinEventTypes.Rising,    //Pinの入力値のタイプ HIGH->LOWがFalling LOW->HIGHがRising
            OnPinEvent);    //コールバックルーチン
        
        Console.WriteLine("Start. Enterキーを押すと終了します。");

        //キー入力があるまで待機
        Console.ReadLine();

        gpio.ClosePin(Pin);

    }

    //ボタンを押したときに呼ばれる
    static void OnPinEvent(object sender, PinValueChangedEventArgs args)
    {
        //チャタリングを防ぐための処理
        DateTime currentTime=DateTime.Now;
        if ((currentTime-lastDebounceTime).TotalMilliseconds > debounceDelay)
        {
            lastDebounceTime=currentTime;

            //ボタンを押したのか離したのか判定
            if (args.ChangeType==PinEventTypes.Falling)
            {
                //押した時
                Console.WriteLine("On");
            } else {
                //離した時
                Console.WriteLine("Off");
            }
        }
    }
}
```

ボタンを押すとイベントが発生し、コンソールに「On」と出力されます。ボタンを離すと「Off」と出力されます。
素早く押すと連続して「On」を出力することがありますが、これはスイッチから発生するノイズが原因となっておこる現象でチャタリングといいます。チャタリングによる誤動作を防ぐためにボタンを操作した直後一定時間処理を受け付けないようにしてあります。

```console
$ dotnet run
Start. Enterキーを押すと終了します。
On
Off
On
Off
On
Off
```


ボタンスイッチ以外にMagneticスイッチ（磁石を利用したドアの開閉スイッチなど）、PIRセンサー（自動ドアなどで利用されている人感センサー）などは置き換えて利用できます。

以上　GPIOの入力処理プログラムでした。

次回の内容はまだ検討中・・・・
Wordpressで自前のAPIを作成する方法

# 結論
`rest_api_ini` フィルタを使おう。
ついでに勝手に使われるといけないので以下のフィルタを使って、認証処理も実装しましょう。
- `determine_current_user`
- `rest_authentication_errors`

# 作成するエンドポイント
```
https://ブログドメイン/wp-json/myapi/v1/helloWorld
```
# コード
```php:完成系
// 使いたいAPIの中身を書こう
function helloWorld()
{
  echo 'Hello World!';
}

// 自作のAPIを作る
add_action('rest_api_init', function () {
    register_rest_route('myapi/v1', '/hello', array(
        'methods' => 'GET',
        'callback' => 'helloWorld',
        'permission_callback' => function () {
            return current_user_can('read'); // 認証が必要
        },
    ));
});

// ユーザ認証を行う
add_filter('determine_current_user', function ($user_id) {
    // 認証情報を取得
    if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
        $username = sanitize_user($_SERVER['PHP_AUTH_USER']);
        $password = $_SERVER['PHP_AUTH_PW'];

        // ユーザーの認証を行う
        $user = wp_authenticate($username, $password);
        if (is_wp_error($user)) {
            return null;
        }

        return $user->ID;
    }

    return $user_id;
}, 20);

// ユーザ認証の成功判定
add_filter('rest_authentication_errors', function ($result) {
    if (!empty($result)) {
        return $result;
    }

    if (!is_user_logged_in()) {
        return new WP_Error('REST APIを使うための認証ができませんでした', 'You are not currently logged in.', array('status' => 401));
    }

    return $result;
});
```
JavaScriptの文法アウトプット➂ ===と==の使い分け

# 厳格な等価性と抽象的な等価性
JavaScript特有の文法である`===`と`==`の使い分けをまとめます。

## 厳格な等価性(`===`)
2つのオペランドを比較して論理値で結果を返す。
2つのオペランドの型が異なる場合には常に偽と判断する。

```
console.log('Hello' === 'Hello');
> true

console.log('Hello' === 'hogehoge');
> false

console.log('1' === 1);
> false

console.log(0 === false);
> false
```

## 抽象的な等価性(`==`)
2つのオペランドの型変換を行い、比較してから論理値で結果を返す。

```
console.log('Hello' == 'Hello');
> true

console.log('10' == 10);
> true

console.log('false' == 0);
> true

console.log(0 == undefined);
> true

```

### 参考文献

* 等価演算子(`==`)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Strict_equality

* 厳格等価演算子(`===`)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Strict_equality
CloudflareによるDDNS用コンテナの構築

## はじめに
まだ記事にしていませんが、VMware ESXiを構築しました。
ESXi上にサーバを構築し、昔作ったRaspberry Pi上の機能を随時移植しています。
今回はその一環としてWAN用のDDNSサーバをコンテナ化し機能移植します。
(Cloudflareへの登録などは行っている前提です。)

## 環境
### バージョン情報
Dockerは現在最新の物を使用していきます。
```ruby:コマンド
$ docker -v
Docker version 27.0.3, build 7d4bcd8
```

ちなみに今回DDNSの機能を移植するに辺り調べていた所、既にコンテナがあることを初めて知りました。
せっかくなのでこちらを活用させて頂きます。

https://hub.docker.com/r/oznu/cloudflare-ddns/

## CloudflareにてユーザーAPIトークンの発行
旧環境のDDNSでもそうでしたが、CloudflareではAPIキーによるIPの更新が出来るようになっています。
今回紹介するコンテナでもAPIキーを使用するため発行してきます。

以下のページへアクセスすると、APIキーの管理画面が開きます。

https://dash.cloudflare.com/profile/api-tokens

まずは「トークンを作成する」ボタンをクリックします。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566232/60b8f5ac-62b9-69f4-1c85-c13b2003b895.png)

色々とテンプレートが用意されているようで、今回は「ゾーン DNS を編集する」のテンプレートを使用しました。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566232/2ab54600-c34f-0f6c-bf26-26f58057ec25.png)

今回使用するコンテナでは、以下の権限が必要と記載があるため、こちらの権限を付与していきます。
- Zone - DNS - Edit
- Zone - Zone - Read
- Zone - Zone Settings - Read

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566232/c744a2e9-e499-93ea-f054-9f53eba6d4cc.png)

また、「ゾーン リソース」では更新対象のドメインを選択してあげましょう。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566232/37a000ce-d0a4-ee39-e114-2f198e36f7b6.png)

上記入力後末尾の「概要に進む」ボタンをクリックすると概要が表示され、「トークンを作成する」をクリックするとトークンが発行されます。
ページにも記載がある通り、セキュリティの都合上1度しか表示されないためちゃんとキーをコピーしておきましょう。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566232/696c1eed-dd84-1b17-2a37-99ff059fd3ff.png)

## コンテナの起動
コンテナを起動するだけであれば、以下のコマンドで完結します。
```bash:コマンド
$ docker run --detach\
  -e API_KEY=[発行されたAPIキー] \
  -e ZONE=[自分のドメイン名] \
  oznu/cloudflare-ddns
```
```bash:コマンド
$ docker ps
CONTAINER ID   IMAGE                         COMMAND                   CREATED          STATUS                 PORTS          NAMES
d71a48522687   oznu/cloudflare-ddns          "/init"                   10 seconds ago   Up 9 seconds                          mystifying_knuth
```

オプションを指定しないと5分ごとに確認しに行ってるとのことです。
今回私はAPIキーなどを忘れそうでしたので、docker-compose.ymlで管理しました。
```yaml:docker-compose.yml
services:
  letsencrypt:
    image: oznu/cloudflare-ddns:latest
    restart: always
    environment:
      - API_KEY=-[発行されたAPIキー]
      - ZONE=[自分のドメイン名]
      - PROXIED=false
```

## 最後に
以上でCloudflareによるDDNS用コンテナの構築は完了です！
旧機能はシェルでアクセスしに行くような作りでしたので、コンテナがあったことで大きく時短できました。
先人の方々に日々感謝…。

## 参考サイト
・oznu/cloudflare-ddns
https://hub.docker.com/r/oznu/cloudflare-ddns/
バグ原因を特定するまでの手順

## はじめに
新卒エンジニアとして開発を進めるなかで、バグ対応をすることがよくあります。
本記事では、改めて効果的にバグの原因を特定するための手順を詳しく解説します。

## バグとは
プログラムを実行しているときに何らかのエラーを出して動かなくなってしまったり、動いたとしても自分が想定している挙動をしない場合に「バグが発生している」と言う。バグ（bug）は英語で虫を意味します。

## デバッグとは

コンピュータの不具合の原因である「バグ(bug)」を「取り除く(de)」と言う意味で、これらを繋げて「debug(デバッグ)」と呼ばれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3792025/47e3c3c3-18dd-5262-ff32-1f5c2df06716.png)

## どう特定するのか

バグ改修をするにあたって、バグ原因を特定するための手順を調べました。

## 前提：事実を客観的に残す

バグに関する情報を客観的に残すことは重要です。

バグに関する調査をするにあたって、自分が調べたことや判明した事実はどんどんコメントで残していきます。例えば、ログやスクリーンショットです。例えばログの解析を行った際には、仮説とログの調査結果をスクリーンショットでNotionなどの作業メモに貼ります。こういうログが出たから〇〇が原因ではない。など、客観的なデータと一緒に残すことで後で誰かが引き継いだときや思い込みを排除し、頭の中を整理する助けになります。

## 再現するところから着手する

バグ報告に対して無闇矢鱈に調査してしまいがちです。運が良ければ修正できますが、複数の原因が重なった複雑なバグはそうもいきませんし、よく調べてみたら原因は全く別の箇所だった、ということもあります。

真っ先にバグを改修しにいくのでなく、どのような手順で操作したら、バグが確認するのかをはっきりさせる。その上で、「○○が原因ではないだろうか？」といった仮説を立てます。

## 再現手順・期待する結果・実際の結果を整理する

バグの原因を調べる前にやっておくべきことは、次の3つの情報を整理することです。

1. 再現手順 (Steps)
    
2. 期待する結果 (Expected Result)
    
3. 実際の結果 (Actual Result)
    

再現手順は、どのようにしたらバグを再現できるかの情報です。再現する環境や、前提条件、操作方法、入力値などを完結にまとめます。

期待する結果は、再現手順を踏んだ結果、どのような結果になるべきかを説明します。修正は、この期待する結果になるように行います。

実際の結果は、再現手順を踏んだ結果、期待にそぐわなず、どのような結果になってしまうかを記述します。

## 仮説を立て、仮説が原因でないことを立証し続ける

不具合の原因を特定するにあたって、仮説を立て、原因でないことを立証し続けます。（帰無仮説）帰無仮説のプロセスを繰り返していくことで、バグの原因箇所が徐々に限定されていって、最終的に必ずバグの原因が特定できるという考え方です。

## 帰無仮説
帰無仮説とは、統計学で用いられる概念で、「差がない」または「効果がない」と仮定する仮説のことです。バグ特定のプロセスにこの考え方を応用すると、次のようになります。

仮説の設定：
「この部分がバグの原因ではない」という仮説を立てます。

検証：
その仮説が正しいかどうかを徹底的に調査します。

結果の解釈：
仮説が棄却される（つまり、その部分がバグの原因である可能性が高い）場合、そこに焦点を当てて調査を進めます。
仮説が棄却されない場合、別の仮説を立てて検証を続けます。

この方法の利点は、バグの原因として考えられる要素を一つずつ体系的に排除していくことで、最終的に真の原因にたどり着ける点です。

例：
仮説：「データベース接続がバグの原因ではない」
検証：データベース接続をテスト
結果：接続に問題がなければ、次の仮説へ移行

このプロセスを繰り返すことで、バグの原因を絞り込んでいきます。

## まとめ

今回はバグ原因を特定するまでのプロセスや大事なことについてまとめました。

これらの手順を実践することで、効率的な原因特定が可能になります。
重要なのは、バグ対応を単なる作業ではなく、システムや開発プロセスを深く理解する機会として捉えることです。一つ一つのバグ対応を通じて、コードの質を向上させ、より堅牢なシステム開発につなげていくことを意識していきたいです。

この記事で紹介した手法を基に、皆さんなりのバグ対応プロセスを確立し、より効率的で質の高い開発を目指してください！
サーバーが機能しているかを確認するもの(?)をjsとhtmlで書く

# 初めに
今回はサーバーが稼働しているかを確認するもの(※)をhtmlとjsで書きます
※...サーバからの応答があれば全部200で返します
# ファイル構成
今回はhtmlファイル一つで大丈夫です
# コードをコピペ
```html:main.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>サーバーチェッカー</title>
</head>
<body>
    <center>
        <h2>テスト</h2>
        <input type="text" id="url" placeholder="URLを入力してください">
        <button onclick="checkServer()">チェック</button>
        <p id="status"></p>
        <iframe id="checkFrame" style="display:none;"></iframe>
        <p>※このコードはサーバが存在しているか確認しているだけなので、サーバが落ちていてもサーバからの応答があれば全部200で返します（CORSなどは例外）</p>
    </center>
    <script>
        function checkServer() {
            const urlInput = document.getElementById('url').value;
            const statusElement = document.getElementById('status');
            const iframe = document.getElementById('checkFrame');

            if (!urlInput) {
                alert('Error: URLが入力されていません');
                return;
            }

            iframe.onload = function() {
                statusElement.textContent = '指定したURLのステータス: 200 OK';
            };

            iframe.onerror = function() {
                statusElement.textContent = '指定したURLのステータス: URLが見つかりません';
            };

            iframe.src = urlInput;
        }
    </script>
</body>
</html>
```
### 仕組み
iframeを使って読み込みに成功したら200,エラーが発生したらエラーを返します
# 最後に
皆さんいかがだったでしょうか？今回はhtmlとjsを使ってサーバが機能しているかをチェックするプログラムを書いてみました。
それではまたお会いしましょう!
エクセル操作ができるopenpyxlまとめ

エクセルを操作できるopenpyxlについてまとめました。

### openpyxlインポート
```
import openpyxl
```

### ブックを作成
```
wb = openpyxl.Workbook()
```
### シートを作成 
```
ws = wb.create_sheet(title='hoge')
```

### シートの取得
```
ws = wb['hoge'] 
```
### A1セルの取得
```
cell = ws['A1'] 
```

### 値の代入
```
cell.value = "テスト"
```
### ファイルの保存
```
wb.save("input_file.xlsx")
```
Type Challenges 中級編17: Fibonacci Sequence,AllCombinations, Greater Than

# Type Challenges 中級編17: Fibonacci Sequence,AllCombinations, Greater Than

## はじめに

[GitHubのtype-challengesリポジトリ](https://github.com/type-challenges)は、TypeScript型システムの理解を深めるための型クイズ問題集です。今回も中級編から3つの問題に挑戦します。

:::note warn
今回全部難しかった、、競プロだろこんなん、
:::

## 1. Fibonacci Sequence

この課題では、与えられた数 `T` に対応するフィボナッチ数を返すジェネリック型 `Fibonacci<T>` を実装します。フィボナッチ数列は次のように定義されます：

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

### 例

```typescript
type Result1 = Fibonacci<3> // 2
type Result2 = Fibonacci<8> // 21
```

### 私の回答(答え見ました)

```typescript
type Fibonacci<T extends number, CurrentIndex extends any[] = [1], Prev extends any[] = [], Current extends any[] = [1]> = 
  CurrentIndex['length'] extends T
    ? Current['length']
    : Fibonacci<T, [...CurrentIndex, 1], Current, [...Prev, ...Current]>;
```
:::note warn
やってることは単純だけどこんな解法思いつかないよ、
:::

### 解き方

- `Fibonacci` 型は、フィボナッチ数列のインデックス `T` を受け取り、その値を計算します。
- ジェネリックパラメータ `CurrentIndex`, `Prev`, `Current` を使用して再帰的に数列を計算します。
- `CurrentIndex['length']` が `T` に達するまで、`Current` の値を更新し続けます。

## 2. AllCombinations

指定された文字列に含まれる文字をそれぞれ最大で1度だけ使った文字列のすべての組み合わせの型 `AllCombinations` を実装します。

### 例

```typescript
type AllCombinations_ABC = AllCombinations<'ABC'>;
// should be '' | 'A' | 'B' | 'C' | 'AB' | 'AC' | 'BA' | 'BC' | 'CA' | 'CB' | 'ABC' | 'ACB' | 'BAC' | 'BCA' | 'CAB' | 'CBA'
```

### 私の回答

```typescript
type String2Union<S extends string> =
  S extends `${infer C}${infer REST}`
    ? C | String2Union<REST>
    : never;

type AllCombinations<
  STR extends string,
  S extends string = String2Union<STR>
> = [S] extends [never]
    ? ''
    : '' | {[K in S]: `${K}${AllCombinations<never, Exclude<S, K>>}`}[S];
```

### 解き方

- `String2Union` 型を使用して、文字列を個々の文字のユニオン型に変換します。
- `AllCombinations` 型は、再帰的に各文字の組み合わせを生成します。
- テンプレートリテラル型を用いて、各組み合わせを文字列として表現します。
:::note alert
これもえぐいって、、、、全然わかりませんでした。
:::

## 3. Greater Than

この課題では、`T > U` を判定する型 `GreaterThan<T, U>` を実装します。負の数は考慮しません。

### 例

```typescript
type Result1 = GreaterThan<2, 1> // true
type Result2 = GreaterThan<1, 1> // false
type Result3 = GreaterThan<10, 100> // false
type Result4 = GreaterThan<111, 11> // true
```

### 私の回答

```typescript
type ArrayWithLength<T extends number, U extends any[] = []> = 
  U['length'] extends T ? U : ArrayWithLength<T, [true, ...U]>;

type GreaterThan<T extends number, U extends number> = 
  ArrayWithLength<U> extends [...ArrayWithLength<T>, ...infer _] ? false : true;
```

### 解き方

- `ArrayWithLength` 型を使用して、指定された長さの配列を生成します。
- `GreaterThan` 型は、配列の長さを比較することで `T` が `U` より大きいかどうかを判定します。
- 配列の展開を利用して、比較を行います。
:::note warn
もう今回全部難しかった、、
:::

## まとめ

これらの課題を通じて、TypeScriptの型システムに対する理解が深まり、複雑な型操作のスキルが向上します。特に、テンプレートリテラル型や再帰型、マッピング型を駆使することで、強力で柔軟な型を実装できるようになります。ぜひ、これらの課題に挑戦して、型の理解を深めてください。

### 参考記事

- [TypeScript公式ドキュメント](https://www.typescriptlang.org/docs/)
- [type-challenges GitHubリポジトリ](https://github.com/type-challenges/type-challenges)
Gitコマンド:初級編

# はじめに
こんにちは！

システム開発に欠かせないGit。
様々なコマンドがあるのですが、よく忘れます、、、
備忘録を兼ねて、まずは初級編としてまとめてみました。

# コマンド（初級編）

### git init

新しいプロジェクトを開始する際に使います。
`git init`コマンドを実行することで、そのディレクトリにGitリポジトリを作成。

```bash
git init
```

### git clone <リポジトリURL>

GitHubなどのリモートリポジトリからコードをダウンロードしたい場合は、`git clone`コマンドを使用。

```bash
git clone https://github.com/username/repository.git
```

### git add <ファイル名>

コードを変更したら、`git add`コマンドで変更をステージングエリアに追加。

```bash
git add myfile.txt
```

複数のファイルを一度に追加したい場合は、ワイルドカードを使用。

```bash
git add *.txt
```

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

ステージングエリアにある変更を保存するには、`git commit`コマンドを使用。

```bash
git commit -m "修正: ファイル名を変更"
```

### git push

ローカルでコミットした変更をリモートリポジトリに送信するには、`git push`コマンドを使用。

```bash
git push origin main
```

### git pull

リモートリポジトリの変更をローカルに取得するには、`git pull`コマンドを使用。

```bash
git pull origin main
```

### git status

現在のリポジトリの状態を確認するには、`git status`コマンドを使用。
変更されたファイル、ステージングエリアの状態などが表示される。

```bash
git status
```

### git log

コミット履歴を表示するには、`git log`コマンドを使用。
各コミットのハッシュ値、作成日時、コミットメッセージなどが表示される。

```bash
git log
```

### git diff

変更内容を確認するには、`git diff`コマンドを使用。
変更前のコードと変更後のコードを比較表示する。

```bash
git diff
```

# まとめ

今回は、初級編をまとめてみました。
GUIで操作することも多いかと思いますが、コマンドも慣れておくと、様々な開発環境に対応できます。

もっと詳しい情報が必要な場合は、以下のサイトをご確認ください。


* Gitの公式ドキュメント: [https://git-scm.com/doc](https://git-scm.com/doc)
* GitHub Guides: [https://guides.github.com/](https://guides.github.com/)
MathematicaのDynamicModuleでアニメーションを実装してみた

## はじめに
先日、授業の課題でMathematicaのGraphics機能を使って絵を描く、という課題が出たので、アニメーションを作りました。
その備忘録です。

## Mathematicaでのアニメーション作成
まず、Mathematicaでアニメーションを作るにはDoループ、Animate、DynamicModuleなどの方法があります。
私が作ろうとしていたアニメーションは、一般的なアニメーションの作成方法と同様に、1秒間に何度か図形を切り替えてアニメーションにしようとしていたため、より高度な扱いができるDynamicModuleを採用しました。
~~Animateうまく動かせなかった~~

## DynamicModuleによるアニメーションのコード
```
DynamicModule[{i = 1}, 
 Circles = {Circle[{0, 2}, 1], Circle[{1, 1.75}, 1], 
   Circle[{2, 1.5}, 1]};
 ground = Line[{{-2, -2}, {2, -2}}];
 Dynamic[
  Graphics[{ground, Circles[[i]]}, PlotRange -> {{-6, 6}, {-6, 6}}]], 
 Initialization :> (RunScheduledTask[If[i < 3, i++, i = 1], 0.5])]
```
動かすとこんな感じになります。

![animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982253/7508c4fb-d3f1-eb7d-0ceb-f87b160f7282.gif)

コードの中身を見ていきます。

```
DynamicModule[{i = 1},
```
ここではDynamicModuleを利用し、最初に変数iに1を代入しています。
iは配列のi番目の要素を出力する時に使うのですが、最初に1つ目の要素を出力するために1を代入しています。ちなみに、Mathematicaは1-index(配列のn番目の数え方が0ではなく1から始まる)なので、0を入れると何も出力されません。
```
Circles = {Circle[{0, 2}, 1], Circle[{1, 1.75}, 1], 
  Circle[{2, 1.5}, 1]};
ground = Line[{{-2, -2}, {2, -2}}];
```
出力する図形です。
円はアニメーションとして動かしているので、Circlesは配列になっています。
地面の線は固定されているので、配列ではなくLineだけになっています。
```
Dynamic[
  Graphics[{ground, Circles[[i]]}, PlotRange -> {{-6, 6}, {-6, 6}}]], 
```
DynamicModuleで動かすGraphicsの変数がまとまっています。
groundとCirclesのi番目をGraphicsで表示、Graphicsの描画範囲をPlotRangeで指定しています。
```
Initialization :> (RunScheduledTask[If[i < 3, i++, i = 1], 0.5])]
```
`If[i < 3, i++, i = 1]`の部分で、iが[1, 2, 3]の値を取ることを、`0.5`の部分で0.5秒ごとに画像を更新することを指定しています。

## 補足
ちなみに、アニメーションの出力はExport関数を使うとgif形式で出力できます。
Wordpress.comでREST API経由で画像にalt属性を付与するときの罠

今日の懲りずにWordpress.comの記事です。

:::note warn
Wordpress.comでWordpress.orgのREST APIを使っています。
:::

Wordopress.comのREST APIにはalt属性を設定するインターフェースがあります。

**Wordpress.com のREST API**

https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/media/new/

**Wordpress.org のREST API**

https://developer.wordpress.org/rest-api/reference/media/

これを使う上で注意しないといけないのは、ここでalt属性を設定した画像を記事中で使用するとき、
- REST API経由で文章を投稿している
- 投稿した画像のURLを記事中に `img` タグで埋め込んでいる 
- 記事の文書をHTMLやMarkdownで作成している

ときは、**imgタグのalt属性を自分で取得してあげる必要があります**。

# サンプル

```php:画像アップロードする処理の例
/**
 * Wordpress APIを使って画像をアップロードする
 * @param mixed $baseUrl WordPressのURL
 * @param mixed $user WordPressのユーザー
 * @param mixed $password WordPressのアプリケーションパスワード
 * @param mixed $contentType 画像のcontent-type
 * @param mixed $content 画像のバイナリデータ
 * @param mixed $filename 画像のファイル名
 * @param string $altText 画像のaltテキスト
 * @return mixed
 */
public function uploadMedia($baseUrl, $user, $password, $contentType, $content, $filename, string $altText = "")
{
    $response = Http::withBody(
            $content,
            $contentType
        )
        ->withHeaders([
            'Authorization' => 'Bearer '.$this->getToken($user, $password),
            'Content-Disposition' => 'attachment; filename='.$filename,
            'Content-Type', 'application/json',
        ])
        ->post(config('services.wordpress.endpoint'). "/wp/v2/sites/". parse_url($baseUrl, PHP_URL_HOST). "/media");
    if (! $response->successful()) {
        Log::error('画像アップロードエラー: '.$response->reason(), [
            'body' => $response->body(),
        ]);

        return null;
    }

    return $response->json() ?? null;
}
```

token取得の処理は、以下を参考にしてください。

https://qiita.com/KyongminS/items/ecbe89379944e42cc184


こうして投稿した画像は、メディアライブラリ上では全てalt属性がきちんと付与されています。
しかし、**サムネとして設定すると**自動でaltが設定されますが、記事中の画像は、`img` タグにalt属性を付与する処理をこちらで実装する必要があります。

```php:alt属性を付与する処理
/**
 * 記事文章マークダウン内のimgタグにalt属性を付与する
 * @param mixed $content マークダウン
 * @param mixed $altText altテキスト
 * @return string
 */
public static function insertAltTag($content, $altText): string
{
    $pattern = '/<img\s[^>]+>/i';

    $replacement = function ($matches) use ($altText) {
        $img = $matches[0];
        if (preg_match('/alt\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)/i', $img)) {
            // 既存の alt 属性がある場合の処理
            if (preg_match('/alt\s*=\s*(["\'])\s*\1/i', $img)) {
                // alt 属性が空の場合、新しい内容で置換
                return preg_replace('/alt\s*=\s*(["\'])\s*\1/i', 'alt="' . htmlspecialchars($altText, ENT_QUOTES) . '"', $img);
            }
        } else {
            // alt 属性がない場合、新しく追加
            return substr_replace($img, ' alt="' . htmlspecialchars($altText, ENT_QUOTES) . '"', -1, 0);
        }
        return $img; // 既存の alt 属性が有効な値を持っている場合は変更しない
    };

    return preg_replace_callback($pattern, $replacement, $content);
}
```

これで2時間ぐらいハマったので、参考になればと思います。
Ubuntu22.04でUdevを用いたキーボードのリマップ

# 概要
Udevを使ってキーボードのリマップをする。やりたかったことは
- CapsLockを全角半角に変更

だった。これには失敗した。変わりに、
- CapsLockをBackSpaceに変更

をした。

## 手順
- キーボードのパラメタを特定
- キーのパラメタを特定
- リマップのためのファイルを書き、反映させる

## キーボードのパラメタを特定
キーマップを変更したいキーボードの諸々を特定する。
```
cat /proc/bus/input/devices
```  
を実行する。たくさんのデバイスが表示される。その中から、探してるキーボードを見つけ(気合)、Bus,Vendor,Productの値をメモする。
e.g.
```
I: Bus=0011 Vendor=0001 Product=0001 Version=ab83
```



## キーのパラメタを特定
#### 変更したいキーの諸々を特定する
```
sudo evtest
```
を実行する。インストールされてなければ、aptで落とせる。
出力された内容のうち、`value`を見つけて記録する。
```
Event: time ... ,------SYN_REPORT---------
Event: time ... , type ... , code ... , value 3a
Event: time ... , type ... , code ... , value 1
Event: time ... ,------SYN_REPORT---------
```
とか出ると思う。上の、`3a`を使う。

#### 変更後のキーの値を特定する

これに関しては、私はよくわかりませんでした。以下のリンクから探すと見つかるかも。
https://hal.freedesktop.org/quirk/quirk-keymap-list.txt
    エンターに変えたかったら`enter`とか、backspaceに変えたかったら`backspace`とかかも。

 
## ファイルを書いて反映させる
`/etc/udev/hwdb.d/hoge.hwdb`を作成し、書き込む。root権限が無いと編集できないので注意。ファイル名は任意。
ここに、今までで特定した値を使うよ。

```hoge.hwdb
evdev:input:b[Bus]v[Vendor]p[Product]*
    KEYBORD_KEY_[valueの値]=[変更後のキー]
```
のように書く。コメントアウトは`//`で書く。
e.g.
`Bus=0011 Vendor=0001 Product=0001`,`value 3a`のキーをbackspaceに変更したいとき
```myremap.hwdb
evdev:input:b0011v0001p0001*
  KEYBOARD_KEY_3a=backspace
```
書き終えたら、設定を反映させる。
```
sudo udevadm hwdb update
sudo udevadm trigger
```
これでできるはず。xbindkeysとか使えば、変更したキーがどう認識されてるかわかる。

(コマンドに誤りあがったため修正。)

## 参考
- HBenpitsu(enpitsuHB),"Ubuntuでキーマップを変更する",Qiita, https://qiita.com/HBenpitsu/items/4f5fabe9f73e0ab34174 ,(閲覧日2024/7/17)


- kzee(そのへんの人)," Ubuntu 22.04 でキー割り当てを変更する",Qiita, https://qiita.com/kzee/items/4ac903282f80e0c4d5aa ,(閲覧日2024/7/17)

- https://hal.freedesktop.org/quirk/quirk-keymap-list.txt  , (閲覧日2024/7/17)
CPaaS + LLMで顧客をイライラさせないコールセンターを実現する！？

CPaaSという耳慣れない言葉を目にしました。どうやらCommunication Platfor as a Serviceの略の様です。ネクスウェイのCPaaS NOWという記事に以下の構成図が掲載されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469079/968d85b6-1c0d-368d-58de-110319951405.png)

https://smslink.nexway.co.jp/cpaas/lp

筆者はこの構成図を見た時に、ふとLLM(Large Language Model)という昨今注目されているAI技術、日本語では大規模言語モデルと呼ばれるモデルであり、人が自然言語で命令を出せる従来とは異なるAIと組み合わせることでイライラさせないコールセンターを実現出来るのではと思いました。

# コールセンターがイライラする理由
筆者はコールセンターには、大きく2種類のコールセンターが有ると思います。1つはイライラしないコールセンター、2つ目はイライラするコールセンターです。イライラしないコールセンターは電話を掛けてすぐに繋がるコールセンターです。但し、この手のコールセンターはナビダイヤルなど有料である場合が多く、お財布には優しくありませんが、確実に繋がります。イライラするコールセンターは十中八九フリーダイヤルです。散々ボタンを押してオプションを選んだ挙げ句に「只今回線が混み合っています。暫くこのままお待ち下さい。」と言われ、待たされるだけ待たされる訳です。さながら、電話主が諦めて電話を切るのを待っているのではないかと思わせる程に待たされます。酷いコールセンターになるとそのまま「後程お掛け直し下さい」と言われ電話を切られます。。。また、最近はコールセンター設置に伴うコストを削減したいのか、コールセンターの番号にすぐに辿り着けないといった事例や、電話を掛けてもSMSでFAQ URLを送ってよこすぞ誘導などてこでも電話させないようにしているのでは？と思わせる事例が多々有ります。

# どう解決するか？
このPaaSとLLMを組み合わせることで、上記を解決するか考えます。まず、コールセンターに人員を増やせばコストが嵩むので最低限の人数でやっているので上記の様なことになっていると仮定します。但し、無理矢理FAQページに誘導しても恐らく電話主であるカスタマーは満足しないどころか、不満を抱えるだけでしょう。よって、電話を掛けたらすぐに友人の電話につなげないのであれば、LLMによる自動回答を間に入れて、LLMに回答させる様にし、LLMが自動音声で回答した内容で解決出来なかった人のみが友人の電話に接続されるようにすれば、散々プッシュした後に回線が混み合ってると散々待たされたり、電話を強制終了させられたりすることが無くなると思います。また、LLMシステムで文字起こしや質問内容の要約を作れる様にしておくことで、コールセンターへの電話でしばしば発生する担当者が交代するとまた最初から話をしなければならない問題も解決出来ると思います。

# まとめ
思いついたアイディアをダンプしましたが、荒削りで細部の詰めが甘いような気もしますが、今後もう少しこのアイディアをブラッシュアップしてみようかと思います。RAGなどを使えば上記コールセンターのシステムは実現出来るのではないかと思います。