OmniAICreator/Qiita-1.07M huggingface dataset:
Vitestでダウンロード機能のテストを書く

## はじめに
Vitest, React-Testing-Libraryでダウンロード機能のテストを書くことがありましたが、いろいろつまずいたので備忘録も兼ねて書きます。
フロントはVite, Reactで構築しています。

## テスト対象
aタグを生成して、プログラム側でクリック、その後削除するコードです。
素でaタグを書いたらええやんと思われるかもですが、ダウンロードの前に何かしらの処理がしたい場合には、下のように書くと便利ですね。
```tsx
const DownloadForm = () => {
    const handleDownload = () =>
        // ...何らかの処理
        const link = document.createElement("a")
        link.href = "downloadUrl"
        link.download = ""
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
    }

    return (
        <button onClick={handleDownload}>ダウンロード</button>
    )
}
```

# Error: Not implemented: navigation 
まずはボタンの表示などの簡単なテストから書き始めていると、下のエラーが出ました。何やらテストライブラリ側でNavigation（URL遷移）をサポートしていないそうなのです。（JSDomあたり?）

```Error: Not implemented: navigation (except hash changes)```

# documentをモックにして解決
navigationが変化したことをテストするのではなく、aタグの生成、クリック、削除の一連の動作が行われたことをテストします。

ハマったのが、documentのモックコードの位置です。it内の先頭やbefore内に書くと、documentが機能しなくなってしまうために、React周りやDOM操作を伴うテストなどが動かなくなります。なので諸々の処理が終わった後にモックを作成し、テストを書いています。
※より良い案があれば教えてくださいませ。

```tsx
it("should file download", async () => {
    render(
        <MemoryRouter>
            <DownloadForm />
        </MemoryRouter>,
    )

    // ダウンロードボタンをクリック
    const downloadButton = screen.getByText("ダウンロード")
    fireEvent.click(downloadButton)

    // モックを作成
    const link = Object.assign(document.createElement("a"), {
        click: vi.fn(),
    })
    vi.spyOn(document, "createElement").mockImplementation(() => link)
    vi.spyOn(document.body, "appendChild").mockImplementation(vi.fn())
    vi.spyOn(document.body, "removeChild").mockImplementation(vi.fn())

    // ダウンロードのテスト
    await waitFor(() => {
        expect(document.body.appendChild).toHaveBeenCalledWith(link)
        expect(link.href).toBe(
        "downloadUrl",
        )
        expect(link.click).toHaveBeenCalledTimes(1)
        expect(document.body.removeChild).toHaveBeenCalledWith(link)
    })

    vi.restoreAllMocks() // モックを初期化
  })
  ```

## おわりに
テストのモックというと、MSWを使ったり、ライブラリの関数をモックするなどしかやったことがありませんでした。このようにブラウザ側の機能もモックすることを覚えると色々なケースに対応できそうです。

## 参考資料
- https://stackoverflow.com/questions/55020924/testing-function-that-starts-file-download-with-jest
- https://codesandbox.io/p/sandbox/6xp9lqjzk3?file=%2Fsrc%2Fdownload.spec.js%3A15%2C5-15%2C49
【勉強記録】1月の成果

# はじめに
こんにちは。アメリカで独学でエンジニアを目指している者です。
本日は1月の勉強成果等を記事にしていこうと思います

## 1月の目標達成率
12月の記事で立てた目標の達成率です
* Linux,Git,SQLの演習を始める(土日の時間？)　達成！
平日の勉強時間に組み込むことで達成。Linux,Git,SQL,JavaScript,ネットワークを1週間の内にそれぞれ2~2.5hとることで復習に当ててます。
* GitHub Copilotを使ってみる　未達成
これについては、使い方等もいろいろ調べてみましたがまだ自分で考えて作成するフェーズであってその方が考える力がつくと思いまだ実装しないことにしました
* 勉強時間150時間　達成
170.5時間でした！土日まったくやっていないことを考えると1日平均8時間くらいはやってる計算ですね。
正直ここまで勉強しているとは思いませんでした！GoodJob！！
* 今思いついていない新しいこと1つはじめる　
これはいくつか始めましたので次の章で書いていこうと思います！

## 1月に新たに始めたこと
* セルフトークをするために勉強日記に「今日よかったこと」、「明日改善したいこと」、「自分にひと言」の欄を追加
これは自分の性格を補完するためにやっていることですが、自分に対してかなり厳しく今回の170時間勉強したことを振り返ってみてもそこまで頑張った自分とも思わないんですよね。人間自分をほめることをしないとしんどいと思うのでNotionの勉強記録シートの欄にこれらを追加してセルフトークするよう心がけています。
独学は以下に自分のモチベーションを管理して勉強できるかだと思っています

* Xに勉強した日の勉強記録をのせている
これは毎日ではなく勉強した日に載せるようにしてます。
Xを始めた理由は自分と同じような独学の人を探していたのですが、人にモチベーションを与えてもらってばかりなのもずるいなと思い始めました！
自分の記録をみて、「この人もこんなやっているなら自分も頑張ろう」と思ってもらえればとてもうれしいですし、そういった方々に負けないくらい努力をしようと思えるようになります

* 寝る前と朝のコールドシャワーを開始
これは毎日ではないのですが、平日は基本毎日です。
朝起きてぼーっとするなと思っていたのですが、コールドシャワーを浴びると健康面でも精神面でもかなりいいことずくめらしいです。

* 毎朝30分前に起きて勉強時間を増やした
上記のコールドシャワーが妻の仕事の時間と被るので、せっかくならといつもの時間よりも30分早く起きることにしました
けど、ただ30分起きただけではもったいないのでせっかくなら勉強時間に当てようと思いPythonを勉強し始めました
これも新しい取り組みですが、Pythonを勉強することでコーディング面接の対策をしようと思っています。
アメリカの企業においてRubyはとてもマイナーな言語なので、Pythonもできたほうが就職市場的にも重宝されると思ったのとコーディング面接はPythonを使っている方が多かったので、解説の量が多いことを踏まえてPythonを学び始めました
とはいってもアメリカの企業はワンちゃんいければラッキーくらいの気合で挑もうと思っています

* Dev.toを開始
これは簡単に言うと英語のQiitaみたいなやつです。
日本語で毎日ブログを上げていますが、英語でもアウトプットしようと思い始めました。
これは英語考えながら作成する必要があるので日本語のブログ記事作るよりも1.5倍くらいかかるのでできても1週間に1つ以上くらいかなと思います

* タイポを撲滅してくれる「Code Spell Checker」を採用した
これは近いうちに記事を出そうと思っていますが、VScodeの新たなツールを導入しました


## 2月の目標
* 一日に1時間は勉強するように努める
実は2月の2週目に日本へ一時帰国するので勉強する時間がほぼ皆無です。ですが朝起きたりして勉強できればやりたいと思っています！
これは達成率何％で来月のブログで書こうと思います
* 毎日ブログ投稿を続ける
Qiitaへの投稿は毎日続けます。一日8時間以上勉強しているのでもちろんアウトプットとして記事にしたい内容はたくさんあるのでこれは頑張って達成使用と思います。


1カ月ほぼ勉強ストップになって今からでもそわそわしてますが、可能な限り勉強できるよう隙間時間を使っていきたいと思います。
OpenHandsをGitHub Actionsで手軽に動かして安Devin?を試してみよう

## はじめに

Devinちょっと高いけど、Devinっぽいことしたいな...
ということで、OSSのOpenHandsを試してみましょう。

OpenHandsは旧名がOpenDevinなこともあり、OSSのDevinを目指してた雰囲気はありますね。

なお、筆者はDevin動かしたことがないので、完全に代替できるかは、すみませんが分かりません。
また、OpenHandsには今のところ、Slackでやりとりする機能は標準ではありません。

この記事の内容を設定するとGitHub上で
AIにPullRequestでコードをつくってもらうことが出来ます。

## GitHub Actionsでの設定方法

OpenHandsをGitHub Actionsで動かすのはとても簡単です。
（最後に書いてありますが、GitHubのプランによって一部制限があります）

### ワークフローファイルの作成

これぐらいの記述を`.github/workflows/openhands-resolver.yml` に書きます。

```yaml
name: Resolve Issue with OpenHands

on:
  issues:
    types: [labeled]
  pull_request:
    types: [labeled]
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  pull_request_review:
    types: [submitted]

permissions:
  contents: write
  pull-requests: write
  issues: write

jobs:
  call-openhands-resolver:
    uses: All-Hands-AI/OpenHands/.github/workflows/openhands-resolver.yml@0.22.0
    with:
      macro: ${{ vars.OPENHANDS_MACRO || '@openhands-agent' }}
      max_iterations: ${{ fromJson(vars.OPENHANDS_MAX_ITER || 50) }}
      base_container_image: ${{ vars.OPENHANDS_BASE_CONTAINER_IMAGE || '' }}
      LLM_MODEL: ${{ vars.LLM_MODEL || 'anthropic/claude-3-5-sonnet-20241022' }}
    secrets:
      PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
      PAT_USERNAME: ${{ secrets.PAT_USERNAME }}
      LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
      LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
```

参考URL:
https://docs.all-hands.dev/modules/usage/how-to/github-action

## 環境変数の設定

