OmniAICreator/Qiita-1.07M huggingface dataset:
【JavaScript・学習ログ6】ボタンの表示/非表示、非同期処理

# 目次
[ボタンの表示と非表示](#ボタンの表示と非表示)
[非同期処理](#非同期処理)

<font color=gray>教材:侍テラコヤ『JavaScriptの基礎を学ぼう』https://terakoya.sejuku.net/programs/60/chapters</font>

## ボタンの表示と非表示
スクロール量に合わせてイベント処理をするコードを記述する。

(1)html: スクロール用のdiv要素を作成
```html
<!DOCTYPE html>
<html>
<head>
	<title>イベント処理(スクロール)</title>
	<meta charset="UTF-8">
	<link rel="stylesheet" href="css/scroll.css">
</head>

<body>
	<div class="blue-screen">1ページ目</div>
	<div class="red-screen">2ページ目</div>
	<div class="blue-screen">3ページ目</div>
	<div class="red-screen">4ページ目</div>
	<div class="blue-screen">5ページ目</div>
	<a id="back-btn" href="#">TOPに戻る</a>
</body>

</html>
```
(2)css: div要素の背景と「TOPに戻る」ボタンを装飾する
```css
.blue-screen {
	height: 100vh;
	color: #fff;
	font-size: 5em;
	text-align: center;
	background-color: #88f;
}

.red-screen {
	height: 100vh;
	color: #fff;
	font-size: 5em;
	text-align: center;
	background-color: #f88;
}

#back-btn {
	display: none;

	padding: 40px 10px;
	background-color: #fff;
	color: #000;
	text-decoration: none;
	border-radius: 50%;

	position: fixed;
	bottom: 10px;
	right: 10px;
}
```
(3)js: 画面のスクロール量を取得する
※**document.scrollingElement.scrollTop**プロパティを使用する。
※画面全体を対象とするため、windowオブジェクトを指定する。

(4)js: スクロール量が300px以上であれば「TOPに戻る」ボタンを表示する
※displayプロパティをinlineに変更する。

(5)js: スクロール量が300px未満であれば「TOPに戻る」ボタンを非表示にする
※displayプロパティをnoneに変更する。

```javascript
const backBtn = document.getElementById('back-btn');
window.addEventListener('scroll', () => {
	const scrollValue = document.scrollingElement.scrollTop;
	if (scrollValue >= 300) {
		backBtn.style.display = 'inline';
	}
	else {
		backBtn.style.sisplay = 'none';	
	}
});
```
<hr>

## 非同期処理
処理実行中にほかの処理も並行して実行できる仕組みのこと。
上から順番に実行される処理を同期処理という。

## 作成方法
(1) コンソールに下記を表示する。
![スクリーンショット 2023-11-04 105207.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3520365/78ca9093-ea18-f5df-3e78-46fcde951512.png)
(2) 待ち時間を設定し、非同期処理を実行する。
実行するまでに秒数を設ける際に、**setTimeout関数**を使用する。
処理4は2秒待ってから出力されるように設定する。
```
console.log('処理1');
console.log('処理2');
console.log('処理3');

setTimeout(() => {
	console.log('処理4(非同期処理)');
},2000);

console.log('処理5');
console.log('処理6');
```
【実行】
![スクリーンショット 2023-11-04 110144.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3520365/60b2f7b5-5de0-8a5b-839d-cf56a32e7af8.png)

<hr>
ARG命令書かなくても、build-argで環境変数を渡せる

ホストのproxy設定を渡したいとする。
ARG命令書かなくても、build-argで渡せる

# 成功例
* proxy環境下でvimをインストールする

```Dockerfile
# ARG http_proxy　★コメントアウト
# ARG https_proxy　★コメントアウト
RUN apt-get install -y vim
```
コマンド実行
>docker build --build-arg http_proxy=(your proxy) --build-arg https_proxy=(your proxy) .
Laravel APIのリクエストとレスポンスログを出力する

:::note
2024/4/10 追記: こちらの記事をライブラリ化しました。

https://qiita.com/ucan-lab/items/dc33ab7df03ebf485898
:::

## 過去記事

https://qiita.com/ucan-lab/items/bfd15b096a916f811468

## 目的

リクエストログと合わせてレスポンスログを出力したい要望がありました。
また、どのリクエストがどのレスポンスと紐付くのかぱっと見わかりやすくするためリクエストIDを付けたい。

## 環境

- PHP 8.2.11
- Laravel Framework 10.30.1

## 手順

```shell
$ php artisan make:middleware ApiLogger
```

```php:app/Http/Middleware/Kernel.php
<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

final class ApiLogger
{
    public function handle(Request $request, Closure $next): JsonResponse
    {
        $requestId = uniqid('req-', false);

        Log::debug($requestId . ':' . $request->method() . ':' . $request->fullUrl(), [
            'user' => $request->user()?->id,
            'headers' => $request->headers->all(),
            'body' => $request->all(),
        ]);

        /** @var JsonResponse $response */
        $response = $next($request);

        Log::debug($requestId, $response->getData(true));

        return $response;
    }
}
```

- ※ Apiのコントローラ側で JsonResponse 返す前提を想定してます。

`app/Http/Kernel.php` を編集します。

```php:app/Http/Kernel.php
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    // ...

    protected $middlewareGroups = [
        // ...
        'api' => [
            // ...
            \App\Http\Middleware\ApiLogger::class, // 追加
        ],
    ];

    // ...
}
```
2023/11/5主にITとかセキュリティの記事

ステルス型AndroidマルウェアMMRatによるオンラインバンキングを狙った金融犯罪(11/3)
https://www.trendmicro.com/ja_jp/research/23/k/mmrat-carries-out-bank-fraud-via-fake-app-stores.html

バカラが不正アクセス被害、国内で個人情報30万件が流出の可能性(11/2)
https://xtech.nikkei.com/atcl/nxt/news/18/16221/

経済産業省が重要視する「ASM」の本質に迫る! アタックサーフェスの増加に対応するには?(11/2)
https://news.mynavi.jp/techplus/kikaku/20231102-2804286/

生成AI悪用の動き：「倫理」から外れ、不正目的に開発されるプログラムも(11/2)
https://www.nippon.com/ja/in-depth/d00948/

ランサム攻撃、業務復旧まで2週間　調査で被害の深刻さ際立つ(11/2)
https://news.yahoo.co.jp/articles/71e71a8920e9abb1108338e5020e36bfecb6c86b

JVN#14762986
e-Taxソフトにおける XML 外部実体参照 (XXE) に関する脆弱性(11/2)
https://jvn.jp/jp/JVN14762986/index.html

AeyeScan blog 第4回「生成AIオプション詳説：セカンドオーダーXSSの自動検出に成功」(11/2)
https://scan.netsecurity.ne.jp/article/2023/11/02/50186.html

2023年11月2日　当社サーバへの不正アクセスについて グラントマト株式会社
https://www.grantomato.jp/topics/topics.php?id=824#topicpath

EU、サイバーセキュリティで連携 SOCを活用(11/2)
https://news.nicovideo.jp/watch/nw13697419

君は怒れるユニコーンを見たか？ OSS の脆弱性を一掃しようとした研究者の記録(11/2)
https://scan.netsecurity.ne.jp/article/2023/11/02/50185.html

東京大学情報基盤センターでユーザ情報の一部が閲覧可能、サーバの設定ミス原因(11/2)
https://scan.netsecurity.ne.jp/article/2023/11/02/50183.html

偽ウォレットアプリやテレグラム（Telegram）で仮想通貨詐欺が急増(11/2)
https://nextmoney.jp/?p=61999

「ランサムウェア攻撃の犯罪集団から連絡」、情報公開の義務化に企業は戦々恐々(11/2)
https://news.mynavi.jp/techplus/article/svalley-977/

AI安全サミット開幕 最先端AI “対応急務”とする宣言採択(11/2)
https://www3.nhk.or.jp/news/html/20231102/k10014245011000.html

知ってる？「QR」コードの意味。実は開発者は日本人だった(11/2)
https://nordot.app/1092571835809629085

マイナ制度は「なりすまし犯罪の温床」　情報システム学会が制度設計に根本的な問題と提言(11/2)
https://www.zaikei.co.jp/article/20231102/744174.html

“筋肉質バカ”な日本の製造業、効率一辺倒のサプライチェーンから脱却せよ(11/3)
https://news.yahoo.co.jp/articles/2c6e9e5ef81291a110a0c796d7af80662d08d039

海洋国家の命綱：情報（橋本 量則）(11/2)
https://agora-web.jp/archives/231101055107.html

『コンピュータウィルスシミュレーター』12月1日発売、実際のウィルスさながらの恐怖とトラウマを“安全に”味わえる 。怖いけどちょっと懐かしい画面でウィルスの脅威を疑似体験しよう(11/3)
https://news.yahoo.co.jp/articles/031f98e0abf9789490db33df90c1ffb886984ced?source=rss

近商ストア、会員らの情報漏洩か(11/3)
https://www.nikkei.com/article/DGKKZO75841070S3A101C2LKB000/

ビットコイン・ホワイトペーパーから15年、ウォール街がビットコインを飲み込む？(11/3)
https://www.coindeskjapan.com/207256/

急増する自動車盗にローテクで対抗…昔ながらのハンドルロック、キャンインベーダーにも有効(11/3)
https://www.yomiuri.co.jp/national/20231102-OYT1T50101/
宇宙機にも現代的なCI/CDを取り入れたい

宇宙機のソフトウェアの高度化に伴い，宇宙機開発においても現代的なソフトウェア開発手法を取り入れることが試みられ始めています．本記事では，現代的なCI/CDを宇宙機のソフトウェア開発に取り入れることについての考察と技術的課題について述べたいと思います．

## 宇宙機におけるシステム・テスト

宇宙機はミッションに失敗すると大きな経済的代償を払うことが多いため，宇宙機には高い信頼性が求められるものです．したがって，宇宙機のソフトウェア開発においては，ソフトウェア・テストの中でも，信頼性を含む非機能要件を確認するようなシステム・テストが重視されると考えられます．

## 宇宙機におけるCI

システム・テストはシステム全体を対象とします．そのため，ソースコードを構成管理ツールで管理している場合，全ての開発者の作業コピーをマージしたソースコードを対象に行う必要があります．

もし，それを頻繁かつ定期的に行うのであれば，すなわち，CI(継続的インテグレーション)を行なっているということです(CIの定義は，頻繁かつ定期的に全ての開発者の作業コピーをマージすることです)．

宇宙機でCIを行う，すなわち頻繁かつ定期的にシステム・テストを行える状態にすることができれば，信頼性を含む非機能要件を確認するのを，ウォーターフォール的にシステム開発の最後の工程でまとめて行うのではなく，アジャイル・ソフトウェア開発的に工程の中で都度確認しながら実施できるように思います．これにより，早い段階で，信頼性を含む非機能要件にかかる問題点を検出することができるのではないかと期待します．

## 宇宙機におけるCD

また，システム・テストを行うのに，シミュレーターや実機を用いることも多いです．

前項で述べたようにCIを取り入れるのであれば，シミュレーターや実機にCD(継続的デプロイメント)を行うことで，システム・テストを効率よく行えるかもしれません．

下記の記事で，Nervesを宇宙機に用いることを提案しました．

https://qiita.com/zacky1972/items/b9bde6aa6724960340ab

Nervesの持つOver the air(OTA)を用いると，ネットワーク越しにセキュアにシステムをアップデートできます．これを用いれば，実機への効率的なデプロイメントを行えます．

さらに，GitHub ActionsのSelf-hosted runnerを用いることで，ローカル・マシンでGitHub Actionsを実行することができます．Self-hosted runnerを起点にして，Nervesシステムをビルドし，OTAで実機へデプロイメントできるのであれば，CDを実現できるでしょう．

## 技術的課題

しかし，システム・テストを頻繁かつ定期的に行うには，コストがかかります．宇宙機の場合，たとえば振動試験や衝撃試験，真空試験，放射線試験などを行うことがあります．そのような試験を行いながらの信頼性を含む非機能要件の確認を実施するには，大きなコストがかかります．それを常時行えるような環境を用意するのは，コスト面で非現実的だと思われます．

では，どうするのか？その答えについても私は考えていますが，その話は機会を改めてしたいと思います．

## 告知

関連する技術記事をCQ出版Interface誌に別冊付録されるFPGAマガジンに連載します！ 直近では2月25日発売号に付録されます．この機会に是非定期購読ください．

20240210追記:

[FPGAマガジン特別版No.2の目次が公開されました！ https://fpga.tokyo/no2-2/](https://fpga.tokyo/no2-2/)

大変光栄なことに，トップを飾っております．

> 第1章　Verilator 5とGitHub Actionsによるテストベンチ自動検証

他にも有用な記事が満載ですので，ぜひこの機会にInterfaceを定期購読ください！
pyenv バグ

なぜかpyenvを実行すると途中で止まり、pythonなども使えなくなった。

~/.bashrcの
```
eval "$(pyenv init -)"
```
をコメントアウトすると
`source ~/.bashrc`が回る。

`pyenv update`でpyenvをupdateしたところ、
```
pyenv: cannot rehash: /path/to/pyenv/.pyenv/shims/.pyenv-shim exists
```
とでてきた。

```
rm -rf /path/to/pyenv/.pyenv/shims/.pyenv-shim
```
してから、~/.bashrcの
```
eval "$(pyenv init -)"
```
をコメントアウトしたらなんか治った気がする。

↑ この対応だとpythonが使えなくなった。
`pyenv init -`も途中で`pyenv rehash`をしていて
そこでスタックしていたようだ。

以下は参考になりそう：
https://github.com/pyenv/pyenv/issues/2256
Elixir，月へ行く

プログラミング言語Elixir(エリクサー)の特長である分散・並行・並列処理とフォールト・トレランスの記述容易性は，宇宙機に好ましい特性です．そこで，北九州市立大学と株式会社ダイモンは，世界最小の月面探査車YAOKIの次期コンピュータシステムのソフトウェア記述言語として，Elixirを採用しました．

世界最小の月面探査車YAOKIについては，下記，株式会社ダイモンのホームページをご覧ください．

https://dymon.co.jp

## 月面でのエッジ・コンピューティングの実現

[「Nerves，月へ行く」](https://qiita.com/zacky1972/items/b9bde6aa6724960340ab)で将来課題として挙げたように，地球と月の間のネットワーク通信は，多大な遅延時間があり，かつ帯域幅が狭いものです．そのため，月面ではエッジ・コンピューティングが必須技術となります．

Elixirの特長である分散・並行・並列処理の記述容易性は，月面でのエッジ・コンピューティングの実現を容易にします．

たとえば，Elixirの機械学習基盤であるNx(エヌエックス)を用いると，機械学習や深層学習をPythonよりもかなり容易に分散並列処理することができます．現在，Nxを基盤として，ニューラル・ネットワークを実現するAxon，ニューラル・ネットワークの流通基盤であるONNXの利用を可能にするAxonOnnx，Stable Diffusion を含む[Hugging Face](https://huggingface.co)の機械学習モデルを利用できるようにするBumblebeeを利用することができます．これだけでも非常に強力なのですが，さらに近い将来LLMを利用できるようにするという表明がされています．もちろん，これらについても，分散並列処理を行うことができます．

さらに，[FPGAを用いた時](https://qiita.com/zacky1972/items/b9bde6aa6724960340ab#さらにfpgaを用いることの利点)には，FPGAベースの機械学習が行えると効果的です．

そこで，私たちが研究開発しているPelemay Backendを適用していきたいと考えています．

https://zacky1972.github.io/blog/2023/05/26/pelemay_backend.html

このような基盤を利用することで，月面で必要なエッジ・コンピューティングを実現できるものと考えています．

## 宇宙線に耐えられるソフトエラー対策の実現

また，バン・アレン帯の外で活動する宇宙機では，宇宙線に耐えられるようにソフトエラー対策を行う必要があります．

ソフトウェアにソフトエラー対策を埋め込むための基本的なソフトウェア・アーキテクチャとして，3つ以上の投機的な分散並列処理を実施し，結果を照合し多数決を取る，さらに多数決が成立しなかった時には再計算するというような方式が考えられます．

これを実現するためには，Elixirの特長である分散・並行・並列処理の記述容易性も有用ですが，それだけではなく，フォールト・トレランスの記述容易性も極めて有効です．多数決が成立しないような異常な結果に陥った時に，プロセスを再起動して再計算を行うというような処理を，Supervisorを用いることで容易に記述できます．

また，定期的にプロセスを再起動することで，ソフトエラーによるプロセスの状態の異常化を予防するということも容易にできます．
ABC327 提出したコードや感想 (言語:Python)

# 記事構成
[1. Atcoder自己紹介](#1-Atcoder自己紹介)
[2. 各問題の感想と提出コード](#2-各問題の感想と提出コード)
[3. 今回の結果想](#3-今回の結果)
[4. 最後に](#4-最後に)

# 1. Atcoder自己紹介
- 茶色🟤コーダー
- 主な使用言語：Python，C++（C++は最近使ってません）
- 一時期休んでたけど最近再開
- 本コンテスト終了時のグラフ↓

![abc327.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564785/3726b847-cd9d-1d03-3f03-47f118f36337.png)

# 2. 各問題の感想と提出コード
## A問題
特にいうこと無し．
A問題にしては中身書きすぎた？

```python:A問題
def c_ab(S):
    N = len(S)
    for i in range(N - 1):
        if (S[i] == 'a' and S[i + 1] == 'b') or (S[i] == 'b' and S[i + 1] == 'a'):
            return "Yes"
    return "No"

a=int(input())
s = input()
print(c_ab(s))
```

## B問題
B問題なら，どうにかなるだろと思い，while文で終了条件を判断
多分C問題以降なら，while文の最後のA^A計算は危険
```python:B問題
def f_aa(b):
    a = 1
    while a ** a <= b:
        if a ** a == b:
            return a
        a += 1
    return -1

b=int(input())
print(f_aa(b))
```

## C問題
問題の条件通りに，実装
思ったより綺麗に書けた
```python:C問題
def is_c(grid):
    for r in grid:
        if len(set(r)) != 9:
            return "No"
    
    for c in range(9):
        if len(set(grid[r][c] for r in range(9))) != 9:
            return "No"
    
    for i in range(0, 9, 3):
        for j in range(0, 9, 3):
            ans_grid = [grid[x][y] for x in range(i, i+3) for y in range(j, j+3)]
            if len(set(ans_grid)) != 9:
                return "No"
    
    return "Yes"

g = [list(map(int, input().split())) for _ in range(9)]

print(is_c(g))
```

## D問題
最近，解いてた過去問でグラフ系の問題があって，二部グラフじゃないか？の考えにつながった
ただそれが無ければ，100%答えに辿り着かなかったので，演習量足りないね
茶diffなのは驚いた（今回も二部グラフかどうか調べる関数はネット参照だし）
```python:D問題
from collections import deque

def is_gragh(graph, n):
    colors = [-1] * n
    for i in range(n):
        if colors[i] == -1:
            queue = deque([i])
            colors[i] = 0
            while queue:
                u = queue.popleft()
                for v in graph[u]:
                    if colors[v] == -1:
                        colors[v] = 1 - colors[u]
                        queue.append(v)
                    elif colors[v] == colors[u]:
                        return False
    return True

def check_pair(N, M, A, B):
    graph = [[] for _ in range(N)]
    for a, b in zip(A, B):
        graph[a-1].append(b-1)
        graph[b-1].append(a-1)
    return is_gragh(graph, N)

n, m = map(int, input().split())
a = list(map(int, input().split()))
b = list(map(int, input().split()))

result = check_pair(n, m, a, b)
print("Yes" if result else "No")
```


# 3. 今回の結果
今回はこんな感じ．まあ上がってるからよし！
割と緑射程圏内

![abc327_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3564785/a7644391-65eb-5089-8eaa-374099f9007b.png)

# 4. 最後に
鉄則本早く終わらせたい
Nerves，月へ行く

Nerves(ナーヴス)のアドバンテージの1つであるOver the air(OTA)，すなわち，オンライン越しにセキュアにアップデートできる機能は，これからの宇宙機に強く求められる特性の1つです．北九州市立大学と株式会社ダイモンは，世界最小の月面探査車YAOKIの次期コンピュータシステムのソフトウェアとして，Nervesを採用しようと，日夜，研究開発を進めています．

世界最小の月面探査車YAOKIについては，下記，株式会社ダイモンのホームページをご覧ください．

https://dymon.co.jp

## 月面探査車YAOKIのビジネスモデルの特徴と，ソフトウェアに求められる要件

YAOKIのビジネスモデルについては，下記ページよりダウンロードできる「Project YAOKI 事業紹介資料」に詳述されています．ご興味のある方はお取り寄せください．

https://dymon.co.jp/yaoki/

ビジネスモデルから読み取れるように，YAOKIは宇宙・地上の両方に展開していきます．月面においては，1台のYAOKIで多様な企業様の多様なニーズに同時に応えるように設計する必要があります．かつ，1台1台が異なるようなYAOKIを100台以上も月面へ送り込むようなことを行います．

必然的に，様々なハードウェア部品に対応する多様なデバイスドライバを備えた上で，多様なファームウェアとアプリケーションソフトウェアを実行してYAOKIを制御し，さらにそのようなYAOKIが多数お互い通信・連携し合って動作することが求められます．

それを実現しようと思うと，少なくともそのようなソフトウェアをC言語プログラミングで1つ1つ手作りで開発していくというのでは，到底スケールしません．もっと効率的にソフトウェア・コンポーネントの組合せで効率よく開発できるような，モダンなフレームワークが求められます．

また，YAOKIの様々なハードウェア技術の進化により，最初は電池が切れたらおしまいだったのが，長寿命化していき，何年以上にもわたって様々なミッションをこなすことができるようになることが想定されます．そうすると，途中でミッションが変わった時に，ソフトウェアをアップデートする機能が欲しくなります．

## OTAを備えたモダンなIoTフレームワークNervesで解決

前述の要望・技術的課題を整理すると次の通りになります．

1. 効率的にソフトウェア・コンポーネントの組合せで効率よく開発したい
2. 途中でミッションが変わった時に，ファームウェアをアップデートしたい

こうした要件に応えることができると我々が期待しているのが，ElixirベースのIoTフレームワークであるNervesです．Nervesにより，次のように技術的課題が解決されます．

1. Elixirでは Mix というビルドツールと Hex というパッケージ・マネージャが標準で備わっており，パッケージ単位でソフトウェア・コンポーネントを扱うことができる
2. Nervesでは，SSH越しにファームウェアをアップデートする機能が標準で備わっている

## さらにFPGAを用いることの利点

とくにミッションを途中で変える場合，ソフトウェアだけでなく，ハードウェアを構成する論理回路もアップデートできるとより効果的です．そこで，FPGAの導入です．

下記イベントにて「FPGAによる宇宙機向け高速画像処理・信号処理・機械学習とアップデート機構の実現」を発表しました．

https://acri.connpass.com/event/298957/

講演スライド(PDF)は下記からダウンロードください．

https://researchmap.jp/zacky1972/misc/44302303

発表のYouTube動画はこちらです．

https://www.youtube.com/live/d1o-pCeE0zw?si=jAehQvvLRCWiPg-A&t=4877

NervesのOTAを用いて，FPGAのロジックをパーシャル・リコンフィギュレーションする技術も開発していきます．

## 将来課題

地球と月の間のネットワーク通信は，多大な遅延時間があり，かつ帯域幅が狭いものです．そのため，Nervesを実用的に月面で活用するためには，ファームウェアをビルドするホスティング・サーバーやパッケージ・マネージャHexを，地球とも適宜同期しつつ月面上にローカル配置するようなエッジ・コンピューティングを行うことが必須になるでしょう．この点については，[「Elixir，月へ行く」](https://qiita.com/zacky1972/items/2f2ff987072a0268652b)で議論したいと思います．

また，このような宇宙機でもモダンなソフトウェア開発手法を取り入れてシステム開発を効率よくしていく必要があること，宇宙機ではとくに信頼性が重要であることを両立する必要があります．この点については，[「宇宙機にも現代的なCI/CDを取り入れたい」](https://qiita.com/zacky1972/items/80fb9aa1b5bf3641f0a5)で議論したいと思います．

## 謝辞

本研究の一部は，北九州産業学術推進機構(FAIS)衛星データにかかる新技術開発事業の支援を受けた．
分布仮説に基づく単語のベクトル化（カウントベースの方法）

# 分布仮説（Distributional Hypothesis）
単語自体に意味はなく、その単語の周囲の単語（コンテキスト）によってその単語の意味が形成される、という考え方。
分布仮説に基づいた文章のベクトル化について自分の理解をアウトプット。

## 単語の周囲にどんな単語があるか記録

以下のような文章を例に使用（Beatlesの名曲"[Hello, Goodbye](https://www.youtube.com/watch?v=rblYSKz_VnI)"の歌詞から引用)
```python
text = 'You say goodbye and I say hello.'
```

文章を単語に分割
```python
text = text.lower()
text = text.replace('.', ' .')
words = text.split(" ")
print(words)
```

```bash
['you', 'say', 'goodbye', 'and', 'i', 'say', 'hello', '.']
```

各単語にユニークなIDを付与。単語：IDの辞書とID：単語の辞書をそれぞれ作成
```python
word_to_id = {}
id_to_word = {}

for word in words:
  if word not in word_to_id:
    new_id = len(word_to_id)
    word_to_id[word] = new_id
    id_to_word[new_id] = word

print(word_to_id)
```

```
{'you': 0, 'say': 1, 'goodbye': 2, 'and': 3, 'i': 4, 'hello': 5, '.': 6}
```
この単語・ID対応表をもとに元の文章をコーパス化する
```python
corpus = [word_to_id[w] for w in words]
print(corpus)
```

```
[0 1 2 3 4 1 5 6]
```


「各単語の両隣にどの単語があるか」を示す行列を作る。IDが`2`の単語`goodbye`の両脇にはID`1`の単語`say`とID`3`の単語`and`があるので、作る行列は`[0, 1, 0, 1, 0, 0, 0]`となる。
これを全単語について作る

```python
def create_co_matrix(corpus, vocab_size, window_size=1):
  corpus_size = len(corpus)
  co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32)

  for idx, word_id in enumerate(corpus):
    for i in range(1, window_size + 1):
      left_idx = idx - i
      right_idx = idx + i

      if left_idx >= 0:
        left_word_id = corpus[left_idx]
        co_matrix[word_id, left_word_id] += 1

      if right_idx < corpus_size:
        right_word_id = corpus[right_idx]
        co_matrix[word_id, right_word_id] += 1

  return co_matrix

C = create_co_matrix(corpus, 7)
print(create_co_matrix(corpus, 7))
```

```
[[0 1 0 0 0 0 0]
 [1 0 1 0 1 1 0]
 [0 1 0 1 0 0 0]
 [0 0 1 0 1 0 0]
 [0 1 0 1 0 0 0]
 [0 1 0 0 0 0 1]
 [0 0 0 0 0 1 0]]
```

分布仮説に基づく単語のベクトル化ができた。これにより「ベクトル成分の類似度が高い単語は意味が似ている」と捉えることができる。しかしこのままでは以下の問題がある
- 登場頻度が高い単語で偽の関連性を示してしまう
- コーパスが増えると行列がデカくなって計算コストがかかる

## 大いに参考
https://github.com/oreilly-japan/deep-learning-from-scratch-2
【Alteryx】複数のCSVファイルやExcelシートを読み込む方法

# はじめに

弊社では昨年からAlteryxを導入しています。複数のCSV・Excelファイルからデータを読み取ることが頻繁にあるので、備忘録として残しておきます。

# CSVファイルの場合

## マクロの作成

まず下図のように1つのCSVファイルからデータを読み込むマクロを作成します。ここで使用しているツールは、コントロールパラメーター、アクション、マクロ出力、データ入力です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/b6a442e6-4bfe-5ba6-c430-446b191ed05b.png)

アクションのアクションタイプにおいて「値を更新」を選択してください。

## ワークフローの作成

次に、作成したマクロを使って複数のCSVファイルを読み込むワークフローを作成します。
あるフォルダにCSVファイルがすべて保存されているとすると、ディレクトリツールを使ってCSVファイルパスのリストを取得してマクロに渡し、読み取られた各ファイルのデータを複数結合ツールを使って行方向に結合します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/6f51e7c4-44f5-d71f-1bc2-c428f2e36f99.png)


# Excelシートの場合

## マクロの作成

複数のエクセルシートを読み込む場合も、CSVファイルの場合と同様に、まず一つのシートからデータを読み込むマクロを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/58b0e5f4-990d-310a-ad2b-3d3a89e46e90.png)

アクションツールのアクションタイプは、CSVファイルの場合と同様に「値を更新」を選択してください。またデータ入力ツールでExcelファイルを選択したら、オプションの「テーブルまたはクエリ」において「シートを選択」を選んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/e49fbc56-fa67-2df0-ce4c-33f96dde29cf.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/b7c61581-3386-bf18-7092-fa5bc0c471e1.png)

## ワークフローの作成

複数のExcelシートからデータを読み込む場合、下図のようにマクロに渡すまでの部分がCSVファイルの場合と異なります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/9ecdf1d1-4713-0494-a5a0-d7c2c9cecc68.png)

まずデータ入力ツールでエクセルファイルを指定するときに、「テーブルまたはクエリ」で「シート名のリストのみをインポート」を選択してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/8257f353-17da-e2b2-aec2-26376c26db26.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/5bdc0622-c9d1-59e4-3d9b-00ba02dc6a10.png)

次にフォーミュラツールに以下の式を入力して、各シートのファイル名を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/311dc1f7-82d3-efb9-4406-9bd2f4b186d3.png)

全てのシートからデータを読みこむ場合は、下図のようにフォーミュラツールから直接マクロへコネクタを接続してしまって問題ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/4d7de1d0-5607-23d3-e6e8-5183ee733655.png)

一方、特定のシートのみ読み込みたい場合は、フィルターやリストを用いて読み込みたいシートのみを選択する必要があります。ここでは、あらかじめ読み込みたいシート名をリストとして入力しておき、結合ツールの内部結合を使うことでそれらのシート名に一致するもののみをマクロに渡す方法を説明します。

最初の図のようにテキスト入力ツールを使って読み込むシート名のリストを作成します。ファイルパスと比較する場合、シート名は文字列でなければならないので、セレクトツールを使ってデータ型を文字列にしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/7d1cae70-fa15-535b-da73-b90a7fbd740a.png)

次に、結合ツールをつかってシート名のリストに含まれるシートのパスのみを選択します。結合ツールのLとRにシートリストとエクセルファイルからのコネクタをそれぞれ接続し、「特定のフィールドで結合」で比較する列名を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/d808984c-7f39-3710-b7ac-576ca6b86349.png)

そして結合ツールのJとマクロをコネクタで接続すると、指定したシートのパスがマクロに渡されてデータが読み込まれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130485/04b4f2b3-e4f3-0b51-8fc2-d87baef1e4f0.png)

## 別の方法

下記のように動的入力ツールを使って読み込む方法もあります。

https://knowledge.alteryx.com/index/s/article/How-To-Import-Multiple-Excel-Sheets-or-a-Specific-Excel-Range-into-a-Workflow-1583461464206
PokeAPIで取得できるポケモンのデータを簡単にまとめてみた

# はじめに
どうもポケモン廃人です。ポケモンSV、DLCの配信日も決まり楽しみですね。

ポケモンに関するデータをいろいろ取得できる`PokeAPI`（ https://pokeapi.co/ ）というものがあることを知ったのですが、公式ドキュメントを眺めたところ、取れるデータが予想以上に豊富でした。
技やアイテムなど、とにかくポケモンのゲームに関する情報は基本的になんでも取得できそうな印象です。

ドキュメントが非常に長い＆英語版ということで、今回はポケモン自体のデータに絞って、どんな情報を取得できるのか簡単にまとめていこうと思います。

APIドキュメント：

https://pokeapi.co/docs/v2

# 実行API
`PokeAPI`には多くの形式のAPIが存在するのですが、今回は
`GET https://pokeapi.co/api/v2/pokemon/{id or name}/`
に限定します。
任意のポケモンのid（基本的には全国図鑑番号ですが後述します）か英語名（小文字表記）を指定して実行すると、そのポケモンに関する詳細データが返ってきます。