あとは、環境変数周りを設定していきます。
GitHubActionsのsecretsにLLM_API_KEYを設定します。初期のモデル設定がanthropicなので、そのキーをいれると良いでしょう。

この手のAPI利用でよくあるlimit制限を突破するためにOpenRouterを使いたいところですが、GitHubActionsモードでうまくモデルを切り替えることが私はできませんでした。

## リポジトリの権限設定

Settings -> Actions -> General -> Workflow permissions に移動

"Read and write permissions" を選択
"Allow Github Actions to create and approve pull requests" を有効化

⚠️ "Read and write permissions" オプションがグレーアウトしている場合の注意点：
まず、組織レベルで権限を設定する必要があるかどうかを確認
組織レベルでもグレーアウトしている場合は、Enterprise ポリシー設定で権限を設定が必要

もう少し細かい制御がした場合はPAT_TOKENに
GitHub上で設定したPersonal Access Tokenをセットします。

## 使用方法

GitHub Issueにfix-meラベルをつけるか @openhands-agent と書くとGitHub Actionsがトリガーされて動き始めます。
あとは、コードレビューをして良ければ取り込み
ダメならPRに @openhands-agent からコメントをして修正事項を依頼します。

憶測ですが、仕組みとしてはDocker上で環境を作ってClineみたいな事してるんだと思いますね。

## 利用制限について

Draft Pull Requestの作成機能は、以下の場合のみ利用可能です：

- GitHub Free を使用している公開リポジトリ
- GitHub Free for organizations を使用している公開リポジトリ
- GitHub Pro（公開リポジトリのみ）
- GitHub Team（公開および非公開リポジトリ）
- GitHub Enterprise Cloud（公開および非公開リポジトリ）

この情報は、[GitHub APIの公式ドキュメント](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#create-a-pull-request)に明確に記載されています。

つまるところ、OpenHandsの内部的にはPull RequestがDraftで出るので
その機能が使えないと、Pull Requestに失敗するようです。

その場合は、ブランチは作成された状態になりますので
手動でPull Requestを作成することは可能です。

私は、GitHub Teamに課金して使っています。
AI-102 学習メモ (4) : コンテンツ モデレーション ソリューションを実装する 

# はじめに
この投稿は AI-102: Microsoft Azure AI エンジニア の学習をした内容のメモです。

投稿の一覧は以下のリンク先にあります。

https://qiita.com/atsmin77/items/5235dfe16ad1bed065ca

# 前の投稿

https://qiita.com/atsmin77/items/f358f79ca5bac4485887

# コンテンツ配信用のソリューションを作成する

## Azure AI Content Safety

**Azure AI Content Safety** を利用すると、テキストや画像内の有害なコンテンツを検出してブロックすることができます。Azure AI Content Safety を利用するには、以下のように Azure Portal から Contents Safety リソースを作成します。

### Contents Safety リソースの作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/df4fde23-89f2-fcb3-b3be-52ac4ec3ee03.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/9607668a-875f-a4c9-b836-f516ea3e6b78.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/2b1af2a8-bc5d-dd2f-5681-8e780da58d63.png)

### Contents Safety Studio

作成した Contents Safety リソースを利用して、 [Contents Safety Studio](https://contentsafety.cognitive.azure.com/) または [Azure AI Foundry](https://ai.azure.com/explore/contentsafety) で実際に動作を確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/53c2a852-54cf-326c-777b-d824b9a113cc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/11f0cbe9-8e79-2593-8aa6-31fb0df01707.png)

サンプルで用意されているテキストをクリックし、[Run test] をクリックすると、検出結果を確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/2fddbd84-78ed-945a-ed03-95a9d4afa2da.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/b201f3e4-4e75-63cb-e881-ca546b1f39d4.png)

::: note warn
Contents Safety Studio を利用するためには、サインインするユーザーに **Cognitive Service ユーザーロール**を割り当てる必要があります。ロールが割り当てられていないと、以下のようなエラーメッセージが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/4a25642a-f789-971c-ad98-b339a5309782.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2680397/e39e1a13-3d5a-6fbc-a45f-c4b018f5c0c2.png)
:::

https://learn.microsoft.com/ja-jp/azure/ai-services/content-safety/overview

https://learn.microsoft.com/ja-jp/azure/ai-services/content-safety/concepts/response-codes#azure-ai-foundry-error-messages

## ブロックリスト

**ブロックリスト**を登録することによって、Contents Safety で任意のテキストを検出させることもできます。

https://learn.microsoft.com/ja-jp/azure/ai-services/content-safety/how-to/use-blocklist?tabs=windows%2Crest

# 次の投稿

https://qiita.com/atsmin77/items/a6132af2d7507ff74c5b
jQuery使いだった人がJavaScriptに移行する際のあるある対比集

# 1. DOM要素の取得
```js
// jQuery
const $element = $('#myElement');
const $elements = $('.myClass');

// モダンJavaScript
const element = document.getElementById('myElement');
const elements = document.getElementsByClassName('myClass');
// または
const modernElements = document.querySelectorAll('.myClass');
```

# 2. イベントリスナーの追加
```js
// jQuery
$('#button').click(function() {
  console.log('クリックされました');
});

// モダンJavaScript
document.getElementById('button').addEventListener('click', () => {
  console.log('クリックされました');
});
```
# 3. クラスの追加・削除
```js
// jQuery
$element.addClass('active');
$element.removeClass('active');
$element.toggleClass('active');

// モダンJavaScript
element.classList.add('active');
element.classList.remove('active');
element.classList.toggle('active');
```

# 4. スタイルの変更
```js
// jQuery
$element.css('backgroundColor', 'red');
$element.hide();
$element.show();

// モダンJavaScript
element.style.backgroundColor = 'red';
element.style.display = 'none';
element.style.display = 'block';
```

# 5. 要素の作成と追加
```js
// jQuery
const $newDiv = $('<div>新しい要素</div>');
$('#container').append($newDiv);

// モダンJavaScript
const newDiv = document.createElement('div');
newDiv.textContent = '新しい要素';
document.getElementById('container').appendChild(newDiv);

//もしくは
insertAdjacentHTML('beforeend', '<div>新しい要素</div>')
//挿入位置の指定
beforebegin //要素の直前
afterbegin //要素の内側の最初
beforeend //要素の内側の最後
afterend //要素の直後
```

# 6. AJAXリクエスト
```js
// jQuery
$.ajax({
  url: '/api/data',
  method: 'GET',
  success: function(response) {
    console.log(response);
  },
  error: function(error) {
    console.error(error);
  }
});

// モダンJavaScript
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

// または async/awaitを使用
async function getData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
```

# 7. アニメーション
```js
// jQuery
$element.fadeIn();
$element.animate({ opacity: 0.5 }, 1000);

// モダンJavaScript
// CSSアニメーション
element.style.transition = 'opacity 1s';
element.style.opacity = '1';

// または Web Animations API
element.animate([
  { opacity: 0 },
  { opacity: 1 }
], {
  duration: 1000,
  fill: 'forwards'
});
```

# 8. 複数要素への処理
```js
// jQuery
$('.items').each(function() {
  $(this).text('更新');
});

// モダンJavaScript
document.querySelectorAll('.items').forEach(item => {
  item.textContent = '更新';
});
```

# 9. フォーム値の取得
```js
// jQuery
const value = $('#myInput').val();

// モダンJavaScript
const value = document.getElementById('myInput').value;
```

# 10. データの保存
```js
// jQuery
$element.data('key', 'value');
const data = $element.data('key');

// モダンJavaScript
element.dataset.key = 'value';
const data = element.dataset.key;
// または
element.setAttribute('data-key', 'value');
const data2 = element.getAttribute('data-key');
```
［競プロ］C++20でコンテナをcoutへの引数にする方法

競技プログラミングで、以下のようにコンテナの中身を改行区切りでSTDINに出力したいとき用。

```cpp:使用例
    std::vector<int> v{1, 2, 3};
    
    std::cout << v << '\n'; // 1\n 2\n 3\n\n
```


# 実装例

簡単には以下のようにすれば良い。

```cpp
#include <iostream>
#include <ranges>
#include <concepts>
#include <string_view>

// range output
template<class T>
  requires std::ranges::input_range<T> && (!std::is_convertible_v<T, std::string_view>)
    std::ostream& operator << (std::ostream& cout, const T& v) {
  for (const auto& i : v) {
    cout << i << '\n';
  }
    
  return cout;
}
```

要素型が`std::ostream`への出力に対応している必要がある点と、`std::string`系を別扱いにしないと文字列が1文字づつ分割されてしまう点に注意。

# 最後に

競プロ向けです。実開発でもデバッグ出力として使えるかもしれませんが、これを使うぐらいなら`std::to_string`を使用するかログ機能を自作したほうがいいと思います。
生成AIによる開発プロセス革命：書籍紹介

![スクリーンショット 2025-01-27 20.42.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3488627/b1b272b4-a48d-0ee0-3ffb-729710535dc3.png)


## はじめに

昨今、生成AIの急速な発展により、ソフトウェア開発の現場は大きな転換期を迎えています。本記事では、書籍「生成AIがもたらす開発プロセス革命」の概要を紹介しながら、これからのIT業界が直面する変革について考察します。

## 概要1：開発プロセスにおける生成AIの実践的活用