# 取得データ
取得される項目は、全部で18項目あります。
以下、API実行時の検索対象のポケモンのことを、「対象ポケモン」と表記します。

## id
対象ポケモンの識別子。
基本的には全国図鑑の番号（フシギダネの場合は1）になってそうでしたが、メガシンカやリージョンフォルムを持つポケモンは別途idが存在しています。

軽く調べた例としては、
`id：10041`がメガギャラドス、`id：10103`がアローラロコン
となっておりました。

`GET https://pokeapi.co/api/v2/pokemon-species/{id or name}/`
で対象ポケモンの全フォルムのIDを取得できるので、複数のフォルムを持つポケモンのデータを取得する際は、先にIDを確認する必要がありそうです。

参考：https://pokeapi.co/docs/v2#pokemon-species

## name
対象ポケモンの英語名。

日本語に置換するためには別途処理が必要そうです。詳細は省きますが、
`GET https://pokeapi.co/api/v2/pokemon-species/{id or name}/`
で各言語でのポケモン名を取得できるので、こちらのAPIの出力と組み合わせれば可能そうです。

参考：https://pokeapi.co/docs/v2#pokemon-species

### 追記
別記事にて、日本語名を取得する具体的な実装を行いました。

https://qiita.com/jinto/items/9d0d9df30dded1e61015

## base_experience
対象ポケモンを倒すことで得られる経験値。

## height
対象ポケモンの身長（高さ）。

## is_default
対象ポケモンがデフォルの姿であるかどうか。

メガシンカやリージョンフォルムのポケモンを検索対象にすると`false`が返ってきます。

## order
（若干あやふやですが、）対象ポケモンのメガシンカやリージョンフォルムも含めた全国図鑑上の表示順序。

例として、ギャラドスは`211`で、メガギャラドスは`212`となります。

## weight
対象ポケモンの重さ。

## abilities
対象ポケモンの特性の一覧。

## forms
ここだけ分からず...もし何を指しているか分かる方がおりましたら、教えていただけると助かります。

## game_indices
対象ポケモンが登場するゲーム（ルビー、ダイヤモンドなど）と、そのゲーム内でのインデックス（=恐らく図鑑の表示順序）の一覧。

## held_items
対象ポケモンと野生で遭遇した時に持っている可能性のあるアイテムの一覧。

## location_area_encounters
対象ポケモンに遭遇できる場所（のリンク）。

## moves
対象ポケモンが覚えることができる技の一覧。

## past_types
対象ポケモンが過去世代で持っていたタイプの一覧。

例えば、ピクシーを指定するとnormalの出力が存在します（現在はフェアリータイプ）。