### 各開発フェーズでの活用方法
これまではコード生成時にアシスタントとして生成AIを使用することがメインでしたが、今後は開発のすべてのフェーズで生成AIが必要不可欠なものになります。
”上流から下流まですべての工程を自動化できる未来がやってくる”という見方もあります。

#### 設計段階
- アーキテクチャの検討や設計レビューの壁打ち相手として活用
- 設計上の懸念点や考慮すべき点の洗い出し
- 設計代替案の提案と比較検討

#### プログラミング段階
- コードの自動生成による開発速度の向上
- 既存コードのリファクタリング提案
- ベストプラクティスに基づいたコード改善

#### テスト段階
- テストケースの自動生成
- テストコードの作成支援
- バグ修正時の回帰テストコード生成

## 概要2：生成AI（GitHub Copilot）導入による業務改善例

### 主要企業の具体的な成果
例えば、GitHub Copilotをいち早く業務に導入している企業では現時点で以下のような効果が現れています。

#### LINEヤフーの事例
Yahoo!アプリのAndroidチームでは、GitHub Copilotの導入により、エンジニア1人あたり1日1〜2時間のコーディング時間削減を達成しています。7000人規模のエンジニア組織での導入は、その効果の大きさを如実に示しています。

#### サイバーエージェントの驚異的な効率化
2024年8月のGitHub Copilot導入後、わずか30日間で300万行のコードを修正。これは約69万行の手修正を削減したことになり、人的リソースの大幅な節約を実現しています。

#### マネーフォワードのプロセス改善
Pull Request作成時間の12%短縮に加え、提案コードの採用率が約30%増加という具体的な成果を上げています。これにより、開発サイクルの加速化とコード品質の向上を同時に実現しています。

#### ZOZOのコスト効果
2週間という短期間の試験導入で、エンジニア1人あたり月約19ドルという投資で明確な生産性向上を実証。投資対効果の高さを示す好例となっています。

#### GitHub Copilot以外の様々な生成AI導入の効果に関しても書籍で多くの事例が紹介されています。

## 概要3：システム開発内製化の潮流

### 内製化による影響
従来システム開発を外注していた企業が、生成AIを活用して開発の内製化に移行し始めているという内容が記載されています。これにより、SIerに代表される受注企業には以下のような影響が予想されます。

#### 受託開発案件の減少
生成AIの導入により、企業はコーディングや設計の一部を自動化し、エンジニアのリソースを大幅に削減できるようになります。これにより、従来SIerに発注していた開発規模の縮小や機能改修の案件が減少する可能性があります。

#### SIerの役割の変化
内製化が進む中で、SIerは従来の「開発請負業者」としての役割を変えていく必要が生じます。

例えば、以下のような新しいサービスが考えられます。
- 企業のAI活用を支援するAIコンサルティング
- 内製化された開発プロセスを最適化するサービス
- 企業が内製するためのトレーニング支援

####  高度な技術力を持つSIerは優位に
生成AIを活用すれば、単純なコーディング業務の価値は相対的に低下します。

逆に、生成AIでは対応しきれない

- 複雑なアーキテクチャ設計
- システム全体の最適化
- 高度なセキュリティ対策
- 高いトラフィックに対応するパフォーマンスの実現

といった領域に関するより **高度な技術力を持ったSIerは競争力を向上させていく** と考えられます。


## 概要4：エンジニアに求められる新しい能力
書籍内で紹介されている各企業の重役からは、これからのエンジニアに求められる能力が大きく変わることについて繰り返し述べられています。

今後、重要視される個人の能力として、

- 生成AIを効果的に活用するスキル
- 生成AIを用いた問題解決能力
- ビジネス課題を技術で解決する能力
- コミュニケーションスキル

などが紹介されています。

## 危機感と展望：一エンジニアの視点から

私自身、現在の所属企業では生成AIの活用が進んでいない状況を目の当たりにし、本書を読んで大きな危機感を覚えました。上記の事例が示すように、生成AI活用企業と非活用企業の間で生まれる生産性の差は、想像以上に大きくなることが予想されます。

特に注目すべきは、これはもはや **「導入すべきか否か」という選択の段階ではなく、「いかに効果的に活用するか」というフェーズ** に移行していることです。個人レベルでも、生成AIを使いこなせるエンジニアとそうでないエンジニアの間で、劇的なパフォーマンスの差が生まれることは必至でしょう。

## まとめ

本書が示す具体的な成功事例は、**生成AIがもはや実験的な技術ではなく、実務で確実な価値を生み出す段階に達していることを証明しています。** 個人や組織が、この技術変革の波に乗り遅れないためには、積極的な学習と適応が求められます。

特に以下の点に注目して行動を起こすことを推奨します：
- 個人レベルでの生成AIツールの積極的な活用
- 組織レベルでの生成AI導入の検討と推進
- 継続的な学習による生成AI活用スキルの向上
- ビジネス課題に対する生成AIを活用した解決策の提案

## 参考文献
- 「生成AIがもたらす開発プロセス革命」（日経BP社、日経クロステック、2024年12月5日）
ChakraUI version 3.5.1 Fieldsetを使ったフォーム送信の実装方法

# 問題
ドキュメントに、Fieldset(Form機能)を使って、送信する方法がなかったので記載します。

ChakraUI version 3.5.1
Fieldset ドキュメント
https://www.chakra-ui.com/docs/components/fieldset

# Fieldsetとは
Fieldsetとは、関連するフォームコントロール（入力フィールド、ラジオボタン、チェックボックスなど）をグループ化するために使用される。
Chakra UIでは、Fieldset.Root、Fieldset.Legend、Fieldset.Contentなどのサブコンポーネントを提供しており、これらを使ってフォームを見やすく構造化できる。

# 解決方法
```Register.tsx
import React, { useState } from "react";
import { Fieldset, Box, Button, Input, Stack, Text, Link } from "@chakra-ui/react";

// 中略
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // 登録処理を実装
    console.log("User registered:", formData);
  };

// 中略

  return (
    {/* 追加 */}
    <form onSubmit={handleSubmit}> 
      <Fieldset.Root size="lg" maxW="md">
        <Stack>
          <Fieldset.Legend>xx新規登録</Fieldset.Legend>
        </Stack>

        <Fieldset.Content>
          <Field label="好きな英単語 *">
            <Input
              type="text"
              name="id"
              value={formData.id}
              onChange={handleChange}
            />
          </Field>

          <Field label="お名前 *">
            <Input
              type="text"
              {...register("name", {
                required: "お名前は必須入力です。",
                maxLength: {
                  value: 20,
                  message: "お名前は20文字以内にしてください。",
                },
              })}
            />
           {errors.name && (
             <Text color="red.500">{errors.name.message}</Text>
           )}
          </Field>
              
          {/* 他のフィールドもここに追加 */}

        </Fieldset.Content>
      </Fieldset.Root>
    </form>
  );
};

```

# おわりに
ドキュメントは、基本的なことしか書いていないので、実装するには書き方を調べる必要があります。

# 参考
ChakraUI version 3.5.1 Fieldset ドキュメント
https://www.chakra-ui.com/docs/components/fieldset
ChakraUI version 3.5.1 ナビゲーションを配列(map)を使って利用する方法

# はじめに
ドキュメントに、ナビゲーションを配列(map)を使って利用する方法がなかったので記載します。

# 問題
はじめにの通りです。

ChakraUI version 3.5.1
Select (Native)
https://www.chakra-ui.com/docs/components/native-select


# 解決方法
```Register.tsx
<Field label="好きな技術 *">
    <NativeSelectRoot>
        <NativeSelectField placeholder="選択してください">
            {skills.map((skill) => (
                <option value={skill.id}>
                    {skill.name}
                </option>
            ))}
        </NativeSelectField>
    </NativeSelectRoot>
</Field>
```


<img width="50%" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/a1950c8f-7c00-bbd3-c9a8-28ece8c62bde.png">



# おわりに
ドキュメントは、基本的なことしか書いていないので、実装するには書き方を調べる必要があります。
参考になれば幸いです
# 参考
ChakraUI version 3.5.1
Select (Native)
https://www.chakra-ui.com/docs/components/native-select
Tower Records Musicのプレイリスト救済