## sprites
対象ポケモンの画像へのアクセスURL。

## species
対象ポケモンの種族（図鑑データ?）に関する情報（のリンク）。

## stats
対象ポケモンの種族値。

## types
対象ポケモンのタイプ。

# 最後に
内容説明があやふやな項目もあり恐縮ですが、`PokeAPI`の取得データについてざっとまとめてみました。
訂正依頼等ございましたら、遠慮なくコメントください。

各ポケモンについて詳細なデータを取得できるので、このAPIを使っていろいろ遊べそうですね。今度何か作ってみようかと思います。


以上
0イイネの主に学ぶ？　MySQL

　　　　[MySQL, データ破損]　
- やりたいこと

　　　　MySQLのデータが破損したので修復or作り直したい

- やったこと

　　　　ググる、チャットGPTに聞く

- error内容

　　　　MySQLにデータが登録できない

- 原因

　　　  1. ファイルを作り直す際にMySQLが予期しないデータを何度も送信した結果として壊れた

　          2.修復不可能だと考えて消去を決める

- 解決策

               1 → MySQLをまずは直したい

                  →REPAIR TABLE companies;を試すも失敗

The storage engine for the table doesn't support repair

　　　　　2→drop tableで削除で成功

（全部削除のやり方は、よく分からなかった）
elan toolchain updateがうまくできなかったときのメモ

elanのnightlyをアップデートしようとしたらうまくできなかった。

```
PS > elan toolchain update leanprover/lean4:nightly
info: syncing channel updates for 'nightly'
info: latest update on nightly, lean version nightly-2023-11-03
info: downloading component 'lean'
180.7 MiB / 180.7 MiB (100 %)  23.1 MiB/s ETA:   0 s
info: installing component 'lean'
error: could not remove 'toolchain directory' directory: 'C:\Users\user\.elan\toolchains\leanprover--lean4---nightly'
info: caused by: アクセスが拒否されました。 (os error 5)
```

どうもVisual Studio CodeでLanguage Serverが動いていたからだめだった。
Visual Studio Codeを閉じたら問題なく`elan toolchain update`が動いた。

```
PS > elan toolchain update leanprover/lean4:nightly
info: syncing channel updates for 'nightly'
info: latest update on nightly, lean version nightly-2023-11-03
info: downloading component 'lean'
180.7 MiB / 180.7 MiB (100 %)  23.2 MiB/s ETA:   0 s
info: installing component 'lean'

  leanprover/lean4:nightly updated - Lean (version 4.4.0-nightly-2023-11-03, commit e217ad3929ed, Release)
```
OpenLDAPクライアント  id: ユーザー名: no such userと出力される現象

# はじめに
OpenLDAPクライアント  id: ユーザー名: no such userと出力されたため、記事にまとめました。

OpenLDAPサーバー構築
https://qiita.com/gama1234/items/2dfd363a79d18f1e0ba2

# 事象

OpenLDAPクライアントでidコマンドでLDAPユーザーの情報をみようとしましたが、
no such userになりました。
```
id test(LDAPユーザー)
id: test: no such user
```

# 解決策
**LDAP クライアントを設定してない場合も上記の事象が起きます。まずは本事象が起きたらLDAPクライアントの設定をしてください**
LDAPユーザーでLDAPクライアントサーバーにログイン後、本事象が解消した

私の環境ではLDAPユーザーでLDAPクライアントにログインすると
LDAPユーザーのホームディレクトリが自動作成されました。

```
ssh　(LDAPユーザー)@LDAPクライアントのIPアドレス

例　
[root@localhost etc]# ssh test@192.168.0.27
yuta@192.168.0.27's password:
Last login: Sat Nov  4 19:56:31 2023 from 192.168.0.24
[yuta@localhost ~]$

```
idコマンドを実行したらID情報が表示されるようになりました。
```
[root@localhost ~]# id test
uid=1000(test) gid=1000(system) groups=1000(system)
[root@localhost ~]#
```

# まとめ
LDAPクライアントに一度もLDAPユーザーでログインしていない場合はまだユーザー情報が
作成されていない可能性があります。
**id: ユーザー名: no such userになる場合は、LDAPクライアントにLDAPユーザーでログインを試してみてください。**
[Ubuntu] vsftpdの設定メモ

# はじめに

ローカルネットワーク上のサブマシンとのデータのやり取りが面倒でしたのでFTPで行う事にしました。
その時の設定のメモです。

基本的に以下の記述をそのまま実行しただけです。

https://ubunlog.com/en/vsftpd-instalar-un-servidor-ftp-ubuntu/

# 環境

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

# vsftpd

FTPサーバとしてvsftpdを使うことにした。

## インストール

`sudo apt install vsftpd`

```:vsfptd -v
vsftpd: version 3.0.5
```

## 証明書の準備

以下のコマンドで作成。

`sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/vsftpd.pem -out /etc/ssl/private/vsftpd.pem`

## 設定

始めに設定ファイルのバックアップを行う。

`sudo cp /etc/vsftpd.conf /etc/vsftpd.conf_default`

そして、`/etc/vsftpd.conf`を編集し、以下を追加する。

```:vsftpd.conf
anonymous_enable=NO
write_enable=YES
local_enable=YES
chroot_local_user=YES

user_sub_token=$USER
local_root=/home/$USER/ftp

rsa_cert_file=/etc/ssl/private/vsftpd.pem
rsa_private_key_file=/etc/ssl/private/vsftpd.pem
ssl_enable=YES

allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES

ssl_sslv2=NO
ssl_sslv3=NO
ssl_tlsv1=YES

require_ssl_reuse=NO
ssl_ciphers=HIGH

xferlog_std_format=NO
```

## コマンド

### ログの確認

ログは、デフォルトで以下に出力される。
`/var/log/vsftpd.log`

`sudo cat /var/log/vsftpd.log`

### サービスの起動
`sudo systemctl start vsftpd`

### サービスの再起動
`sudo systemctl restart vsftpd`

### サービスの終了
`sudo systemctl stop vsftpd`

### サービスの状態確認
`sudo systemctl status vsftpd`

### サービスの自動起動登録
`sudo systemctl enable vsftpd`

### サービスの自動起動解除
`sudo systemctl disable vsftpd`

### サービスの自動起動確認
`sudo systemctl is-enabled vsftpd`

# FTP用のフォルダの準備

`<username>`は、実際のユーザ名に置き換える。

```
sudo mkdir ftp
sudo chown nobody:nogroup ~/ftp
sudo chmod a-w ~/ftp

sudo mkdir ~/ftp/files
sudo chown <username>:<username> ~/ftp/files
```

---

# ufw

念のためにファイヤーウォールの設定も行っておく。

## 設定

```
sudo ufw allow 20/tcp
sudo ufw allow 21/tcp
sudo ufw allow 990/tcp
sudo ufw allow 40000:50000/tcp
```

## コマンド

### ufwの状態確認
`sudo ufw status`

### ufwの有効化
`sudo ufw enable`

### 設定の再読み込み
`sudo ufw reload`

---

# クライアント

クライアントとしてFileZillaをインストール。

https://filezilla-project.org/

1. ホストにサブマシンのローカルIPアドレスを入力
2. サブマシンのユーザ名とパスワードを入力

 証明書を信頼するかどうかのメッセージが表示されるので、内容を確認して信頼する。
これで問題なく接続できた。
Rustでコールバックにユーザ定義引数を渡す感じのなんか

# はじめに
コールバックにユーザ定義引数を渡す方法を考えて見るだけの話。
多分調べれば出てくる気はするけど、なんとなく考えてみた。

# 試そうとしたこと
Traitでやる：処理の抽象化ではなくデータ型の抽象化がしたいので却下
enum：データ型の抽象化はできるけど、用途的に絶対に違うので却下
std::any::Any: 惜しい気もする。流石にこれを使うまでもなさそう。
Generics：これだ

# やれるとどうなる？
ライブラリに毎回手を入れずとも、アプリケーションに合わせた引数を定義できる。

# 実際にやってみる

### ディレクトリを走査するライブラリ側のコード
非同期処理（並列・並行処理）を行うので、callbackはFutureにしている。
Pが任意のパラメータ型で、callbackはPathBufとP型を受け取り実行される。

```Rust:dirwalker.rs
    pub async fn dir_walk_async<F, Fut,P>(
        &mut self,
        dir: &DirectoryInfo,
        callback: F,
        user_param:P
    ) -> io::Result<()>
    where
        F: Fn(PathBuf,P) -> Fut,
        Fut: Future<Output = ()> + std::marker::Send + 'static,
        P:Clone
    {
        let dir = Path::new(&dir.path);
        let mut join_set = JoinSet::new();
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if entry.path().is_dir() {
                self.dir_list.push(DirectoryInfo {
                    path: entry.path().into_os_string().into_string().unwrap(),
                });
            } else {
                join_set.spawn(callback(entry.path(),user_param.clone()));
            }
        }
        while join_set.len() > 0 {
            join_set.join_next().await.unwrap().unwrap();
        }
        Ok(())
    }
```

### 呼び出し側のコード
```Rust:main.rs
// Callbackに対するパラメータの定義をしている。必要に応じて変更する。
#[derive(Clone, Debug)]
struct CallbackParameter {
    out: StandardLog, // 通常ログのWriter
    err_out: ErrorLog, // エラーログのWriter
}
// コールバックでログを出したいので、ログ用のWriterを定義する
// もちろん別スレッドにぶん投げて、並列動作する可能性があるのでArc+Mutexする。
#[derive(Clone, Debug)]
struct StandardLog {
    out: Arc<Mutex<BufWriter<File>>>,
}
#[derive(Clone, Debug)]
struct ErrorLog {
    err_out: Arc<Mutex<BufWriter<File>>>,
}
impl Write for ErrorLog {
// Writeを実装する
}

impl Write for StandardLog {
// Writeを実装する
}

#[tokio::main]
async fn main(){
    let mut dw = DirectoryWalker::new("./").unwrap();
    // dw.pop()：あるディレクトリを1階層ずつ走査して、そこに有るディレクトリをpopする操作=次に走査するべきディレクトリが返るような関数。
    while let Some(next) = dw.pop() {
        let _ = dw.dir_walk_async(&next, callback, param.clone()).await;
    }
}
async fn callback (path: PathBuf, mut param: CallbackParameter){
// ここで色々ログ出したりの処理する
    write!(param.out,"処理中のファイル{}に関するお知らせ用ログ。",path.to_str().unwrap());
    write!(param.err_out,"処理中のファイル{}でエラーが起きたとき用ログ",path.to_str().unwrap());
}
```

# まとめ
Genericsマジ便利。
あと、Mutex使っててもWriteでラップすれば呼び出し側では意識すること無くフラットに使えるので超便利な感じがした。
【Rails】Omniauth使用時のActionController::InvalidAuthenticityToken対処法【DeviseTokenAuth】

## 対処法

`config/application.rb`に以下の設定を追加し、CSRFトークンをフロントエンドから送信すれば解消します。

```config/application.rb

    config.action_controller.forgery_protection_origin_check = false

```
ChatGPTにチャレンジ

## 最初に
　私は現在、100人程度の従業員が在籍しているスーパーで店舗の運営と人事管理を行っています。
しかしながら、営業中は多くのお客さんや取引先の人、従業員が多く動くのに対し、業務に使用できるパソコンの台数は限られており、パソコン作業を待つのは勤務時間においてストレスとなりがちです。また、翌日や1週間先の予定のことを考えると手元で従業員のシフトを把握や業務に関わるツールを作れたら良いなと考えました。
## CHATGptに聞きまくる
いざ、何かを聞こうとしてもすぐには出てこないので全く関係のないことを色々と聞いてCHATGptで遊んでみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/cdcca656-ab61-c594-2473-1d3b69a7638c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/f7220ca2-1ac1-f21e-52ff-765af64fd8a5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/2229e8ed-0d53-e70c-e391-1bba3ebdffe8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/cbfa633c-b35a-032a-2121-78520855973b.png)

## 続いてChatGPT APIをLINEbotに組み込んでみる作業にチャレンジ
久しぶりにコレ、オウム返し設定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/a68b45b8-affb-a61e-5da7-d069947c4d79.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/db5e5645-23c2-9197-406b-ad061ba3427d.png)


しかし、ここから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/34f601f1-ad0d-5012-db45-03453d11a209.png)
何度やってもつまづいてしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/645d56b5-06e3-3e31-0d69-c4dba9e76215.png)
上記の429のエラーから検索してみました。

https://qiita.com/Keichan_15/items/b1aac09f77c6f8580113

心当たりはないのですが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/e25991ac-8063-7b35-c155-040bd5066994.png)
の画面が多く出てきます。
残念ながら、時間切れの為、断念

## 読んでみた資料
https://www.youtube.com/watch?v=ALnTOptGIoc

https://qiita.com/fumitate/items/7a55b15bd7dc47cbc18f

https://qiita.com/yuko6891/items/f5916147031e841f52e2


## 空いた時間は
と言いたい所でしたが、復習しても色々みても細かい作業のエラーが見つけられず、そんな時間は作れませんでした😰残念です。
学習不足の理解不足で何回も同じような失敗を繰りかえしてある程度の所で止まってしまっています。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/ef535453-8222-1118-9023-e19ebff4d450.png)

それにしても
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/53670fdf-9aeb-4359-0a61-8f21e811e81d.png)
本当にこんなに色々と教えてくれて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3552218/bd696883-1a97-159f-1765-6cd0d4d1dae0.png)
すごい😲
 
## 最後に
　Chat GPTが公開されてすでに1年が経過しようとしており、私は決して早い段階で取り組んだ訳ではなかったものの、今この段階で試してみることができてとても良かったです。特に私は書類を作るのがとても遅いので原案が示されているのは良くも悪くも時短につながります。それにより作った時間は商品出しなど、お店の営業に関わる身体を動かす仕事に充てる事が出来るので周りにも是非進めたいです。
Chat Completions APIで会話履歴を踏まえストリーミングで応答を得るAPIリクエスト

## ライブラリインストール
```
pip install openai
```

## ソースコード

```
import openai

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello! I'm John."},
        {"role": "assistant", "content": "Hello John! How can I assist you today?"},
        {"role": "user", "content": "Do you know my name?"},
    ],
    stream=True
)

#print(response)
for chunk in response:
    choice = chunk["choices"][0]
    if choice["finish_reason"] is None:
        print(choice["delta"]["content"])
```

## レスポンス（少しずつ表示されていく）
```
Yes
,
 you
 mentioned
 that
 your
 name
 is
 John
.
 How
 can
 I
 assist
 you
,
 John
?
```

## 解説
- messagesのところに会話の履歴を繋げていく
    - roleの意味
        - system
            - LLMの動作についての指示
        - user
            - ユーザー
        - assistant
            - AI（APIのレスポンス）
- streamパラメータをTrueとするとレスポンスがストリーミングレスポンスになる（少しずつ表示されていく）
    - 受け取り方がfor文での処理になる
    - ストリーミングレスポンスでなければ、```print(response)```で表示可能
久しぶりにgithubにssh接続しようとしたら「IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!」とエラーが出た

久しぶりにgithubで自分のレポジトリをcloneしようとしたところ、


```bash
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is

...
Host key for github.com has changed and you have requested strict checking.
```

的なメッセージが出て、cloneできなかった.

## 原因

githubで何らかの変更があった模様. 
2023/03/23の記事で、上記エラーを確認したのが2023/11/04なので、この変更をまたぐと
エラーが起きる可能性あり

[github: We updated our RSA SSH host key](https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/)

## 対処方法

sssにconfigで`github.com`を登録している場合、一度消してあげればといよい。

下記を実行
```bash
ssh-keygen -R github.com
```

その後、`ssh git@github.com`を試せば、yes/noを聞かれた後、接続成功のメッセージが出るはず.

`You've successfully authenticated, but GitHub does not provide shell access.`

的なメッセージが出れば良い
PythonでYoutubeの動画をダウンロード

# 備忘録

###### pythonでyoutubeの動画をダウンロードするやつ

```python:main.py
import os
from yt_dlp import YoutubeDL

abs_path = os.path.dirname(__file__)

#オプションを指定（最高画質の動画と最高音質の音声を取り出し結合するためのオプション）
#実行ファイルと同じディレクトリにdataフォルダを作成してください
option = {
        'outtmpl' : str(abs_path) + '/data\original/%(title)s.%(ext)s',
        'format' : 'bestaudio/best'
    }

#インスタンスの生成
ydl = YoutubeDL(option)

#ダウンロードの実行
result = ydl.download(['https://www.youtube.com/watch?v=M2cckDmNLMI&list=PLItoQPoI2CslCLn2RKLEwe1IfgdjF5idU&index=2',
                        'https://www.youtube.com/watch?v=cCm5-S5GV2o',])

```
DRP

# 学習済みモデルを作る
ウェブ・ブラウザでTeachable Machine を使ってで学習済みモデルを作る
'Tensorflow'　'Saved model' として学習済みモデルをエキスポートする
ダウンロードされたファイルをLinux(WSL)側に移して解凍する