[Tower Records Musicがサービス終了してしまう](https://music.tower.jp/special/00018)ので[Spotify](https://open.spotify.com/)に乗り換えたのだが、Tower Records Musicでせっせと作ったプレイリストを捨て去るのが悲しかったので救済した話。

世の中には各種アプリのプレイリストを相互変換するサービスがあり、[Soundiiz](https://soundiiz.com/)もそのひとつ。しかし残念ながらTower Records Musicのプレイリストに対応している相互変換サービスは見つからなかった。また、Tower Records Musicの方にも、プレイリストを他のアプリで読み込めるような形式でエクスポートする機能はない。

ただ、[SoundiizはCSV形式の入力が可能](https://soundiiz.com/ja/tutorial/import-excel-to-soundiiz)で、Spotifyへの出力は当然可能なので、Tower Records MusicプレイリストをなんとかしてCSVに変換できれば救済は可能と見込んだ。

Tower Records Musicにはいい感じのプレイリストエクスポート機能がない。仕方がないのでWeb版Tower Records Musicでプレイリストを表示し、そのHTMLからプレイリスト情報を掘り起こすという手段をとる。整理すると以下の流れ：

1. Web版Tower Records Musicでプレイリストを表示
1. 「名前を付けて保存」でHTMLファイルを保存
1. 自作スクリプトでHTMLを読み込み、CSVに出力
1. SoundiizでCSVを読み込みSpotifyにエクスポート

[SoundiizのCSVは「曲名,アーティスト名」というごく簡単なものでもOK](https://support.soundiiz.com/hc/en-us/articles/360010006793-What-is-the-CSV-format-to-import-playlists-and-favorites)なので、曲名とアーティスト名を抽出するHTMLパーサを作る。

ここでChatGPT（じゃなくて本当はBing Copilotを使ったが、ここではChatGPTと書くことにする）に働いてもらうことにした。プレイリストを保存したHTMLをChatGPTのプロンプトに貼って、「ここから曲名とアーティスト名を抽出するPythonスクリプトを書いて」とお願い。ただHTMLがプロンプトに貼るには長大すぎたので、最初の数曲の情報が書いてあるあたりまででカットした。それでもChatGPTはちゃんと動くスクリプトを一発で出してくれた。偉いぞChatGPT。

後はVS CodeのGitHub Copilotに頼りつつ仕上げていくと、あっという間に目的のものが出来てしまった。[こちらに置いてあります](https://github.com/SenYoshida356/towerRecordsMusic2csv)。

実際に動かすと、半分以上の曲はちゃんとSpotify側でも取り込んでくれたが、失敗する曲も多い。昭和の歌謡曲ばかりなこともあって、曲名とアーティスト名だけではマッチングが取れないケースが多いのかな。そこはまぁ、Spotifyのレコメンド機能もあるから、またすぐに立派に育ってくれたので全然OKでした。
【Rancher Desktop】Dockerが「error during connect: in the default daemon configuration…」と出たときの対処法

## 事象
Dcokerコンテナの稼働状況を見ようとコマンドを打ったところ、下記のようなエラーが出ました。

```
C:\Users\pj>docker ps
error during connect: in the default daemon configuration on Windows, the docker client must be run with elevated privileges to connect: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.47/containers/json": open //./pipe/docker_engine: The system cannot find the file specified.
```

### 確認
自分はRancher Desktopを使っているので、Rancher Desktopを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881444/00c0010d-e50d-794c-3e23-2c8fc29d861d.png)
はじめから起動していることもあり問題なさそう。一度再起動しても状況は変わりませんでした。

### 解決方法
インジケータを開くとRancher Desktopのアイコンが赤くなっていました。
Kunernetesがエラーになっているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881444/5864ece5-44d7-64f2-cbe4-9b53bd4afb25.png)


`Quit Rancher Desktop`で一度停止してから、再度Rancher Desktopを起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881444/c68d52a0-7ce1-f92a-a0b4-16eccbba3f1b.png)

再読み込みがおこなわれましｔ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881444/138ab644-48b3-5225-a45f-b7b78451eabe.png)

`docker`コマンドが使えるようになりました。
```
C:\Users\pj>docker ps
CONTAINER ID   IMAGE                        COMMAND                   CREATED              STATUS              PORTS     NAMES
a31602098704   11a5d8a9f31a                 "entry"                   About a minute ago   Up About a minute             k8s_lb-tcp-443_svclb-traefik-eca55e84-cxwhq_kube-system_b420f7b7-8c8d-45b7-b920-ad4a4f498b75_11
157587a617fd   11a5d8a9f31a                 "entry"                   About a minute ago   Up About a minute             k8s_lb-tcp-80_svclb-traefik-eca55e84-cxwhq_kube-system_b420f7b7-8c8d-45b7-b920-ad4a4f498b75_11
08bc55c6cee8   5d221316a3c6                 "local-path-provisio…"   About a minute ago   Up About a minute             k8s_local-path-provisioner_local-path-provisioner-6795b5f9d8-rr944_kube-system_4b4ef622-1521-49b2-83d3-4d50334bd615_11
```
ABC391回答メモ

# 0.はじめに
　先週はコロナに倒れ参加できませんでした。
　まだ本調子ではありませんが、何とか参戦。
　Ａ～Ｃの難易度はそれほどでもなく
　Ｄもちょっとした工夫でＡＣとなり、4問クリア。
　レートも+21の758。このペースで行けば再入緑も
　見えてきました。


# 1.A - Lucky Direction
　4つの方角を変換する辞書を用意し
　入力文字をその辞書に従い変換して出力しました。

　https://atcoder.jp/contests/abc391/submissions/62260259


# 2.B - Seek Grid
　Bにしてはやや複雑な問題。
　とはいえ、パターンとしてはよくあるし、SにTが含まれることも
　保証されているので素直に実装。
　Sの左上から、始点をずらしていき、始点ごとに
　Tと一致するかをチェックする形で実装しました。

　https://atcoder.jp/contests/abc391/submissions/62269993


# 3.C - Pigeonhole Query
　素直に実装すると、TLEになりそうな問題。
　複数の鳩がいる巣は列挙ではなく、数だけ分かればよいので
　出力用の変数を用意し、クエリーごとの処理で
　巣の数の増減を巣の状態と別に管理することで高速化を実現しました。

　【実装】
　　1.以下変数を用意
　　　・変数ans　：複数の鳩がいる巣の数。クエリ２の時個の数を表示
　　　・辞書Ｄ　  ：巣ごとの鳩の数、KEYに巣の番号(初期値は１～N)、値にその巣にいる鳩の数(初期値は１)
　　　・辞書Ｐ　  ：鳩毎のいる巣の位置：ＫＥＹに鳩の番号(初期値は１～N)、値にその鳩がいる巣の番号(初期値は１～N)
　　2.クエリ1の時に以下の処理（入力は変数p(移動する鳩番号)、変数d(移動先の巣の番号)）
　　　-1.変数pd（鳩がもとにいた巣）にP[p]をセット
　　　-2.P[p]にdをセット（鳩を移動）
　　　-3.D[pd]が2の時(鳩の出ていく事に伴い、鳩が元居た巣の鳩の数が複数出なくなる時)、ansから1減算
　　　-4.D[pd]から1減算
　　　-5.D[d]が1の時（鳩が入ってきた事に伴い、鳩の移動先の巣が複数いる巣になるとき）、ansに1加算
　　　-6.D[d]に1加算
　　3.クエリが2の時ansを出力

　https://atcoder.jp/contests/abc391/submissions/62278858


# 4.D - Gravity
　こちらも、素直な実装だと、TLEとかMLEになりそうな問題。
　色々複雑な印象を受けましたが、以下が分かればあとは、クエリ毎に
　Ｔ秒時点でブロックＡが消えているかを判定すればよいことが分かりました。
　　Ａ．対象ブロックが消されるか
　　Ｂ．対象ブロックが（消されるとしたら）いつ消されるか
　　（ＡとＢをまとめて、対象ブロックがいつ消されるか、消されない場合いつが無限大になる
　　　としてもよかったのですが、実装の都合上分けました）

　【考え方】
　　１.Ａ（対象ブロックが消されるか）の求め方
　　　-1.全体で何列削除されるかを求める
　　　　-各列毎のブロック数をカウントする
　　　　-カウントの最小値が消される列数となる（例題1の場合、2、2，1となるので1列消される）
　　　-2.対象ブロックが列内の下から何番目かを求める（読み込み時に保持しておく）
　　　-3.対象ブロックが下からｘ番目で、全体でｙ列削除される場合、ｘ＜＝ｙの時、対象ブロックは削除される
　　2.Ｂ（対象ブロックがいつ消されるか）の求め方
　　　-1.消される列が何秒時点で消されるかを求める
　　　　-ブロックの列数毎の初期位置ｙのうち、一番大きい値を保持しておく（例題１の1列目の場合、1，2，2となるので2）
　　　　-上記値が対象列が消される秒数となる
　　　-2.対象ブロックが列内の下から何番目かを求める（読み込み時に保持しておく）
　　　-3.対象ブロックが下からｘ番目で、ｘ列目が削除されるタイミング＝対象ブロックが削除されるタイミング
　　3.クエリ毎の判断
　　　・クエリ秒数時点で、ブロックＡが消されていればＮｏ、消されていなければＹｅｓとなる。

　https://atcoder.jp/contests/abc391/submissions/62297645


以上
OpenAPI 関係



# OpenAPIドキュメント生成

+ [【アイデア】RESTful統合のためのOpenAPIドキュメントを生成する機能](https://ideas.salesforce.com/s/idea/a0B8W00000GdcNPUAZ/ability-to-generate-openapi-docs-for-restful-integration)
+ [sObject REST API 用の OpenAPI 3.0 ドキュメントを生成する (ベータ版)](https://help.salesforce.com/s/articleView?id=release-notes.rn_api_rest.htm&release=240&type=5)
+ [【Spring 25】sObjects REST API の OpenAPI ドキュメント生成に関するその他の改善 (ベータ版)](https://help.salesforce.com/s/articleView?id=release-notes.rn_api_rest_openapi_spec_beta.htm&release=254&type=5)

# 応答

+ [更新された OpenAPI 仕様 sObject での詳細の表示 (ベータ)](https://help.salesforce.com/s/articleView?id=release-notes.rn_api_rest_openapi_spec_beta.htm&release=250&type=5)

>更新された OpenAPI 仕様 sObject REST API を使用して、詳細ページを表示し、より使いやすい応答を取得します。新しい詳細ページには、現在の要求の状態と発生したエラーの詳細に関する情報が含まれます。
【準1級】統計学実践ワークブック　第7章　例4　中心極限定理とデルタ法


統計検定準1級の勉強のための解説記事となります。下記にご了承いただける方のみ、お読みください。

:::note warn
##### 注意
* 著作権の都合により問題文は掲載せず解説のみの記述となります。
* 独自の解釈により、不適切な表現がある可能性があります。
:::

以下の動画でも解説していますので、見やすいほうでご覧ください。

<iframe width="560" height="315" src="https://www.youtube.com/embed/NgdvrfyBIZY?si=e8TGcyPzw2I0wjd1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


# 学習のポイント

確率変数の列$\{X_n\}$ は平均 $\mu$、分散 $\sigma^2$ の独立同一分布に従うと仮定する。また、$X_1, X_2, ..., X_n$ の標本平均を $\bar{X}_n$ とする。

:::note


・中心極限定理
$\sqrt{n}(\bar{X}_n - \mu)$は、正規分布 $N(0, \sigma^2)$ に分布収束する。

・デルタ法
ある関数 $f$ を用いて $f(\bar{X}_n)$ と表される量を考える。$f(x)$ が連続微分可能のとき、$\sqrt{n}(f(\bar{X}_n) - f(\mu))$ は $N(0, (f'(\mu))^2 \sigma^2)$ に分布収束する。
:::

・確率関数のグラフの変化（中心極限定理）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618291/0434ad3c-a2bc-9cb3-befc-a90eb049b0bb.png)

・累積分布関数のグラフの変化（中心極限定理）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618291/9fc96bc4-6806-6448-b35b-3cfa27cc65f5.png)

# 解法ノート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3618291/739635b7-93cc-341a-4d54-b92ffc6ca770.png)


# 解答

確率変数列 $X_n$ は、平均 $\mu$, 分散 $\sigma^2$ の独立同一分布に従います。

| 標本サイズ$n$ | 標本サイズ $n=1$ の場合 | 標本サイズ $n=2$ の場合 | ... | 標本サイズ $n$ の場合 |
|-----|-------------------|-------------------------------|-----|---------------------------------------|
|標本平均：$\bar{X}_n$| $\bar{X}_1 = \frac{X_1}{1}$ | $\bar{X}_2 = \frac{X_1+X_2}{2}$ | ... | $\bar{X}_n = \frac{X_1 + X_2 + ... + X_n}{n}$ |
| 中心極限定理：$\sqrt{n}(\bar{X}_n - \mu) \xrightarrow{d} N(0, \sigma^2)$ | $\sqrt{1}(\frac{X_1}{1} - \mu)$                | $\sqrt{2}(\frac{X_1+X_2}{2} - \mu)$             | ... | $\sqrt{n}(  \frac{X_1 + X_2 + ... + X_n}{n} - \mu)$            |




$f(x) = x^2$ とおき、デルタ法を適用します。

ここで、$f(\bar{X}_n) = \bar{X}_n^2$, $f(\mu) = \mu^2$, $f'(\mu) = 2\mu$ となるため、
$$
\sqrt{n}(\bar{X}_n^2 - \mu^2) \xrightarrow{d} N(0, (2\mu)^2 \sigma^2)
$$
です。

したがって、$\mu \neq 0$ のとき、$\sqrt{n}(\bar{X}_n^2 - \mu^2)$ の分布収束先は $N(0, (2\mu)^2 \sigma^2)$ です。

| 標本サイズ$n$ | 標本サイズ $n=1$ の場合 | 標本サイズ $n=2$ の場合 | ... | 標本サイズ $n$ の場合 |
| ------------------------ | ----------------------------------------- | ----------------------------------------------------------- | ----- | ----------------------------------------------------------------- |
| 標本平均：$\bar{X}_n$ | $\bar{X}_1 = \frac{X_1}{1}$                   | $\bar{X}_2 = \frac{X_1+X_2}{2}$                  | ... |  $\bar{X}_n = \frac{X_1 + X_2 + ... + X_n}{n}$   |
| デルタ法適用：$\sqrt{n}(\bar{X}_n^2 - \mu^2) \xrightarrow{d} N(0, 4\mu^2\sigma^2)$ | $\sqrt{1}((\frac{X_1}{1})^2 - \mu^2)$ | $\sqrt{2}((\frac{X_1+X_2}{2})^2 - \mu^2)$ | ... | $\sqrt{n}((\frac{X_1 + X_2 + ... + X_n}{n})^2 - \mu^2)$ |







# コラム


#### 標本平均の標準化


   
  標本平均 $\bar{X}_n$ を標準化した確率変数 $Z_n$ は以下のようになります。
   $$Z_n = \frac{\bar{X}_n - E(\bar{X}_n)}{\sqrt{Var(\bar{X}_n)}} = \frac{\bar{X}_n - \mu}{\frac{\sigma}{\sqrt{n}}} = \frac{\sqrt{n}(\bar{X}_n - \mu)}{\sigma}$$
laravelでレスポンスデータをエスケープする方法

# エスケープとは
プログラム内で特殊な意味を持つ文字を、普通の文字として扱うための技法です。

エスケープはセキュリティ上の観点からも非常に重要で、不正なデータがシステムに渡ることを防ぎ、__クロスサイトスクリプティング(XSS)やSQLインジェクションなどの攻撃を防ぎます。__

例えば、Webページにユーザーからの入力をそのまま表示する場合、下記のような悪意のあるスクリプトを表示してしまう可能性があります。このような場合、HTMLエスケープが必要です。

```javascript:悪意のあるスクリプト
<script>alert('Hacked!');</script>
```

```html:エスケープの例
"&lt;script&gt;alert(document.cookie);&lt;/script&gt;"
```


今回はLaravelで、レスポンスデータをエスケープするコードの一例を紹介します。

## 前提条件
PHP 8.3
Laravel 11.4

## 実際のコード
```php:ミドルウェアでのエスケープ処理
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);

        // レスポンスがJSONの場合
        if ($response->headers->get('Content-Type') === 'application/json') {
            $content        = $response->getContent();
            $decodedContent = json_decode($content, true);
            
            // JSONデータをエスケープ
            if (is_array($decodedContent)) {
                array_walk_recursive($decodedContent, function (&$value) {
                    if (is_string($value)) {
                        $value = e($value);
                    }
                });

                $response->setContent(json_encode($decodedContent));
            }
        }
        // レスポンスが文字列の場合
        else if (is_string($response->getContent())) {
            $escapedContent = e($response->getContent());
            $response->setContent($escapedContent);
        }

        return $response;
    }
```

## 最後に
もっと良いコードがあったらコメントで教えて
個人的な備忘録：Python の input() を使ったユーザー入力プログラムを作りながら条件判定してみた

## はじめに

プログラミングの基本である **条件分岐** を理解するために、Python を使って偶数・奇数を判定するシンプルなプログラムを作成しました。  

:::note info
***このコードでは、ユーザーが入力した数値が **偶数か奇数かを自動判定し、結果を表示** します。***
:::

Python の `if-else` 文や、`input()` を使った **ユーザー入力の処理** も学べる内容になっています。  

## 書こうと思ったきっかけ  

プログラミングを学び始めたころ、**「条件分岐 (`if-else`)」** の使い方が理解しやすいシンプルな例を探していました。  

:::note warn
その中で、偶数・奇数の判定は **数学的にも直感的であり、すぐに結果を確認できる** ため、初心者が練習するのに最適だと感じました。
:::

さらに、`input()` を使った **ユーザー入力の処理** も含まれており、実際に動かしながら学べる点が魅力的です。  

## 完成したコード

```test.py
num = int(input("数値を入力してください: "))
if num % 2 == 0:
    print(f"{num} は偶数です")
else:
    print(f"{num} は奇数です")
```

## コードの流れ

- ユーザーが数値を入力し、`int()` で整数に変換する。  
- `num % 2 == 0` を判定し、偶数なら `"◯◯ は偶数です"` を出力。  
- 条件に当てはまらない場合（奇数）、`"◯◯ は奇数です"` を出力。  

## 実際に実行してみる
まず、以下のコマンドを実行して作成したコードの動作を確認します。

```bash
python test.py
```

**実行結果**

```bash
➜  Desktop git:(main) ✗ python test.py
数値を入力してください: 2
2 は偶数です
➜  Desktop git:(main) ✗ python test.py
数値を入力してください: 1
1 は奇数です
➜  Desktop git:(main) ✗ 
```

## コードの解説

### 1. ユーザーから数値を入力する

```py
num = int(input("数値を入力してください: "))
```

- `input()` を使ってユーザーから数値を入力してもらう。  
- `int()` を使って、入力された文字列を整数型に変換する。  

### 2. 偶数かどうか判定する

```py
if num % 2 == 0:
```

- `num % 2` は `num` を `2` で割った余りを求める。  
- 余りが `0` の場合、`num` は偶数。  

### 3. 偶数ならメッセージを出力

```py
    print(f"{num} は偶数です")
```

- `f` 文字列を使用し、`num` の値を `"◯◯ は偶数です"` という形式で出力する。  


### 4. 奇数の場合の処理

```py
else:
    print(f"{num} は奇数です")
```

- `if` の条件に当てはまらない場合（`num` が奇数の場合）、`else` の処理が実行される。  
- `f` 文字列を使用し、`num` の値を `"◯◯ は奇数です"` という形式で出力する。  

### 5. 実行結果の例

```py
数値を入力してください: 7
7 は奇数です

数値を入力してください: 10
10 は偶数です
```

## まとめ  
ここまで読んでいただきありがとうございました。***今回の内容は基本的な部分でしたが、Python の `if-else` 文や、`input()` を振り返ることができました。***

今後もこのような基礎的な部分については丁寧にまとめていこうと思っています。
Laravelで複数のレコードを更新（バルクアップデート）する方法

# バルクアップデートとは
複数のレコードを一括で更新すること

## 前提条件
PHP 8.3
Laravel 11.4

## 実際のコード

```php:リクエストデータは下記のような感じ
array (
  0 => 
  array (
    'id' => 1,
    'target_column' => 'data',
  ),
  1 => 
  array (
    'id' => 2,
    'target_column' => 'data',
  ),
)
```

```php:バルクアップデート処理
    public function bulkUpdate(array $data): bool
    {
        $ids       = [];
        $bindings  = [];
        $caseSql   = "CASE ";
        
        try {
            DB::beginTransaction();
            
            foreach ($saveData as $data) {
                $id         = intval($data['id']);
                $caseSql   .= "WHEN id = ? THEN ? ";
                $bindings[] = $id;
                $bindings[] = strval($data['target_column']);
                $ids[]      = $id;
            }
            
            // SQL文を作成
            $placeholders = implode(',', array_fill(0, count($ids), '?'));
            $updateSql    = "UPDATE table_name SET target_column = $caseSql END WHERE id IN (" . $placeholders . ")";
            $bindings     = array_merge($bindings, $ids);
            
            // バルクアップデート
            $result = DB::statement($updateSql, $bindings);
        
            DB::commit();
            return $result;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
```

## 最後に
もっと良いコードがあったらコメントで教えて
[GAS] clasp利用時の型定義について

## 環境
- Windows 11 Pro
- Node.js v20.17.0
- NPM 9.8.0
- clasp v2.4.2 

## はじめに
claspを利用し、TypeScriptにてGAS開発を行う際に  
GASに関わる部分の型定義の部分をどうするかについてまとめました。

## 課題
下記はラベルオブジェクトを取得するコードだが、戻り値の型を定義する必要がある。  
VSCodeでは「: GmailLabel」と型予測が表示されるが、それをそのまま入力してもエラー判定となる
```ts
  const LabelObj = GmailApp.getUserLabelByName("ラベル名");
  // VSCode上では、LabelObjに入る値の型として「: GmailLabel」が表示される
```

##  解決法
import文を記載する必要はなく  
下記のように「GoogleAppsScript」からドット記法を用いて「GmailLabel」を持ってくる。
```ts
  const LabelObj: GoogleAppsScript.Gmail.GmailLabel = 
    GmailApp.getUserLabelByName("ラベル名");
```

<br>

また、上記コードではGmail操作を行う場合のものを記載したが  
スプレッドシートの操作を行う際も同様に、「GoogleAppsScript」から型定義を引っ張ってくる。
```ts
  const SpreadSheetObj: GoogleAppsScript.Spreadsheet.Spreadsheet = 
    SpreadsheetApp.openById("スプレッドシートID");
```

## 参考
- How to import types from @types/google-apps-script?
  - https://stackoverflow.com/questions/77165173/how-to-import-types-from-types-google-apps-script
柔道整復師の業務が多すぎる問題(開発日誌#1)

# 柔道整復師の業務が多すぎる問題

## 1. 施術関連業務
- 患者の問診・診察
- 整復固定
- 後療管理
- 生活指導
- 物理療法
- 施術計画の作成・管理
- 患者のメンタルケア
- 生活運動量調整
- 予約管理とスケジュール調整

## 2. 事務・会計業務
- レセプト作成・請求業務
- 健康保険組合・労災・自賠責保険への対応
- 療養費支給申請の記入・提出
- 施術録（カルテ）の記入・管理
- 領収書・明細書の発行
- 売上管理・会計処理
- 物品・消耗品の在庫管理と発注

## 3. 患者対応・接客業務
- 新規患者の対応・説明
- 施術内容や治療計画の説明
- 患者からの質問対応・健康相談
- 電話・メール対応
- クレーム対応
- 口コミ・紹介などの対応
- 書類対応

## 4. 経営・運営管理
- 集客・マーケティング（SNS、広告）
- ホームページの管理・更新
- 施術所の清掃・衛生管理
- 従業員の教育・シフト管理
- 設備の点検・修理手配
- 行政・保険機関とのやりとり
- 法改正・保険制度の情報収集と対応

## 5. 診療報酬請求・監査対応
- 療養費の請求事務
- 不支給や返戻の対応
- 保険者・審査機関との折衝
- 突然の監査対応（カルテや施術内容のチェック）
- 適正請求のための情報収集

## 6. 教育・研修・資格更新
- 技術・治療法の勉強
- セミナーや学会への参加
- 地域会合への参加・活動
- スタッフへの指導・教育
- 資格更新のための講習受講

## 7. その他の業務
- 学校・スポーツチームなどへの外部指導
- 地域イベントやボランティア活動
- 施術所のブランディング・差別化戦略
- 他業種との連携



その中でも文章作成・記入の占める時間は膨大なものになる。これらの1つでも効率化できれば、我々の本分である施術にもっとウエイトを割り当てられるのではないか。
個人的な備忘録：Python の平均値の計算からリストとループをアウトプットしてみた

## はじめに
プログラミングでは、データの集計や統計計算が頻繁に求められます。平均値の計算は、その中でも基本的で実用的な処理の一つです。  

:::note info
**Python では、`sum()` や `len()` などの便利な関数を使うことで、シンプルなコードで平均値を求めることができます。**
:::

***本記事では、Python を使ってリストの平均値を計算する方法を学びます。*** 

## 書こうと思ったきっかけ  
プログラミング初心者の方にとって、リストの扱いと基本的な集計処理を理解することは重要です。  

:::note warn
特に、`sum()` や `len()` などの組み込み関数は、データ処理を効率化するうえで欠かせません。  
:::

このコードはシンプルですが、Python のリストとループの基本を学ぶのに最適な例です。  

**また、実際の開発ではデータの平均値を求めるシーンも多いため、初心者の方がすぐに役立てられる内容としてまとめました。**

## 完成したコード

```test.py
numbers = [10, 20, 30, 40, 50]
total = sum(numbers)
average = total / len(numbers)
print(f"平均値: {average}")
```

### コードの流れ

- 数値のリストを作成  
  - `[10, 20, 30, 40, 50]` というリストを用意する。  

- 合計値を計算  
  - `sum(numbers)` を使ってリスト内の全ての数を合計する。  

- 平均値を計算  
  - 合計値を `len(numbers)`（リストの要素数）で割って平均値を求める。  

- 結果を出力  
  - `print(f"平均値: {average}")` で計算結果を表示する。  

## 実際に実行してみる
まず、以下のコマンドを実行して作成したコードの動作を確認します。

```bash
python test.py
```

**実行結果**

```bash
平均値: 30.0
```

## コードの解説

### 1. リストの作成

```py
numbers = [10, 20, 30, 40, 50]
```

- 5 つの数値が入ったリストを作成する。  
- `numbers` という変数に `[10, 20, 30, 40, 50]` を代入する。  

### 2. リストの合計を求める

```py
total = sum(numbers)
```

- `sum()` 関数を使って、リスト内の数値をすべて合計する。  
- `sum([10, 20, 30, 40, 50])` の結果は `150`。  


### 3. 平均値を計算する

```py
average = total / len(numbers)
```

- `len(numbers)` でリストの要素数（5）を取得する。  
- `total` を `len(numbers)` で割ることで平均値を求める。  
- `150 / 5 = 30.0` となる。  

### 4. 結果を出力する

```py
print(f"平均値: {average}")
```

- `f` 文字列を使用し、計算結果を `"平均値: 30.0"` の形式で出力する。  
- `f"平均値: {average}"` により、変数 `average` の値が文字列内に埋め込まれる。  

## まとめ  
ここまで読んでいただきありがとうございました。  ***今回の内容は基本的な部分でしたが、私自身もリストとループを振り返ることができました。***  

今後もこのような基礎的な部分については丁寧にまとめていこうと思っています。
PHPで「PHPで文を記述する」の動作を確認してみた。

# 概要

PHPで「PHPで文を記述する」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/php/ini/index3.html

# 実装

以下のファイルを作成しました。

```sample.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPテスト</title>
</head>
<body>

<p>
<?php
echo "こんにちは。<br>";
  echo "こんにちは。<br>";
echo   "こんにちは。<br>";
echo
"こんにちは。<br>";
?>
</p>

</body>
</html>
```

以下のコマンドを実行しました。

```
$ php sample.php 
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>PHPテスト</title>
</head>
<body>

<p>
こんにちは。<br>こんにちは。<br>こんにちは。<br>こんにちは。<br></p>

</body>
</html>
```

# まとめ

何かの役に立てばと。
日本の標準地域メッシュコードを緯度経度に変換する（中心補正つき）

n番煎じの感じがありますが、作ってしまったので書いておきます。

地域メッシュコードとはなんぞや、というのはまず以下のページを参照してください。
[地域メッシュ統計について - 総務省統計局](https://www.stat.go.jp/data/mesh/m_tuite.html)
[メッシュデータについて - 国土数値情報ダウンロードサイト](https://nlftp.mlit.go.jp/ksj/old/old_data_mesh.html)

さてメッシュコードを緯度経度に変換する際、注意する点の一つは、「メッシュコードが示す座標は、該当のメッシュの南西端（左下）である」ということです。
メッシュコードの中心座標を求めたい場合、メッシュの大きさに応じて補正する必要があります。

というわけで、補正機能付きの関数を書きましょう。

```python
def mesh_code_to_latlon(mesh_code: str, apply_correction: bool = True):
    """
    日本の地域メッシュコードを緯度・経度に変換する。

    標準地域メッシュ（4桁）から5次メッシュ（10桁）までをサポート。
    無効なメッシュコードの場合は `ValueError` を発生させる。

    Parameters
    ----------
    mesh_code : str
        変換する地域メッシュコード。ハイフン（"-"）が含まれていても自動で除去される。
    apply_correction : bool, optional
        メッシュの中心点を取得するかどうか（デフォルトは `True`）。
        `False` にすると、メッシュの南西端（左下）の座標を返す。

    Returns
    -------
    tuple[float, float]
        (緯度, 経度) のタプルを返す。

    Raises
    ------
    ValueError
        メッシュコードが無効（4桁未満、5桁・7桁、または10桁超過）である場合。
    """

    # ハイフンを削除
    mesh_code = mesh_code.replace("-", "")

    # 無効なメッシュコードのチェック
    if not mesh_code.isdigit() or len(mesh_code) not in {4, 6, 8, 9, 10}:
        raise ValueError("Invalid mesh code.")

    # メッシュコードの種類を特定
    mesh_level = {4: 0, 6: 1, 8: 2, 9: 3, 10: 4}.get(len(mesh_code))

    # 各メッシュレベルの増分（緯度・経度の単位距離）
    increments = [
        (40 / 60, 1),              # 第1次メッシュ
        (5 / 60, 7.5 / 60),        # 第2次メッシュ
        (30 / 3600, 45 / 3600),    # 第3次メッシュ
        (15 / 3600, 22.5 / 3600),  # 第4次メッシュ（2分の1地域メッシュ, 1/2メッシュ）
        (7.5 / 3600, 11.25 / 3600) # 第5次メッシュ（4分の1地域メッシュ, 1/4メッシュ）
    ]

    # メッシュレベルごとに座標を加算
    for i, (lat_inc, lon_inc) in enumerate(increments):
        # メッシュコード内の該当位置を取得
        if i == 0:  # 第1次メッシュ（2桁）
            # 基本の緯度・経度
            latitude = int(mesh_code[:2]) / 1.5
            longitude = int(mesh_code[2:4]) + 100
        else:
            if i < 3:  # 第2・3次メッシュ（2桁）
                index = 4 + (i - 1) * 2
            else:  # 第4・5次メッシュ（1桁）
                index = 8 + (i - 3)

            # メッシュレベルごとの加算処理
            if i < 3:
                # 2桁番号に応じた緯度経度の加算
                latitude += int(mesh_code[index]) * lat_inc # 緯度方向の番号
                longitude += int(mesh_code[index + 1]) * lon_inc # 経度方向の番号
            else:
                # 1桁番号で縦横2分割した緯度経度の加算
                code_value = int(mesh_code[index])  # 1桁の番号
                latitude += ((code_value - 1) // 2) * lat_inc  # 緯度方向の移動
                longitude += ((code_value - 1) % 2) * lon_inc  # 経度方向の移動

        # 最終レベルの場合、補正値を適用して返す
        if i == mesh_level:
            if apply_correction:
                return latitude + lat_inc / 2, longitude + lon_inc / 2
            else:
                return latitude, longitude
```

特にこれ以上説明することはないです。
コードは好きに使ってください。
最近はAIがこういうのも簡単に作ってくれるので良いですね。
柔道整復師のカルテ大変すぎ問題(開発日誌#2)

# 柔道整復師のカルテ作業の負担

## 1. 1日の業務時間の内訳（AM9:00〜PM9:00勤務）
柔道整復師の業務は施術が中心だが、カルテ作成や事務作業も必要不可欠である。  
ここでは、カルテ作業時間が1日の中でどれだけの割合を占めるかを試算する。

### 1日の業務スケジュール（概算）
| 時間帯        | 業務内容          |
|--------------|----------------|
| 9:00 - 12:30  | 午前の施術（3.5時間） |
| 12:30 - 14:30 | 昼休憩（2時間） |
| 14:30 - 21:00 | 午後の施術（6.5時間） |
| 21:00 - 22:00 | カルテ記入・事務作業（1時間） |
| **合計** | **勤務時間 13時間** |

### 1日の業務時間（合計）
- **施術時間**：10時間（午前3.5時間 + 午後6.5時間）
- **休憩時間**：2時間
- **カルテ記入・事務作業**：1時間（実際には増減あり）

---

## 2. 1患者あたりのカルテ作業時間の試算
### 記録する文章量（手書きカルテの場合）
- **施術内容の記録**（約50〜75文字）
- **症状の変化や経過**（約25〜50文字）
- **その他、施術中の気づき**（約25文字）
- **合計：約100〜150文字**

### 手書きでの所要時間（概算）
- **1分間に約50文字**程度記入可能と仮定
- 100〜150文字の場合 → **約2〜3分**

---

## 3. 1時間でカルテ作業が可能な患者数（概算）
### 1時間のうちに記録できる人数
- **1患者あたり2〜3分の記録時間**
- **1時間（60分） ÷ 2〜3分**  
  → **20〜30人**

### 1日あたりの患者数との比較
- **1日50人を診る場合**
  - 1時間では記録しきれず、**20人分のカルテが未記入になる可能性**
  - **追加で30〜60分の時間が必要**（業務終了が22:30〜23:00になる）
- **1日30人を診る場合**
  - 1時間で記録可能
  - ただし、他の事務作業（会計処理・レセプト作成）もあるため、**業務後の負担はゼロではない**

---

## 4. 現状の問題点
- **カルテ作成時間は1時間でギリギリ、患者数が多い日は足りなくなる**。
- 施術時間が長いため、業務終了後にまとめて記入することが増える。
- **21時に施術が終わっても、カルテ記入の負担で帰宅が遅れることがある**。
- **事務作業の負担が大きく、施術以外の時間が圧迫されている**。

---

## 5. まとめ
- 勤務時間（AM9:00〜PM9:00）で施術に10時間を費やし、**カルテ作業は1時間で対応**。
- **1日30人なら1時間でギリギリ記録可能**だが、**50人の場合は追加時間が必要**。
- **柔道整復師の業務負担の一因はカルテ作業の多さ**であり、効率化が大きな課題となっている。




# 紙カルテの問題

## 1. 紙カルテの物理コスト

### 1-1. 保管スペースの問題
- **1患者あたりの記録量が増えるほど、保管に必要なスペースも増大する**。
- 施術所の規模によっては、一定の期間ごとに**古いカルテを移動・整理する手間**が発生。
- **法律上の保管義務**があるため、すぐに破棄することはできず、長期保存が必要。

### 1-2. 書類管理の負担
- **紙のカルテは1つの施術所内でしか管理できない**ため、分院や別の施術所との情報共有が難しい。
- **ファイリングのルールが統一されていないと、カルテを探す時間が増える**。
- **経年劣化**によるインクの薄れや紙の破損で、内容が読みにくくなる可能性がある。

---

## 2. 紙カルテの時間コスト

### 2-1. 記入時間の負担
- **1患者ごとに施術内容を記録する必要があり、手書きの場合は時間がかかる**。
- **来院頻度の高い患者ほど、記録の量が増え、記入作業の負担が大きくなる**。
- 修正や追加記録の際に、**訂正線を使うため、書き直しの手間が発生**。

### 2-2. カルテの検索時間
- **保管場所やファイリングのミスがあると、カルテを探すのに時間がかかる**。
- **特定の期間の記録を振り返る場合、1ページずつ確認しなければならない**。
- **1日30人の患者のカルテを探すだけで、業務全体に影響が出る**。

### 2-3. レセプト作成の手間
- **手書きの記録を基に手入力でレセプトを作成するため、ミスのリスクがある**。
- **計算ミスや記載漏れがあると、訂正作業が発生し、時間のロスにつながる**。
- **保険請求時に確認が必要な場合、過去のカルテを1件ずつ探す必要がある**。

---

### 3-1. 月額コストの詳細概算
| 項目 | 月額コスト（円） |
|------|------------|
| カルテ用紙代 | 6,000 |
| バインダー・ファイル代 | 1,280 |
| 文房具（ペン・インク） | 2,300 |
| 収納用具（キャビネット・書類ボックス） | 1,250 |
| 事務作業の人件費 | 65,000 |
| **合計** | **75,830** |

### 3-2. コストの具体的な内容
- **カルテ用紙代**：1患者あたり1.5枚 × 80人 × 10回の来院 × 5円 = **6,000円**
- **バインダー・ファイル代**：1バインダー800円で50人分保管 → **1,280円**
- **文房具（ペン・インク）**：
  - ボールペン（100円 × 3本）= **300円**
  - インク（2,000円 × 1個）= **2,000円**
- **収納用具（キャビネット・書類ボックス）**：
  - キャビネット（30,000円を120ヶ月で分割）= **250円**
  - 書類整理ボックス（2,000円を2ヶ月に1個のペースで購入）= **1,000円**
- **事務作業の人件費**：1,300円 × 2時間 × 25日 = **65,000円**

---

## 4. まとめ
- **カルテの記入・管理にかかる時間と手間が大きな負担となる**。
- **検索や修正作業に時間がかかり、施術以外の業務負担が増加する**。
- **スペース確保や書類管理のコストが積み重なり、経済的な負担も無視できない**。
- **業務の効率化には、紙カルテのデジタル化が避けられない課題となっている**。




人件費を除いたとしても月1万円弱はコストがかかっていると思うと驚愕だ。
paizaラーニングレベルアップ問題集の「半角スペース区切りの2つの入力」をやってみた。

paizaラーニングレベルアップ問題集の[半角スペース区切りの2つの入力](https://paiza.jp/works/mondai/d_rank_level_up_problems/d_rank_level_up_problems__stdin_boss)をやってみました。
- - -
###### 問題
https://paiza.jp/works/mondai/d_rank_level_up_problems/d_rank_level_up_problems__stdin_boss
- - -
###### 参考
- 「[1行の入力](https://paiza.jp/works/mondai/d_rank_level_up_problems/d_rank_level_up_problems__stdin_1)」⇒「[1行のデータの入力](https://qiita.com/anonymously115/items/50ed25f7748f1cb1b426)」をやってみた
- 「[2行の入力](https://paiza.jp/works/mondai/d_rank_level_up_problems/d_rank_level_up_problems__stdin_2)」⇒「[入力された2つの文字列を出力](https://qiita.com/anonymously115/items/05aac49a652dcbab9b2d)」をやってみた
- 「[1つの入力](https://paiza.jp/works/mondai/d_rank_level_up_problems/d_rank_level_up_problems__stdin_3)」⇒「[1つのデータの入力](https://qiita.com/anonymously115/items/50ed25f7748f1cb1b426)」をやってみた
- - -
###### C
```c
#include <stdio.h>

int main() {
	char s[11], t[11];
	scanf("%s %s", s, t);
	puts(s);
	puts(t);
	return 0;
}
```
- - -
###### C++
```cpp
#include <iostream>
using namespace std;

int main() {
	string s, t;
	cin >> s >> t;
	cout << s << endl;
	cout << t << endl;
	return 0;
}
```
- - -
###### C#
```cs
using System;

class Program
{
	public static void Main()
	{
		string[] s = Console.ReadLine().Split();
		Console.WriteLine(s[0]);
		Console.WriteLine(s[1]);
	}
}
```
- - -
###### Go
```go
package main
import "fmt"

func main() {
	var s, t string
	fmt.Scan(&s, &t)
	fmt.Println(s)
	fmt.Println(t)
}
```
- - -
###### Java
```java
import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.println(sc.next());
		System.out.println(sc.next());
		sc.close();
	}
}
```
- - -
###### JavaScript
```js
const [s, t] = require("fs").readFileSync("/dev/stdin", "utf8").trim().split(' ');
console.log(s);
console.log(t);
```
- - -
###### Kotlin
```kotlin
import java.util.*;

fun main() {
	val sc = Scanner(System.`in`)
	println(sc.next())
	println(sc.next())
	sc.close()
}
```
- - -
###### PHP
```php
<?php
	[$s, $t] = explode(' ', trim(fgets(STDIN)));
	echo $s, PHP_EOL;
	echo $t, PHP_EOL;
?>
```
- - -
###### Perl
```pl
my ($s, $t) = split(' ', <STDIN>);
print "$s$/";
print "$t$/";
```
- - -
###### Python3
```py
s, t = input().split()
print(s)
print(t)
```
- - -
###### Ruby
```rb
s, t = gets.split
puts s
puts t
```
- - -
###### Scala
```scala
import scala.io.StdIn._

object Main extends App{
	val Array(s, t) = readLine().split(' ')
	println(s)
	println(t)
}
```
- - -
###### Swift
```swift
let s = readLine()!.split(separator: " ")
print(s[0])
print(s[1])
```
DeepSeekに画像を入力する【Colab付き】

画像を入力してテキストを生成する例です。
速い、安い、かんたん。

1, vllmをインストール

```shell
pip install vllm
```

2, deepseekマルチモーダルモデルを初期化

```python
from vllm import LLM
llm = LLM(model="deepseek-ai/deepseek-vl2-tiny", hf_overrides={"architectures": ["DeepseekVLV2ForCausalLM"]},dtype='float16')
```

3,実行(T4GPU 0.65秒)

```python
image = PIL.Image.open("menu.jpg")
prompt = "USER: <image>\nHow much is an espresso?\nASSISTANT:"

outputs = llm.generate({
    "prompt": prompt,
    "multi_modal_data": {"image": image},
})

for o in outputs:
    generated_text = o.outputs[0].text
    print(generated_text)
```

![pexels-cax0000-3636100.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/6b617bec-38cc-1c11-9912-2e031ccc1f5c.jpeg)

>The price for an espresso is listed as 4.0.
エスプレッソの価格は 4.0 と記載されています。

マルチモーダルがこんなにかんたんに使えるとありがたいです。

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLを使ったアプリを作っています。
機械学習関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
リッチテキストのまとめ





+ [リッチテキストにあるURLだけを抜き取る](https://qiita.com/Keiji_otsubo/items/608419daf4e103851ed9)

+ [リッチテキスト項目を入力していて文字がぼやける現象](https://qiita.com/Keiji_otsubo/items/7e0a8be3bd5c2810b96f)
+ [ナレッジ記事のリッチテキストエリア項目に追加された画像が Lightning Experience に表示されない](https://qiita.com/Keiji_otsubo/items/dc64a1794be080c99612)
+ [数式の制限：リッチテキスト領域フィールドの制限](https://qiita.com/Keiji_otsubo/items/5b327a653b37194904ea)


# フロー

+ [フロー内でリッチ テキストをプレーン テキストに変換する](https://qiita.com/Keiji_otsubo/items/83418c7df277a07f1756)
+ [フローのテキストテンプレート](https://qiita.com/Keiji_otsubo/items/9aadfbcfef71d409c578)
+ [フローのコアアクション「メールを送信」の本文にHYPERLINKは使えません](https://qiita.com/Keiji_otsubo/items/278fa2a2d003fc563643)
フロー内でリッチ テキストをプレーン テキストに変換する



+ [Flow を使用してリッチ テキストをプレーン テキストに変換する](https://salesforcetime.com/2023/12/13/using-flow-to-convert-rich-text-to-plain-text/)

リッチ テキストには、太字、斜体、色、ハイパーリンクなどの書式設定が含まれており、視覚的なプレゼンテーションが強化されます。対照的に、プレーン テキストは書式設定されていない文字で構成され、シンプルさと普遍的な可読性を維持します。特定のシナリオでは、リッチ テキストをプレーン テキストに変換することが必要になる場合があります。この変換によりコンテンツが簡素化され、プレーン テキストのみをサポートするシステムとの互換性が確保され、データ処理が合理化されます。

Apex の stripHtmlTags メソッドを使用して、リッチ テキストをプレーン テキストに変換することができます。このメソッドは、文字列から HTML タグを削除し、プレーン テキストを返します。この投稿では、リッチ テキストをプレーン テキストに変換する呼び出し可能なアクションを見つけることができます。

# 考慮事項
+ この操作により、すべての HTML タグが削除されます。つまり、改行も失われます。
+ このアクションにより、空白文字も削除されます。
+ stripHtmlTags 関数はタグを再帰的に削除しないため、返される文字列にタグが残っている可能性があります。
+ stripHtmlTags 関数は将来廃止される可能性があります。

以下に古い投稿

+ [JSON を使用してリッチ テキスト フィールドから書式設定せずにテキストのみを取得するにはどうすればよいでしょうか?](https://salesforce.stackexchange.com/questions/32049/how-to-retrieve-just-the-text-without-formatting-from-a-rich-text-field-using-js)

>リッチ テキスト エリア フィールドが数式内で使用される場合、数式が実行される前に HTML タグが削除されます。たとえば、リッチ テキスト エリア フィールドが検証ルールの条件内で使用される場合、評価の前に HTML タグが削除されます。

https://help.salesforce.com/HTViewHelpDoc?id=fields_using_rich_text_area.htm&language=en_US

ただ気になる回答　2023年時点

残念ながら、Flow の Formula エンジンは HTML を削除しないようです。Flow を使用してこのソリューションを再現しようとしましたが (新しいワークフローを作成できなくなったため)、長いテキスト領域フィールドが HTML でいっぱいになってしまいました。最新のソリューションを見つけたいです。システムで HTML を簡単に削除できるのはばかげているように思えますが、プログラムでそれを実行する方法はありません。

+ [フロー内でリッチ テキストをプレーン テキストに変換しますか?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KCAQt0AP)

こんにちは。Salesforce フローでリッチテキストをプレーンテキストに変換し、同時に書式も修正したいと考えています。Apex を使用して変換する方法はわかりましたが、リッチテキストが箇条書きの場合、プレーンテキストに変換すると、箇条書きと箇条書き間の段落スペースも削除されます。