# ONNXモデルに変換する
WSLのUbuntu上で作業

- tf2onnx をインストール
- tensorflow をインストール
- tensorRT をインストール

```
pip install onnx==1.13.0
pip install tensorflow==2.9.0
pip install onnxruntime
pip install -U tf2onnx==1.13.0
```
<small><i>※バージョン重要</i></small>
<i>pythonバージョンは3.8.10でやりました</i>

解凍すると，model.savedmodelというディレクトリがある．このディレクトリを `--saved-model` 引数に与え，次のコマンドを実行する．
出力ファイル名は `--output` 引数で指定する．

```
python -m tf2onnx.convert --saved-model model.savedmodel --output sushi.onnx --opset 12
```

# 実行形式へ変換する
ルネサスのフレームワークを使ってONNXファイルを変換する．

## 環境構築
予め，DRP-AI Translator V1.83 Installer (ZIP)をダウンロードしておく．
DRP-AI Translator V1.83 User’s Manual（p.9）の3.2.1.Installation Procedureに従い，インストールをする．

```
$ sudo apt-get update
$ sudo apt-get install -y libgl1-mesa-dev wget libopencv-dev
$ sudo apt-get install -y python3-pip
$ pip3 install -U pip
$ chmod +x DRP-AI_Translator-v1.83-Linux-x86_64-Install
$ ./DRP-AI_Translator-v1.83-Linux-x86_64-Install
```


# ダウンロード
- DRP-AI Translator V1.83 Installer (ZIP)
- DRP-AI Translator V1.83 User‘s Manual (PDF) 使い方

(1)Teachable Machine
https://teachablemachine.withgoogle.com/
(2)TensorFlow Hubから取得した機械学習モデルをONNX形式に変換する
https://zenn.dev/yuyakato/articles/226faa817ed01d
ABC327をPythonで（A~E）

HHKBプログラミングコンテスト2023(AtCoder Beginner Contest 327)

https://atcoder.jp/contests/abc327

# A問題
ab か ba のどちらかが入っているか確認
```python:A
input()
s = input()
print("Yes" if "ab" in s or "ba" in s else "No")
```
# B問題
答えとなるAの数は少ないので全探索
```python:B
b = int(input())
a = 1
while a ** a <= b:
    if a ** a == b:
        print(a)
        exit()
    else:
        a += 1

print(-1)
```
# C問題
全部がんばって調べる
```python:C
a = [list(map(int, input().split())) for _ in range(9)]

if any(len(set(a[i])) < 9 for i in range(9)):
    print("No")
    exit()

x = [set() for _ in range(9)]
for i in range(9):
    for j in range(9):
        x[j].add(a[i][j])

if any(len(x_i) < 9 for x_i in x):
    print("No")
    exit()

y = [set() for _ in range(9)]
for i in range(9):
    for j in range(9):
        y[i // 3 + j // 3 * 3].add(a[i][j])

if all(len(y_i) == 9 for y_i in y):
    print("Yes")
else:
    print("No")
```
# D問題
$s_i$と$t_i$を辺でつないだグラフが2部グラフか判定すればよい
```python:D
from collections import deque

n, m = map(int, input().split())
s = list(map(lambda x:int(x) - 1, input().split()))
t = list(map(lambda x:int(x) - 1, input().split()))

edge = [[] for _ in range(n)]
for s_i, t_i in zip(s, t):
    edge[s_i].append(t_i)
    edge[t_i].append(s_i)

def bfs(start):
    global dist, check
    q = deque()
    q.append(start)
    check[start] = True
    dist[start] = 0
    while q:
        now = q.popleft()
        for to in edge[now]:
            if check[to]: continue
            check[to] = True
            dist[to] = (dist[now] + 1) % 2
            q.append(to)

check = [False] * n
INF = float("inf")
dist = [INF] * n
for i in range(n):
    if not check[i]:
        bfs(i)

for s_i, t_i in zip(s, t):
    if dist[s_i] == dist[t_i]:
        print("No")
        exit()

print("Yes")
```
# E問題
dpを使い、レート計算式の前の項の分子の最大値を使った個数ごとに求め、あとで正しいレートを計算する
```python:E
from math import sqrt

n = int(input())
p = list(map(int, input().split()))

INF = float("inf")
dp = [0] + [-INF] * n
for i, p_i in enumerate(p):
    new_dp = [0] * (n + 1)
    for j in range(1, i + 2):
        new_dp[j] = max(dp[j], dp[j - 1] * 0.9 + p_i)
    dp = new_dp

ans = -INF
x = 0
for i, d_i in enumerate(dp[1:], 1):
    x += pow(0.9, i - 1)
    ans = max(ans, d_i / x - 1200 / sqrt(i))

print(ans)
```
単純でないイニシャライザにはファクトリを

[前回](https://qiita.com/Se1getsu/items/4654b38886b468ea3756)に引き続き、Modelの設計についての勉強として「[ドメイン駆動設計入門](https://www.shoeisha.co.jp/book/detail/9784798150727)」を読み進めています。サンプルコードはC#で書かれていますが、iOSを勉強している人にもオススメの一冊です。

今回は**9章**の**ファクトリ**についての内容をベースに、Swiftのコードを使って紹介します。

# イニシャライザは単純に
**イニシャライザ**といえば、インスタンスの初期化の役目を担い、プロパティに初期値を代入する関数です。しかし、その**初期化の処理が単純でなくなる場合**には、ファクトリを用いて生成処理をカプセル化しましょう。
とはいえ単に「単純でないイニシャライザ」と言っても、基準が分かりづらいですよね。
「ドメイン駆動設計入門」では、その指標を以下のように示しています。
> イニシャライザ内で**他のオブジェクトを生成**するかどうか

それでは、例を見てみましょう。

# よくない例
例えば、ToDoアプリのドメインモデルとして、以下のようなModelがあるとします。
```swift
struct ToDo {
    private let id: ToDoID
    private(set) var title: ToDoTitle
    
    init(id: ToDoID, title: ToDoTitle) {
        self.id = id
        self.title = title
    }
    
    mutating func changeTitle(_ title: ToDoTitle) {
        self.title = title
    }
}
```
ここで、ToDoを新規作成する際に、IDを渡さなくても、Model側で新しいIDを生成する仕組みを作りたいと思いました。
真っ先に頭に浮かぶのは、引数にIDが不要なイニシャライザを、別で定義することでしょう。
```diff_swift
struct ToDo {
    private let id: ToDoID
    private(set) var title: ToDoTitle
   
+   init(title: ToDoTitle) {
+       self.id = ToDoID(UUID().uuidString)
+       self.title = title
+   }
+  
    init(id: ToDoID, title: ToDoTitle) {
        self.id = id
        self.title = title
    }
    
    mutating func changeTitle(_ title: ToDoTitle) {
        self.title = title
    }
}
```
しかし、このイニシャライザは、ToDoIDの生成という**単純ではないロジック**を含んでしまっています。

# ファクトリを用いた例
新たなイニシャライザを作るのではなく、ファクトリでIDを生成するようにします。
```swift
struct ToDoFactory {
    func create(title: ToDoTitle) -> ToDo {
        let id = ToDoID(UUID().uuidString)
        return ToDo(id: id, title: title)
    }
}
```

ToDoの新規作成は、ファクトリを使って行います。
```swift
let toDoFactory = ToDoFactory()
let toDo = toDoFactory.create(title: ToDoTitle("買い物に行く"))
```

これによって、イニシャライザに余計なロジックを持たさずに済みます。
この例では簡単のためにUUIDでIDを生成していますが、リポジトリを呼び出してIDの生成を行うといった処理も考えられます。

# メソッドをファクトリの代わりにする例
そうは言っても、わざわざFactoryを作るほど大したことはしてないんだよな... ということもあるでしょう。その場合は、このようにstaticな関数を定義して同等のことを行うのも良さそうです。
```diff_swift
struct ToDo {
    private let id: ToDoID
    private(set) var title: ToDoTitle
    
+   static func create(title: ToDoTitle) -> ToDo {
+       let id = ToDoID(UUID().uuidString)
+       return ToDo(id: id, title: title)
+   }
+   
    init(id: ToDoID, title: ToDoTitle) {
        self.id = id
        self.title = title
    }
    
    mutating func changeTitle(_ title: ToDoTitle) {
        self.title = title
    }
}
```
```swift
let todo = ToDo.create(title: ToDoTitle("買い物に行く"))
```

# まとめ
ファクトリは、インスタンス生成の処理の複雑さを代わりに引き受けてくれます。
イニシャライザを記述する時には、複雑な処理を書かないように注意し、必要であればファクトリを定義しましょう。

# 参考
https://www.shoeisha.co.jp/book/detail/9784798150727
小売業会社員の奮闘録　LINEBotによる熨斗紙の提案

みなさまこんにちは、こんばんは、はたまたおはようございます。
ぐーじんです。

今回はLINEBotによる熨斗紙の提案を作成してみました。

前回の記事はこちら

https://qiita.com/Gujin0109/items/de113832b1e930550433

さて、今回この熨斗紙のLINEBotを作るきっかけは、
熨斗紙の表書きはたくさんあります。
その中で今後を見据えて教育する立場にあることから、
そこで何か作れないかなと思った次第です。
熨斗紙の中にも蝶結び、結びきり、
紅白、黒白、黄白など様々です。
その中でお客さまからの情報などをchatGPTに繋いだLINEBotに情報を投げて、
最適な熨斗紙の種類、表書きなどがすぐにわかればいいなと思い今回の作成に至りました。

### 使ったもの ###
・Make
・LINEBot
・ChatGPT
中身は至って簡単です。
![all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/29b9c785-cfff-acaf-1ccc-888aadcc0b2f.png)
中身を見ていきましょう。
### まずは左のLINEから ###
![hidari1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/caae34f5-90c5-7a36-2411-18319353d2d5.png)
オウム返しを作る内容と同じなので、webhookの名前はオウム返しとしてあります。

### 続いてchatGPTの中身です。 ###
![gpt1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/6c078429-a40a-ac05-53db-cbd3aa9716de.png)
今回はChatGPTの中身が3.5になりますので、選択肢から
gpt-3.5-turboを選択しています。
Messagesの中身です。
![gpt2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/f6a4a5ab-5145-5153-0949-e2277f94b93d.png)
こちらはLINEのEventsの中のMessage.Textを選択しています。

### そして返答を返してくれる右のLINEの中身です。 ###
![migi1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/610534a9-968a-cad0-a60f-fe5d8a03b1b7.png)
まずはEventsのReplyTokenを選択し、
次にMessagesの中身です。
![migi2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/cced7a72-61dd-97f0-a52c-e1cc65de8f12.png)
OpenAIの中からChoicesのMessage.Contentを選択します。

これでLINEBotとChatGPTが繋がりました。
あとは、聞きたいことを打ち込んで聞くだけ！！
なのですが…。

### 繋げる前にいくつかChatGPTに質問をしてみました。 ###
![gptnakami1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/507baf9e-9cb0-11b1-ccd7-5bd5c812f5c1.png)
はい。これ間違っています。
米寿の祝いは60歳ではなく88歳のお祝いのことです。

他にも質問をしています。
![gptnakami2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3539432/ced03014-1670-0011-654a-24899e0b40c0.png)
こちらも間違いです。
黄白の熨斗紙は祝い事では用いません。
そういった間違いを訂正、修正していって、できたchatGPTのLINEBotがこちらです。

https://twitter.com/Gujin_75qq5q/status/1720798566241927400


### 最後に・・・ ###
ただ、一番苦労したのが、ChatGPTに熨斗紙の知識を入れ込んでいく作業
これがまた思っていたことと違う内容であったり、
間違った知識が入っていて苦労しました。

そして・・・実はまだ未完成です。
店舗で使うには余分な情報や、欲しい情報が全く吸いあがってこない！！

ChatGPTを使えば簡単にできると思っていましたが、
中身はやはり育てていかないと十分に使えるものにはなりえませんでした…。

ChatGPTは使い方次第ではとても簡単に扱えるものであることは理解できました。
ただ、使い方次第ではとても難しく、ChatGPTの中身を精査しなければならないものであることがわかりました。

最後まで読んでいただきありがとうございました。
[django-environ] 環境変数を読み込めない

## Issue

django-environを使って、環境変数を読み込もうとしたら以下のエラーが出た。

`django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable`

pathの指定が上手くいっていないのかと思って、色々試してみたが解決しない。

## Solution

`.env`ファイルで環境変数を定義するときには、`=`の両隣は詰める。

``` .env
SECRET_KEY = 'some-secret-key-here' 
```

これだとだめで、

``` .env
SECRET_KEY=some-secret-key-here 
```

これだとOK。
プロジェクトを作ったときに同時に生成されるデフォルトの行をそのままコピペしたせいだった。

.envファイルを書くにあたっては、当たり前の慣習なんだろうか?
IPA基本情報技術者試験で混同しがちな公開鍵暗号方式によるメッセージの暗号化及び複合とディジタル署名の検証による送信者の正当性の確認

## メッセージの暗号化
受信者の目的：複合
送信者が**受信者の公開鍵**で暗号化し、
受信者が**受信者の秘密鍵**で複合する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501146/0211ddb8-f7af-7eaf-520d-69da7eab2d92.png)

## ディジタル署名の検証
受信者の目的：送信者の正当性の確認
送信者が**送信者の秘密鍵**でディジタル署名を生成し、
受信者が**送信者の公開鍵**で署名を複合し（ハッシュ値の比較により）送信者の正当性を確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501146/c8c3374c-0b3e-01e8-c94f-ccd4d8df8606.png)

### 練習問題
https://www.fe-siken.com/index_te.html#11_1
GASからSlackメンションする時のまとめ

## 概要

SlackAPIで誰かに向けてメンションする時, @ユーザー名のような文字列を足せばいいのではとなるのですが、そうはいかないのが大変なところ.
少し表記があるので, そちらを紹介します.

## それぞれの表記

GAS等で用いる文字列について, ここではメンションテキストと呼ぶことにします.

| slackでしたいメンション | メンションテキスト |
| ---- | ---- |
| @channel | `<!channel>` |
| @here | `<!here>` |
| @メンバー | `<@メンバーID>` |
| @グループ | `<!subteam^グループID>` |

メンバーIDやグループIDってどこで取得するね〜〜んという内容について, 以下で解説します.

## メンバーIDやグループIDの取得方法

既存の記事に正攻法は載っているので, 技術的に理解することに問題ない方は是非そちらを見てください.

ちなみに, [公式リファレンス](https://api.slack.com/reference/surfaces/formatting#linking-channels)に載っている方法はこちらです.

> Your app can get this user ID from an interaction request payload, from the Event API payload sent when one of the event types occurs, or by looking them up via the users.list Web API using another unique piece of information you have about them (such as their email address). ...

ただ今回, もっと素朴な方法でメンバーIDやグループIDを得ることができました.
**ブラウザからslackにアクセスして, URLの末尾を見る**です.

まず, ブラウザからslackにログインします.

そして, 以下のようにIDを取得したいメンバーorグループのプロフィールを表示させてください.

![Group 22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2236620/49f0ce40-aebd-f969-7ec9-d74b07b01436.png)

この時のURLが以下のようになっていると思います.
`https://app.slack.com/.../(11文字の英数字の文字列)`

これの末尾についている11文字の英数字の文字列, これがメンバーIDやグループIDなんです！

なので, メンバーの場合はこれの先頭に@をつけて, `<@XXXXXXXXXXX>`, グループの場合は色々とつけて`<!subteam^XXXXXXXXXXX>`という文字列にすることでメンションテキストを作ることができます.

## まとめ

意外と記事が少なかったと思うので, これを見てどなたかのお役に立てれば幸いです.
【備忘録 #3】FastAPIの基本

## ■概要
https://www.youtube.com/watch?v=vVNCVgYb6Xw

上記動画を学習したので、
備忘録として残します。

## ■Pythonの型ヒント
下記のように、型情報を記述することができる。
```python
num: int = 3
def add(a: int, b: int) -> int:
    return a+b
```

リスト、辞書の場合
```python
from typing import List, Dict

sample_list: List[str] = ["helllo world", "hogehoge"]
sample_dic: Dict[str, int] = {"hoge":4}
```


## ■FastAPI起動
「/」にアクセスした際にindex関数を実行する。
```main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/hello{show_str}")
async def hello(show_str: str):
    return {"message": show_str}
```

上記ファイル作成後、以下コマンド実行でサーバー起動
```shell
uvicorn main:app --reload
```
起動すると、`http://`+ IPアドレス +`:8000` でサーバー起動します。


必須ではないパラメータの場合、
ファイル先頭に`from typing import Optional`と記述し、
変数を`country_name: Optional[str] = None`とすると
そのパラメータは必須ではなくなる。
```
from typing import Optional
from fastapi import FastAPI

app = FastAPI()

@app.get("/countries/")
async def country(country_name: Optional[str] = None, country_no: Optional[int] = None):
    return {
        "country_name": country_name,
        "country_no": country_no
    }
```


### ドキュメントの自動生成
起動したサーバーのURL + /docs
もしくはURL + /redocにアクセスすると
ドキュメントが自動生成されています。
凄すぎる！

◯/docs
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512411/b5efe2b3-2398-c68c-4d73-0e3398ba87fa.png)

◯/redoc
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512411/21e01412-a488-4ce3-31be-e484c3c8a4ca.png)