OmniAICreator/Qiita-1.07M huggingface dataset:
定期的なZoomミーティングを自動実行させてみた

## 環境
MacOS Ventura バージョン13.3
## 対象
MacOSでコマンドを定期実行させたい人！
## Zoom meetingを起動するシェルの書き方
### シェルスクリプト(`night_meeting.sh`)の中身
```shell
/usr/bin/open "zoommtg://zoom.us/join?confno=会議ID&pwd=パスワード"
```
コマンドは絶対パスで指定してください。定期実行するのに`launchd`というmacOSデフォルトのアプリケーションを用いるのですが、`launchd`は絶対パスで指定しないと実行できないからです。
### シェルスクリプトに実行権限を付与
```bash
chmod +x /path/to/night_meeting.sh
```
### ミーティングが起動するかテストする
```bash
/path/to/night_meeting.sh
```
## 定期的に自動起動する方法
`~/Library/LaunchAgents`ディレクトリに.plistファイル(`zoomscheduler.night_meeing.plist`)を書く。名前はテキトーで良いです。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <!-- ファイル名と一致させる -->
    <string>zoomscheduler.night_meeting</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/sh</string>
        <!-- 上で作成したシェルスクリプトへのパス -->
        <string>/path/to/your/night_meeting.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Weekday</key>
        <!-- 日曜は0, 月曜は1, ..., 土曜は6 -->
        <integer>2</integer>
        <key>Hour</key>
        <!-- 24時間表示 -->
        <integer>18</integer>
        <key>Minute</key>
        <integer>59</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/path/to/logs/night_meeting.log</string>
    <key>StandardErrorPath</key>
    <string>/path/to/logs/night_meeting_error.log</string>
</dict>
</plist>
```
エラーログは`/path/to/logs/night_meeting_error.log`に書き込まれます。
1時間ごとに実行する等の命令もできます。
https://qiita.com/rsahara/items/7d37a4cb6c73329d4683
### シェルにフルディスクアクセスの権限を追加する
`/bin/sh`と`/usr/bin/open`がlaunchdの起動に従って呼び出されます。これをMacのセキュリティの設定で、これを実行する許可がデフォルトでは与えられていないので、「画面左上の🍏 -> システム設定 -> プライバシーとセキュリティ -> フルディスクアクセス」と移動して、下に表示される+ボタンを押して`/bin/sh`と`/usr/bin/open`を選択し、フルディスクアクセスの権限を与えます。
### 新しく作成したlaunchdジョブを読み込む
```bash
launchctl load ~/Library/LaunchAgents/zoomscheduler.night_meeting.plist
```
### テストする
```bash
launchctl start ~/Library/LaunchAgents/zoomscheduler.night_meeting.plist
```
これでzoomが起動すれば成功です。
## 時間帯等を変更したい場合
1. アンロードする
```bash
launchctl unload ~/Library/LaunchAgents/zoomscheduler.night_meeting.plist
```
2. `.plist`ファイルを編集
3. ロードする
```bash
launchctl load ~/Library/LaunchAgents/zoomscheduler.night_meeting.plist
```
Next.js 13 で URL の情報を取得する方法を整理する 【useRouter・useSearchParams・usePathname・useParams】(next/navigation)

### 概要

- next.js 12 から next.js 13 へのバージョンアップの大きな変更点の1つとして，ルーティングの方法が変更された点が挙げられます。next.js 12 では，"next/router" の `useRouter` を使用していましたが，next.js 13 では，"next/navigation" の `useRouter` を使用する必要があります。

- この記事では，"next/navigation" を使用して表示しているURLの情報を取得する方法について解説します。


### URL の情報を取得する

- URL の情報を取得する関数として，以下の3つがあります。

1. `useSearchParams`
1. `usePathname`
1. `useParams`

- Next.js の公式の [Pages ディレクトリから App ディレクトリへの移行に関する説明](https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#step-5-migrating-routing-hooks) では，以下のような記述があります。


> - 新しい useRouter は，`pathname` の文字列が返しません。`pathname` が必要なら，`usePathname` hook を使用してください。
> - 新しい useRouter は，`query` オブジェクトを返しません。`query` オブジェクトが必要なら，`useSearchParams` hook を使用してください。
> - `usePathname` と `useSearchParams` は一緒に使用できます。
> - `usePathname` と `useSearchParams` は，Client Componentでのみ使用できます。


- また，Next.js 13.3 から，[`useParams`](https://nextjs.org/docs/app/api-reference/functions/use-params) hook が導入されました。この hook は，表示中のページの dynamic params を取得するために使用します。この hook は Pages ディレクトリで使用すると `null` を返します。


### 動かしてみた

#### 前提(共通)

- 動作環境は，next.js 13.3 です。
- それぞれのサンプルコードを表示するページに追加します。
- ページへのパスは，"/demo/[id]" とします。[id]は dynamic params です。
- ページへのアクセスのときは，"/demo/hoge?params1=fuga" をブラウザに入力します。

#### `useSearchParams`

```react
import { useSearchParams } from 'next/navigation';

// 省略

  const searchParams = useSearchParams()
  console.log(searchParams)
  
// 省略
```

まずは，`useSearchParams` の結果です。ページにアクセスするときに付与したクエリパラメータが取得できることがわかります。

```bash: 実行結果
# /demo/hoge?params1=fuga にアクセスした結果

params1=fuga
```
`useSearchParams` で取得したパラメータは，以下のように取得できます。

```react
const searchParams = useSearchParams()
const params1 = searchParams.get('params1')
```

#### `usePathname`

```react
import { usePathname } from 'next/navigation';

// 省略

  const pathname = usePathname()
  console.log(pathname)
  
// 省略
```

次に，`usePathname` の結果です。アクセスしたときのパスが取得できることがわかります。このパスは，dynamic params を含みます。

```bash: 実行結果
# /demo/hoge?params1=fuga にアクセスした結果

/demo/hoge
```

#### `useParams`

```react
import { useParams } from 'next/navigation';

// 省略

  // useParams の戻り値はオブジェクト
  const params = useParams()
  console.log("result of useParams: %o", params)
  
// 省略
```

最後に，`useParams` の実行結果です。dynamic params である [id] が取得できていることが確認できました。

```bash: 実行結果
# /demo/hoge?params1=fuga にアクセスした結果

{id: 'hoge'}
```

取得したパラメータへのアクセスは，以下のようにできます。

```react
const params = useParams()
const id = params.id
```

### まとめ

URL の情報を取得する "next/navigation" の3つの hook を整理しました。

1. usePathname: dynamic params を含むパスを取得
1. useSearchParams: クエリパラメータを取得
1. useParam: dynamic params を取得

誰かの参考になれば幸いです。
Dockerfileとdocker-compose.ymlの書き方をまとめてみた

# はじめに
`Dockerfile`と`docker-compose.yml`の書き方についてまとめてみました

# 事前知識
`Dockerfile`と`docker-compose.yml`の書き方に入る前にDockerとは何かそしてDocker Composeとは何かについて簡潔にまとめます。
## Dockerとは
コンテナ仮想化技術を使って、アプリケーションを開発、配置、実行するための隔離された環境を提供するツール

### コンテナとは
パソコンやサーバ上で隔離され干渉されない環境のこと

## Docker Composeとは
複数のコンテナを定義して操作するためのツール

# `Dockerfile`とは
Dockerイメージを作成するためのファイル
`Dockerfile`を使い以下の順番でコンテナ作成が出来る

```mermaid
graph LR
    Dockerfile --> Dockerイメージ
    Dockerイメージ --> Dockerコンテナ
```
# `docker-compose.yml`とは
複数のコンテナを定義するためのファイル

# Dockerfileの書き方
### 代表的なDockerコマンド
|コマンド  |意味  |
|---|---|
|`FROM`  |DockerHubで公開中の元イメージを指定  |
|`LABEL`  |作成者情報・バージョンを指定  |
|`COPY`  |	イメージにファイルを追加  |
|`ADD`  |イメージにファイルを追加。圧縮ファイル指定時は圧縮まで実行される。  |
|`RUN`  |イメージをBuildする際に実行するコマンド  |
|`CMD`  |コンテナ起動の際に実行するコマンド  |
|`ENTRYPOINT`  |イメージ実行時に強要するコマンド  |
|`USER`  |`RUN`、`CMD`、`ENTRYPOINT`で指定のコマンドを実行するユーザー  |
|`WORKDIR`  |`RUN`、`CMD`、 `ENTRYPOINT`、`ADD`、`COPY`の際の作業ディレクトリ |
|`ENV`  |環境変数を設定  |
|`ONBUILD`  |Build完了時に実行するコマンド  |
|`EXPOSE`  |イメージ利用者にポートを解放  |
|`VOLUME`  |イメージ利用者に永続データが保存される場所を解放  |
|`ARG`  |docker build時に指定する引数  |

### `Dockerfile`記述例
```dockerfile
FROM node:18

CMD["npm","start"]
```

# `docker-compose.yml`の書き方
ファイル名は**必ず`docker-compose.yml`とする必要がある**
YAMLファイルは大きく`version`、`services`、`networks`、`volumes`から構成されている

```docker-compose.yml
version: [バージョン]

services:
    コンテナ1:
    コンテナ2:

networks:
    ネットワーク名1:
    ネットワーク名2:

volumes:
    ボリューム名1:
    ボリューム名2:
```

## `version`
Docker Composeで使用するバージョンを指定する

## `services`
`services`項目内にコンテナ作成に以下の必要な設定情報を定義していく

|項目  |意味  |
|---|---|
|`image`  |DockerHubで公開中の元イメージを指定  |
|`build`  |作成者情報・バージョンを指定  |
|`container_name`  |	イメージにファイルを追加  |
|`networks`  |イメージにファイルを追加。圧縮ファイル指定時は圧縮まで実行される。  |
|`volumes`  |イメージをBuildする際に実行するコマンド  |
|`volumes_from`  |コンテナ起動の際に実行するコマンド  |
|`ports`  |イメージ実行時に強要するコマンド  |
|`environment`  |`RUN`、`CMD`、`ENTRYPOINT`で指定のコマンドを実行するユーザー  |
|`depends_on`  |`RUN`、`CMD`、 `ENTRYPOINT`、`ADD`、`COPY`の際の作業ディレクトリ |
|`restart`  |環境変数を設定  |
|`command`  |Build完了時に実行するコマンド  |
|`env_file`  |イメージ利用者にポートを解放  |
|`entrypoint`  |イメージ利用者に永続データが保存される場所を解放  |
|`logging`  |docker build時に指定する引数  |
|`external_links`  |設定する外部リンク  |
|`network_mode`  |ネットワークモード設定  |

## `networks`
作成するネットワーク名を定義する

## `volumes`
作成するボリューム名を定義する








# 参考記事
[【Docker】Dockerfileとは｜書き方・コマンド一覧・イメージ作成手順を徹底解説](https://di-acc2.com/system/23152/)
[Docker Composeとは｜仕組み・使い方・YAMLファイルの書き方に至るまで徹底解説！](https://di-acc2.com/system/23357/#index_id0)
[Docker ドキュメント日本語化プロジェクト](https://docs.docker.jp/index.html)
web技術の基本をまとめてみた

## はじめに
これからエンジニアとして学んでいくうえで基本となる技術をまとめてみました。私と同じような初学者の方にも読みやすく、わかりやすい内容になるように心がけましたので一読いただけると幸いです。

# webアプリケーションの仕組みについて
webページを観覧するとき、プラウザにURLを打ち込むことで、webサーバに対してwebページが見たいという要求を投げかけます。その要求を受け取ったサーバ側は必要なファイルをプラウザ側に渡してあげることでwebページを見ることができる仕組みになっています。

この時プラウザからサーバに送られる要求のことを「__リクエスト（request）__」、サーバーからプラウザに対する返答を「__レスポンス（response）__」と呼びます。

このようにクライアントとサーバで情報のやり取りをすることでwebページを閲覧することができます。
ただ、インターネットには様々な種類のコンピュータが接続されてるため、どのように情報をやり取りするかの取り決めをする必要があります。この取り決めを「__通信プロトコル__」と呼びます。
webページの通信プロトコルとして、「__HTTP（Hyper Text Transfer Protocol）__」が、当時既に存在していたファイル転送のためのプロトコル（FTP）やメール転送のためのプロトコル（SMTP）を参考にして考案されました。

# どのように情報を受け渡すのか
スマートフォンやパソコンなどのインターネットに接続されている機器はデータの送信元や送信先を識別するために「__IPアドレス__」という番号を使用します。このIPアドレスを使用してインターネットの中で情報の送受信を担うのが「__TCP/IP（ティーシーピー・アイピー）__」というプロトコルです。
TCP/IPはプラウザから受け取ったHTTPリクエストなどの情報を「__パケット__」と呼ばれる小さな単位に分割して送信し受信先ではそれを元通りに組み立ててwebサーバに渡します。
パケットに分割することで送信に失敗したときすべての情報を送りなおす必要がなく、失敗したパケットだけ送りなおせば済むため効率的に情報の受け渡しが可能になります。
ただし、TCP/IPは受信した情報がどのようなプロトコルやアプリケーションによって処理をされているかを判断することはできません。そこで「__ポート__」という仕組みが使用されます。
情報を受け取るアプリケーションは必ず「待ち受けポート」を決めて情報を待つことで、情報が対象のアプリケーションに正しく渡されるようになります。

# ログイン機能のしくみ
HTTPはリクエストに対してレスポンスを返すだけのシンプルなやりとりで完結するため、前後の状況に関係なく常に同じレスポンスが返されます。このようなプロトコルを「__ステートレス・プロトコル__」と呼びます。一方で、前の状態を保持して、そのあとの処理結果に反映させるプロトコルを「__ステートフル・プロトコル__」といいます。
それではステートフルなログイン機能はどのように実現されているのでしょうか。

## どのように状態を保持するか
状態を保持するために考案された技術が「__Cookie（クッキー）__」です。
CookieはHTTPの使用を拡張してwebアプリケーションとwebプラウザの間で情報を交換できるようにしたものです。
Cookieを利用することでwebプラウザに状態を持たせることができますが、セキュリティ上の問題がありました。ある程度の知識を持った人であれば、ツールを利用して内容を覗くことができてしまうためです。
この問題を解決するために考え出されたのが「__セッション__」と呼ばれる仕組みです。セッションはクライアントがサーバに接続した時点で開始され、サーバから切断すると終了するというもので、一連の流れを管理することをセッション管理といいます。セッション管理ではセッションを識別するID（__セッションID__）を生成し、ユーザ情報や処理状況を紐づけます。このセッションIDをCookieに保存することで、通信時にセッションIDの受け渡しだけで安全に情報のやりとりをすることができます。

# 単語をまとめる
<dl>
<dt>リクエスト（request）</dt>
<dd>クライアントからサーバに送られる要求</dd>
<dt>レスポンス（response）</dt>
<dd>サーバからクライアントに送られる返答</dd>
<dt>プロトコル</dt>
<dd>どのように情報をやり取りするかの取り決め</dd>
<dt>HTTP（Hyper Text Transfer Protocol）</dt>
<dd>クライアントとサーバで情報のやりとりをするためのプロトコル</dd>
<dt>IPアドレス</dt>
<dd>インターネットに接続されている機器がデータの送信元や送信先を識別するために使用する番号</dd>
<dt>TCP/IP（ティーシーピー・アイピー）</dt>
<dd>IPアドレスを使用してインターネットの中で情報の送受信を担うプロトコル</dd>
<dt>パケット</dt>
<dd>送受信する情報を小さく分割した時の単位</dd>
<dt>ポート</dt>
<dd>どのアプリケーションが通信するかを識別する仕組み</dd>
<dt>ステートレス・プロトコル</dt>
<dd>前後の状況に関係なく常に同じレスポンスが返されるプロトコル</dd>
<dt>ステートフル・プロトコル</dt>
<dd>前の状態を保持して、そのあとの処理結果に反映させるプロトコル</dd>
<dt>Cookie（クッキー）</dt>
<dd>HTTPの使用を拡張してwebアプリケーションとwebプラウザの間で情報を交換できるようにしたもの</dd>
<dt>セッション</dt>
<dd>クライアントがサーバに接続した時点から切断までのこと</dd>
</dl>

# 参考
この記事を書く上で参考とさせていただいたものです。

https://gihyo.jp/book/2010/978-4-7741-4235-7
アメリカ特許のAPI

# アメリカ特許のAPI
アメリカは特許や商標などのAPIを公開しているようです。
アメリカの特許・商標などのAPI
https://developer.uspto.gov/api-catalog

## 使用方法
使用方法をコード化したので公開します。
まずは、特許の検索APIは、下記リンク先にあります。
https://developer.uspto.gov/api-catalog/bulk-search-and-download

このリンク先の中の、"API Syntax"をクリックするとAPIの詳細が分かります。(下記リンク)
https://developer.uspto.gov/ibd-api/swagger-ui/index.html

このAPIの中の"GET"メソッドの"publications"から情報を取得するコードをPythonで書きました。
```Python
import requests

endpoint = "https://developer.uspto.gov/ibd-api/v1/application/publications"
params = {
    "searchText": "Machine Learning",
    "publicationFromDate": "2022-12-29",
    "PublicationToDate": "2023-01-01"
}

# APIリクエストを送信
response = requests.get(endpoint, params=params, verify=False)

# レスポンスのステータスコードを確認
if response.status_code == 200:
    # レスポンスのJSONデータを取得
    data = response.json()
    print(data)
    # 結果が存在することを確認
    if 'results' in data:
        # レスポンスの処理
        for result in data['results']:
            # 'inventionTitle'が存在することを確認
            if 'inventionTitle' in result:
                inventionTitle = result['inventionTitle']
                print(f"Title: {inventionTitle}")
            # 'publicationDate'が存在することを確認
            if 'publicationDate' in result:
                publicationDate = result['publicationDate']
                print(f"PublicationDate: {publicationDate}")
            # 'abstractText'が存在することを確認
            if 'abstractText' in result:
                abstractText = result['abstractText']
                print(f"Abstract: {abstractText}")
            # 'claims'が存在することを確認
            if 'claimText' in result:
                claims = result['claimText']
                print(f"Claim: {claims}")
else:
    print("エラーが発生しました。")
```

本コードにおいて、
検索は、"Machine Learning"、
検索する公開日の期間は、2022年12月29日から2023年1月1日にしています。
また取得する情報は、「発明のタイトル」、「公開日」、「要約」、「特許請求の範囲」です。

## 他にも
他にも色々な項目の情報を取得できますが、膨大なデータのため、割愛します。

## 感想
ChatGPT4に訊けば、すぐ出来るだろうと思っていたら、「最近の情報には対応していません」と言われてしまいました。。。
ChatGPT無しの開発速度が遅いことについて、反省しました。。。

## 追記（2025年時点）：OA2JPプロジェクトに挑戦して見えた限界

2025年、私は日本の特許事務所での実務経験を元に、米国特許庁（USPTO）の拒絶理由通知（Office Action）に対して、日本語コメントを自動生成する支援ツール「OA2JP」を試作しようとしました。

当時の課題意識として、日本の特許事務所では米国や欧州の拒絶理由通知に対する「英語コメントの作成」に時間がかかりがちで、特に中間処理段階で実務負担が大きいと感じていました。そのため、**公開APIから審査情報を取得し、それを元にChatGPTで日本語コメント草案を出力する**という仕組みを構想しました。

---

### 試したAPIと状況（2025年時点）

| API名 | 結果 | 備考 |
|-------|------|------|
| `oa_citations` | ❌ データ取得失敗 | 拒絶日（oaMailDate）、引用特許（citedPatentNumber）などが取れない |
| `oa_rejections` | ❌ ヒットせず | 拒絶理由分類情報が取れると期待したが、動作せず／データ不十分 |
| `office action text retrieval` | ❌ ID.me認証が必要で断念 | 米国居住者でないと通過困難（日本からは難しい） |
| `patentsview` | ❌ API廃止（410 Gone） | かつて有用だったが、2024年末に廃止 |
| Google Patents API | ❌ 公式APIなし | サードパーティ（SerpAPIなど）経由しか取得できず、信頼性／費用面で断念 |

---

### 得られた教訓・制限

- 「APIがある」＝「実用できる」ではない（403/410/認証などで使えないことも）
- 現時点（2025年）では、**公開APIだけでOffice Actionの全文を自動取得→コメント生成**という構成は、**日本の開発者には現実的でない**
- 一方で、**PDFを手動で取得し、ChatGPTに日本語コメントを生成させる**ルートは可能性がある（OAのたたき台支援）

---

### 最後に：記録として残す意味

今回は残念ながら目的達成には至りませんでしたが、試行ログを残しておくことで、
同じように「OAの自動処理」に取り組む誰かのヒントになればと思います。

また、もし今後、**USPTOがAPIの国際公開を拡大したり、Google Patentsが公式APIを提供**するような動きがあれば、再挑戦する価値があると考えています。

何か代替手段をご存知の方がいれば、ぜひコメント等で共有いただけると嬉しいです。
引数ラベルってなんだ...?省略ってなんだぁ？

# Swiftにおける引数の書式

## 【背景】
新卒研修の関係で、SwiftUIという新しめの環境ではなく、storyboardというものを勉強する為に、同期に借りたSwiftの参考書↓
![たった2日でマスターできるiPhoneアプリ開発集中講座Xcode11|Swift5|対応](https://m.media-amazon.com/images/I/51boJ4nOK+L._SX258_BO1,204,203,200_.jpg)
で、
> 引数ラベルの省略は、_(アンダースコア)を使う。

と書いてあった。
よく分からんが「ほーん」と思いながら進めていくと
```swift
func tableView(numberOfRowsInSection section: Int)
//                   ↑   こいつらは何だ？　　　　　　↑  
```
こんな記述があるじゃないか。

Swiftの関数の引数が1個につき3項目あることに気づき、
「えっ、引数って <引数名: データ型>だけじゃなかったんか..?」
と思ったから調べました。

Swiftの引数は、　``(引数ラベル 引数名: データ型)``
---
で構成されるようです。
Swiftで関数を、引数の**省略なしで**書くこのようになります。

```swift
func 関数名(引数ラベル 引数名: データ型)-> 返り値のデータ型{
    //~~中身が詰まってる~~
    return 〇〇
}
```
つまりどういうことかと言いますと
|名前|意味|
|---- | ---- |
|引数ラベル | 呼び出す時の名前 |
|引数名|関数内での名前|
|データ型|引数のデータ型|
pyaudioがインストールできないときの対処方法

### ※お急ぎの方は、「結論」を参照してください
# 環境
* Ubuntu20.04.5LTS
* Python 3.8.10

# 状況
　pyaudioをインストールしようとしたところ、以下のエラーが発生した。
```
ERROR: Could not build wheels for pyaudio, which is required to install pyproject.toml-based projects
```

# 試したこと
### １つ目
```
$ sudo apt install portaudio19-dev
$ pip install pyaudio
```
### ２つ目
```
$　pip install python3-pyaudio
ERROR: No matching distribution found for python3-pyaudio
```
いずれのだめだった

# 結論
以下を実行したら正常にインストールできた。
```
$ pip install PyAudio
```
https://pypi.org/project/PyAudio/
FireDACインストール関係リンク先覚書


XE3 以外の IDE のバージョンに FireDAC をインストールする
https://support.embarcadero.com/jp/article/42975

AnyDAC および XE3/XE4 FireDAC から XE5 FireDACへの移行について
https://support.embarcadero.com/article/43590
Apache Kafkaを動かす環境を作成する

仕事でKafkaを使うことになったので、自分のパソコンにもKafkaの環境を入れてみようと思いました。
ひとまずはKafkaでCLIによる送信・受信ができるまでを目標に行います。

## インストール
Kafka自体は圧縮ファイルで一式ダウンロードして試すこともできるが、Dockerを使うという前提条件があった。
そのため、Dockerを使って環境構築をする。
Windowsの場合は下記のインストールが必要になる。
- WSL2
- Docker Desktop

## WSL2
PowerShellを管理者として実行し、以下のコマンドを行う。
`wsl --install`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565426/2e1ac01c-7101-8262-6ac1-a01d3d3d6986.png)

レスポンスにある通り、これをインストールしたら再起動をする必要がある。

## Docker DeskTop
下記からインストールを行う。
https://docs.docker.com/desktop/install/windows-install/

基本的にデフォルトの設定から変更する必要なく、そのまま進んでいけばよい。

## 再起動後
再起動後Docker Desktopが立ち上がる。
正常に起動できれば以下のような画面になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565426/145b0f64-df39-7146-9e02-b2da4647c537.png)

## Docker Imageを利用してKafkaの環境を作成する。
ymlファイルを利用して、docker-composeコマンドを使い環境を作成する。

### Docker、Docker composeがインストールされていることを確認
それぞれのバージョンを確認して、レスポンスが返ってくることを確認すればよい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565426/a9b0112f-f7b1-2a06-391d-34d3123b41dc.png)


### ymlファイル

```
version: "3"
services:
  kafka:
    image: confluentinc/cp-kafka
    environment:
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:32181
      KAFKA_LISTENERS: EXTERNAL_SAME_HOST://:29092,INTERNAL://:9092
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:9092,EXTERNAL_SAME_HOST://localhost:29092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL_SAME_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    ports:
      - 29092:29092
    depends_on:
      - zookeeper
  zookeeper:
    image: confluentinc/cp-zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 32181
```

### 起動コマンド
```
docker-compose -p test -f "ymlファイルのパス" -d
```

これで問題なければ以下のような画像の構成になるはず。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565426/46746691-280b-8b51-21bb-879981f2797f.png)

コンテナが作れたら、kafkaのコンテナに入り一応下記のコマンドを執行してkafkaのCLIが実行できることを確認しよう
`kafka-topics --version`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565426/ff0dd352-8876-e9e1-d443-e51ce9a91ef7.png)

## トピックを作って送受信できることを確認する
### トピックを作成する
```
sh-4.4$ kafka-topics --bootstrap-server localhost:9092 --create --replication-factor 1 --partitions 1 --topic sample1
Created topic sample1.
```

### トピックにメッセージを送信させる
```
sh-4.4$ kafka-console-producer --bootstrap-server=localhost:9092 --topic sample1
>testmessage
>
>;
>^C
```

### トピックにメッセージが送られたかを確認する
```
sh-4.4$ kafka-consoconsumer --bootstrap-server localhost:9092 -topic sample1 --from-beginning
testmessage

;
```

これでKafkaを利用できる環境は整った。
次はdebeziumとKafkaを連携できるようにしたいと思う。
GCP deployment-managerでサービスアカウントを作成してみた

# 概要
デプロイメントマネージャでサービスアカウントを作成してみました。
デプロイメント名：nori-test-service-account
サービスアカウント名：nori-test-service-account

先日はデプロイメントマネージャでネットワークを作成しました。以下の通り。

https://qiita.com/nori-channel/items/43ab1b56308d1239464b

# ソースコード
以下の通りYAMLで記述しました。プロジェクト名はご自分のプロジェクト名に読み替えてください。
```service-account3.yaml
resources:
- type: gcp-types/iam-v1:projects.serviceAccounts
  name: nori-test-service-account
  properties:
    accountId: nori-test-service-account
    displayName: nori-test-service-account
  accessControl:
    gcpIamPolicy:
      bindings:
      - role: roles/editor
        members:
        - "serviceAccount:nori-test-service-account@<PROJECT_NAME>.iam.gserviceaccount.com"
```

# 実行結果
まずデプロイメントを作成して、プレビュー状態にしました。
```
$ gcloud deployment-manager deployments create nori-test-service-account --config service-account3.yaml --preview
The fingerprint of the deployment is 9kyoYfEGgsjWnsRAMn0nqg==
Waiting for create [operation-1684649682963-5fc2e11ec8bd4-dac9489d-3ef139a9]...done.                                                                                                                      
Create operation operation-1684649682963-5fc2e11ec8bd4-dac9489d-3ef139a9 completed successfully.
NAME                       TYPE                                       STATE       ERRORS  INTENT
nori-test-service-account  gcp-types/iam-v1:projects.serviceAccounts  IN_PREVIEW  []      CREATE_OR_ACQUIRE
```
デプロイメントを更新して、リソースを作成しました。
```
$ gcloud deployment-manager deployments update nori-test-service-account
The fingerprint of the deployment is QSYC1B0yV-qZwWcftqj1MA==
Waiting for update [operation-1684649705704-5fc2e13478c34-38a52960-1ab84ab6]...done.                                                                                                                      
Update operation operation-1684649705704-5fc2e13478c34-38a52960-1ab84ab6 completed successfully.
NAME                       TYPE                                       STATE      ERRORS  INTENT
nori-test-service-account  gcp-types/iam-v1:projects.serviceAccounts  COMPLETED  []
```
サービスアカウントが作成されたのをGUIで確認しました。
![Screenshot from 2023-05-21 15-18-49_r2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/2ff3e1a7-91f9-94c2-3d55-d2d01b809997.png)
念の為デプロイメントもできているのをGUIで確認しました。
![Screenshot from 2023-05-21 15-16-58-1_r2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192024/357039fe-574c-8298-15d4-32569e65e6e5.png)

# まとめ
何かの役に立てばと。
【Flutter】Dioを使ってHTTPリクエスト(GET)

# 使用パッケージ

https://pub.dev/packages/dio

叩くAPIは [openweathermap](https://openweathermap.org/) を使わせてもらう。

# 使い方

### pubspec.yaml にパッケージを追加

```yaml
dependencies:
  json_annotation: ^4.8.1
  dio: ^5.1.2

dev_dependencies:
  build_runner: ^2.4.4
  json_serializable: ^6.7.0
```

APIの取得結果であるJSON文字列を、クラスに詰め替えるのに、
[json_serializable](https://qiita.com/kenta4327/items/d58a73cc85b944521ccc) を使用したので、ついでにそのパッケージも追加。

### 使ってみる

```dart
    final urlPath = 'https://api.openweathermap.org/data/2.5/weather';
    // 東京タワーの緯度経度
    final lat = '35.6579781';
    final lon = '139.7433048';
    final appid = 'xxxxxxxxxx';
    final units = 'metric';
    final lang = 'ja';
    final url = '${urlPath}?lat=${lat}&lon=${lon}&appid=${appid}&units=${units}&lang=${lang}';

    final dio = Dio();
    final response = await dio.get(url);
    final cityWeatherMap = json.decode(response.toString());

    final city = City.fromJson(cityWeatherMap);

    // とりあえず場所と天気だけ表示
    print('CityWeathre: ${city.id}, ${city.name}, ${city.weather?[0].description}');
```

([http](https://qiita.com/kenta4327/items/718147677ad30d73f087) と比較して)
URLの文字列をそのまま投げられる。
レスポンスはオブジェクトなので `toString()` してデコードする。
超簡単！ESP32をBlueToothスピーカーにする方法

　ESP32のブルートゥース機能を使ってみようとサイトを色々めぐりました。

https://qiita.com/Tw_Mhage/items/7473d694a31ca6bd9592

https://edf-player.hatenablog.com/entry/2021/03/29/022721

https://github.com/pschatzmann/ESP32-A2DP

この３番目のサイトが、これでできるのか？いうくらいとてもシンプルなものだったので試してみました。その報告です。使ったのは、
![KIMG0472.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/7355e7d5-b3fd-7b8a-e239-3ffcbab048d5.jpeg)

:::note
１．FREENOVE ESP32-WROVER-DEV（"ArduinoIDE2.1.0"では"ESP32 Wrover Module"）
２．PCM5102DACモジュール
３．ヒャッキン 300円（330円）アクティブスピーカー
:::
3番目のサイトから"ESP32-A2DP-main.zip"をダウンロードして、"ArduinoIDE"で
```
Sketch → Include Library → Add .ZIP Library…
```
という順に進み、取り込みます。そして、新たに加わったExamplesの
```
File → Examples → ESP32-A2DP → bt_music_receiver_simple
```
を開きコンパイルし、ESP32に転送するだけです。
```c:bt_music_receiver_simple.ino
#include "BluetoothA2DPSink.h"

BluetoothA2DPSink a2dp_sink;

void setup() {
  a2dp_sink.start("MyMusic");  
}

void loop() {
  delay(1000); // do nothing
}
```
こんなに簡単にできるなんて、Phil Schatzmannさんに感謝、感激ですね。もちろん裏側に様々なライブラリが隠されている訳です。試しに少し機能をつけ足そうとすると、実行ファイルのサイズが大きくなって"Sketch too big"と叱られます。そんな時には、
```
Tools → Partition Scheme
```
で"Huge APP" を選択、つまり
![hugeSketch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/fee55160-7e9c-79bd-5700-128b2616dd58.jpeg)
としてやると、問題なく通るようです。
　I2Sのピンが他のピンに変えられるかも試してみました。ライブラリ内では
```c:
This creates a new Bluetooth device with the name “MyMusic” and the output will be sent to
the following default I2S pins which need to be conected to an external DAC:

bck_io_num = 26
ws_io_num = 25
data_out_num = 22
```
と定められてるようです。それを
```c:
i2s_pin_config_t my_pin_config = {
        .bck_io_num = 4,
        .ws_io_num = 2,
        .data_out_num = 0,
        .data_in_num = I2S_PIN_NO_CHANGE
};
```
としてみましたが問題なく動作しました。
```c:
#include "BluetoothA2DPSink.h"

// I2S PCM5102 への出力
// #define I2S_LRC  25
// #define I2S_DOUT 22
// #define I2S_BCLK 26

BluetoothA2DPSink a2dp_sink;

void setup() {
    i2s_pin_config_t my_pin_config = {
        .bck_io_num = 4,
        .ws_io_num  = 2,
        .data_out_num = 0,
        .data_in_num = I2S_PIN_NO_CHANGE
    };

  a2dp_sink.set_pin_config(my_pin_config);
  a2dp_sink.set_volume(128);// uint8_t 0-255
  a2dp_sink.start("MyMusic");
}

void loop() {
  delay(1000); // do nothing
}

```
　私の古い"SONY WALKMAN"から"MyMusic"にブルートゥース接続して聴いてみましたが良い音です。ただWALKMANから音量を変えることはできないみたいです。なので動作中に音量を変えたければ、loop()内に何か記述することになるのかと思われます。
　駄文を最後まで見ていただきありがとうございました。
データサイエンスのための微分積分 第9回 極限の性質

:::note info
本記事は数学講座[2.8極限の性質](https://www.matongxue.com/lessons/586/parts/935)を勉強して投稿したメモです。詳細は元の素晴らしい講座のページをチェックしてください。
:::

# まとめ

- **極限の一意性（きょくげんのいちいせい）**：ある関数や数列の極限が存在する場合、**その極限は唯一である**という性質です。つまり、極限が存在する場合、その値は一つだけです。

- **極限の局所有界性（きょくげんのきょくしょゆうかいせい）**：ある関数や数列がある点において極限を持つ場合、その点の近くでは有界であるという性質です。つまり、局所的には極限を持つ関数や数列は、**その近くで値がある範囲に収束すること**を意味します。

- **極限の局所保符号性（きょくげんのきょくしょほごうせい）**：ある関数や数列がある点において極限を持ち、その極限が正または負である場合、その点の近くでは符号が保たれるという性質です。つまり、局所的には極限を持つ関数や数列は、**その近くで正の値または負の値を保ち続けること**を意味します。
Snowflakeのすべてのロールに付与された権限を確かめたい。

Snowflakeの権限を棚卸するときに、Snowflakeのすべてのロールに付与された権限を確かめたくなるので、やり方を整理した。

データベース内のオブジェクトの権限を確かめたい場合は、 Information Schemaにある `OBJECT_PRIVILEGES` ビューを確認する。

https://docs.snowflake.com/ja/sql-reference/info-schema/object_privileges

## 確認用SQL
Snowflake Scriptingですべてのロールに対し `SHOW GRANTS TO ROLE` を実行し、結果をテーブルに格納する。

```SQL
-- 結果格納先のテーブルを作成
create or replace temp TABLE GRANTS (
	"created_on" TIMESTAMP_LTZ(3),
	"privilege" VARCHAR(16777216),
	"granted_on" VARCHAR(16777216),
	"name" VARCHAR(16777216),
	"granted_to" VARCHAR(16777216),
	"grantee_name" VARCHAR(16777216),
	"grant_option" VARCHAR(16777216),
	"granted_by" VARCHAR(16777216)
);

begin
    -- ロールの一覧を取得
    show roles;
    let c1 CURSOR FOR select "name" from table(result_scan(last_query_id()));
    for r in c1 do
        -- show grantsの結果をロールごとに挿入
        let role_name string := r."name";
        show grants to role identifier(:role_name);
        insert into grants select * from table(result_scan(last_query_id()));
    end for;
end;
```

結果の確認
```SQL
select * from grants;
```

## 補足

### IDENTIFIERの引数

文字列をオブジェクト識別子に変換するIDENTIFIERの引数は文字列、セッション変数、バインド変数、Snowflakeスクリプト変数しか受け取らない。
そのため、レコードの変数を直接受け取れず、Snowflakeスクリプト変数を介する必要がある。

```SQL
-- NGなパターン
-- カーソル内のレコードを参照
for r in c1 do
    show grants to role identifier(r.name);
end for;

-- OKなパターン
for r in c1 do
    let role_name string := r."name";
    show grants to role identifier(:role_name);
end for;
```

https://docs.snowflake.com/ja/sql-reference/identifier-literal
【Flutter】HTTPリクエスト(GET)

# 使用パッケージ

https://pub.dev/packages/http

を使用して、HTTPリクエストしてみる。
なおAPIは [openweathermap](https://openweathermap.org/) を使わせてもらう。

# 使い方

### pubspec.yaml にパッケージを追加

```yaml
dependencies:
  json_annotation: ^4.8.1
  http: ^0.13.6

dev_dependencies:
  build_runner: ^2.4.4
  json_serializable: ^6.7.0
```

APIの取得結果であるJSON文字列を、クラスに詰め替えるのに、
[json_serializable](https://qiita.com/kenta4327/items/d58a73cc85b944521ccc) を使用したので、ついでにそのパッケージも追加。

### 使ってみる

```dart
    final urlPath = 'https://api.openweathermap.org/data/2.5/weather';
    // 東京タワーの緯度経度
    final lat = '35.6579781';
    final lon = '139.7433048';
    final appid = 'xxxxxxxxxx';
    final units = 'metric';
    final lang = 'ja';
    final url = '${urlPath}?lat=${lat}&lon=${lon}&appid=${appid}&units=${units}&lang=${lang}';

    final response = await http.get(Uri.parse(url));
    final cityWeatherMap = json.decode(response.body);

    final city = City.fromJson(cityWeatherMap);

    // とりあえず場所と天気だけ表示
    print('CityWeathre: ${city.id}, ${city.name}, ${city.weather?[0].description}');
```

URLの文字列をUri形式に変換してGETする。
レスポンスの `body` がJSON文字列なので、そのままデコードすればOK。
（検索エンジニアのための）ニューラルネットワークの基本

社内むけを含む説明用のスライドですが、私用の時間で作成されており、かつ、社外秘情報を含まないため公開します。
![スライド1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/bb100a40-c69a-0fb0-71e4-262cb845ed63.png)
![スライド3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/127d9547-d428-d97e-61d5-42a62ba5f4b9.png)
![スライド4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/82820923-020d-7f83-6364-ade145b7d357.png)
![スライド5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/b541d233-cace-5619-8dd6-032a04d3c688.png)
![スライド6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/84584782-23c5-d372-92ab-31f6a4f37dbe.png)
![スライド7.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/7b27c987-0601-0ce5-a590-4864fd0cecfe.png)
![スライド8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/0f85004f-6c20-9ca7-7bfd-4cb0e3498ef7.png)
![スライド9.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/65e19beb-11fa-d34d-61ac-1efe9fc4a753.png)
![スライド10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/76d093a0-154b-efe0-a951-e725d322e0f2.png)
![スライド11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/2b0b25c2-d1b3-30e7-d72b-9def6ddaafb9.png)
![スライド12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/ff6018cd-4e51-3479-a09d-53e9463ea2b5.png)
![スライド13.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/411dd44c-e617-ceb9-775b-0b010c4e5487.png)
![スライド14.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/a8c2f322-81ab-f628-0afd-b4b410b1c2cd.png)
![スライド15.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155352/fe29e41b-76e9-c217-d120-7f27321f7096.png)
データサイエンスのための微分積分 第8回 無限大

:::note info
本記事は数学講座[2.7無限大](https://www.matongxue.com/lessons/585/parts/4678)を勉強して投稿したメモです。詳細は元の素晴らしい講座のページをチェックしてください。
:::

# 無限大の三つの定義

```math
\begin{array}{c|c|c|c}
    \hline
    \quad x\to x_0\ \text{関数極限} \quad\quad&\quad x\to x_0\ \text{正の無限大}\quad &\quad x\to x_0\ \text{負の無限大}\quad &\quad x\to x_0\ \text{無限大}\quad \\
    \hline 
    \\
    \forall\epsilon > 0 &\forall M > 0&\forall M > 0&\forall M > 0\\
    |f(x) - L| < \epsilon &f(x) > M&f(x) < -M&|f(x)| > M\\
    \\
    \hline
\end{array}
```

# 無限大

正の無限大と負の無限大の定義は、無限大のと似ていますので、省略します。

**無限大の定義：**

定義: 関数$f(x)$が$\mathring{U}(x_0)$上で定義されているとする。任意の$M > 0$に対して、$\delta > 0$が存在し、$\forall x\in\mathring{U}(x_0,\delta)$に対して以下が成り立つ場合、関数$f(x)はx\to x_0$のときに**「無限大 (Infinity)」**と呼ばれる。
```math
|f(x)| > M
```

記号としては、以下のように表される:
```math
\lim_{x\to x_0}f(x)=\infty
```
ここで、$\mathring{U}(x_0)$は$x_0$を中心とする開区間、$\mathring{U}(x_0,\delta)$は半径$\delta$の開球を表します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327130/46f10f32-a659-2e25-9409-9b72e74934b3.png)

## 極限が存在しない

$x\to x_0$のときの無限大は$\lim_{x\to x_0}f(x)=\infty$と表されますが、これは**任意の定数に収束しない**ため、$x\to x_0$の極限は存在しません。

したがって、$\lim_{x\to x_0}f(x)=\infty $　$➡$　$f(x)$は$x\to x_0$のときの**極限が存在しないこと**を意味します。

# 無限大と無限小

定理: 自変数の同一の変化プロセスにおいて、以下の場合：

- $f(x)$が無限小であり、対応する局所領域で$f(x)\ne 0$である場合、$\displaystyle\frac{1}{f(x)}$は無限大である。この結論は$\displaystyle\frac{1}{0}=\inftyと$も表される。
- $f(x)$が無限大である場合、$\displaystyle\frac{1}{f(x)}$は無限小である。この結論は$\displaystyle\frac{1}{\infty}=0$とも表される。

例えば、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3327130/524b6f67-233e-887d-5916-3ae37da2cea6.png)

# 練習：

- 例１：二つ無限大の和が必ず無限大ですか？

$f(x)=x，g(x)=-x$とおくと、$x\to \infty$のとき、$f(x)とg(x)$はいずれも無限大ですが、

$\displaystyle \lim_{x \to \infty}f(x)+g(x)=0$

このとき、$f(x)+g(x)$は**無限大**ではありません。
リモートリポジトリをローカル環境に作成してgitの練習をする

## はじめに
gitコマンドの挙動を覚えるためには、ひたすらに実践するのが効果的です。
ただ、githubなどにリモートリポジトリを作成すると、ターミナルだけでgitコマンドの実行をすることが難しく、勉強するのにめんどくささがつきまといます。

そこで、ローカル環境にリモートリポジトリを作成することで、ターミナルだけでgitの練習ができる環境を構築します。
## ローカルにリモートリポジトリを作成するとは
リモートリポジトリについては、gitのドキュメントに以下の記述があります。

>リモートリポジトリとは、インターネット上あるいはその他ネットワーク上のどこかに存在するプロジェクトのこと

リモートリポジトリとは、githubやAWSのcodecommitなど、ローカル環境ではないところを指すような印象を持ちますが、ローカル環境というネットワークを使えば、ローカルリポジトリとリモートリポジトリをローカル環境だけで構築することができます。

## ローカルにリモートリポジトリを作成する手順
まず初めに、ホームディレクトリでローカルリポジトリ用とリモートリポジトリ用のフォルダを作成します。
```
$ mkdir ~/git_local
$ mkdir ~/git_remote
```
今回はローカルリポジトリ用にgit_localを、リモートリポジトリ用にgit_remoteを作成しました。
それぞれのフォルダをgit管理します
```
$ cd ~/git_local
$ git init

$ cd ~/git_remote
$ git init
```
ここで、ローカルリポジトリ(~/git_local)のリモートリポジトリを~/git_remoteに指定するために
```
$ git remote add origin ~/git_remote
```
と叩くと、~/git_localのリモートリポジトリが~/git_remoteとして指定されます。
試しに、↓のコマンドを叩くと、リモートリポジトリのディレクトリが表示されます。
```
$ git remote -v
   origin	/Users/[ユーザー名]/git_remote (fetch)
   origin	/Users/[ユーザー名]/git_remote (push)
```
これでローカルリポジトリとリモートリポジトリの準備が整いました。
試しにローカルでファイルを作成し、コミットしてリモートリポジトリにpushしてみます。
```
$ cd ~/git_local
$ git checkout -b develop
$ touch index.html
$ git add index.html
$ git commit -m "first commit"
$ git push origin develop
    Enumerating objects: 3, done.
    Counting objects: 100% (3/3), done.
    Writing objects: 100% (3/3), 209 bytes | 209.00 KiB/s, done.
    Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
    To /Users/[ユーザー名]/git_remote
     * [new branch]      develop -> develop
```
リモートリポジトリにpushすることが成功しました。
試しに~/git_remoteに移動して確かめてみます。
```
$ cd ~/git_remote
$ git checkout develop
$ ls
    index.html
```
~/git_localで作成したファイルが~/git_remoteのdevelopブランチで存在することが確認されました。
コミットログをターミナル上で確認するには、
```
$ git config --global alias.lol 'log --graph --decorate --pretty=oneline --all --abbrev-commit'
```
と設定すると、git lolと打つだけでコミットログがターミナル上に描画されます。

## まとめ
gitのリモートリポジトリをローカル環境に作成し、gitの練習が行いやすくなりました。
gitコマンドは失敗を恐れずに叩き続けることで身につくと思うので、練習環境を整えてgitへの理解を深めていきたいです。
【Java Bronze」1Z0-818-JPN 合格体験記

# はじめに
タイトルにある資格をこれから受験する方に向けての記事になります。

# スペック
* [スッキリわかるJava入門 第3版 (スッキリわかる入門シリーズ)](https://amzn.asia/d/cuQJeti "Amazon Link")本書の内容を
理解できる程度
* Java実務経験なし

# 勉強方法・勉強期間
<dl>
  <dt>勉強方法</dt>
  <dd>・Javaプログラマ Silver SE 8（紫本）</dd>
  <dd>・分からない箇所は逐一検索</dd>
  <dd>・「Java Bronze　練習問題」で検索して出てくる問題を解く</dd>
  <dt>勉強期間</dt>
  <dd>・2週間（平日・休日とも2時間程度）</dd>
</dl>

# 押さえておくポイント
* **基本データ型と参照型**
* **配列の宣言・領域確保・値の代入**
* **基本三構造(if・switch・for・while)**
* **インスタンス化**
* **コンストラクタ**
* **インスタンスメンバとクラス(static)メンバ**
* **オーバーロード**
* **アクセス修飾子**
* **継承**
* **オーバーライド**
* **thisとsuper**
* **抽象クラス(abstract)**
* **インターフェイス(interface)**
* **ポリモーフィズム**
* **コンパイルエラーと実行時エラー**
* **パッケージとインポート**

上記の項目を理解して説明できる様になれば合格できると思います。

# 最後に
勉強で一番大切なのは,毎日コンスタントに勉強できるかだと思いますので焦らず進めていきましょう。

以上になります。

# 参考文献
* [Javaプログラマ Silver SE 8(紫本)](https://amzn.asia/d/0Pz49lT "Amazon Link")
【Python】カウントソートの実装

# はじめに
カウントソートの実装例です。

# 実装
```python:counting_sort.py
from typing import List

def counting_sort(numbers: List) -> List[int]:
  max_num = max(numbers)
  counts = [0] * (max_num + 1)
  result = [0] * len(numbers)
  
  for num in numbers:
    counts[num] += 1
  
  # 各数字の個数を調べる
  for i in range(1, len(counts)):
    counts[i] += counts[i-1]
  
  i = len(numbers) - 1
  while i >= 0:
    index = numbers[i]
    result[counts[index]-1] = numbers[i]
    counts[index] -= 1
    i -= 1
  return result
  
# テスト  
if __name__ == "__main__":
  nums = [4,3,6,2,7,1,6]
  print(counting_sort(nums))
```

# 実装
https://www.udemy.com/course/python-algo
matplotlibで図が表示されない場合

matplotlibを使用している際以下のようなエラーメッセージが出る場合があります。

```
UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
```

# 対処法
その場合はmatplotlibをimportするコードの上に`%matplotlib inline`を追加することで解消できます。

```python
%matplotlib inline
import matplotlib.pyplot as plt
```

以上！
VSCodeが更新できなくて困った話

# VSCode更新しようとしたら
メニューバーから`更新の確認`を押すと以下のメッセージが表示され更新ができない

`Cannot update while running on a read-only volume. 
The application is on a read-only volume. 
Please move the application and try again. 
If you're on macOS Sierra or later, you'll need to move the application out of the Downloads directory. 
This might mean the application was put on quarantine by macOS. 
See [this link](https://github.com/microsoft/vscode/issues/7426#issuecomment-425093469) for more information.`

注目して欲しいのはこの1文
**`If you're on macOS Sierra or later, you'll need to move the application out of the Downloads directory.`**
(macOS Sierra以降をお使いの場合は、アプリケーションをダウンロードディレクトリから移動する必要があります)

# ダウンロードディレクトリ内を確認し更新
確かにダウンロードディレクトリ内にVSCodeのアイコンを発見
指示通りにアプリケーションディレクトリに移動させ、更新をかけると正常に更新が掛かった。
GitHub Actionsでgo mod downloadがgo: no modules specifiedで失敗する

# はじめに

モノレポで開発をしている中でGitHub Actionsを利用してGoのテストをするのに時間がかかったのでその原因をまとめます

# 問題

リポジトリを以下の構成で作成しました

```
┣ .github
  ┣ workflows
     ┣ run.yjobs:
  test:
    name: Run Go and Jest tests
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2
      
    - name: Set up Go
      uses: actions/setup-go@v3
      with:
        go-version: 1.20.3 # Replace with your required Go version
version

    - name: cd api
      run: cd ./lt-api && ls

    - name: Download Go modules
      run: go mod downloadml
┣ frot
┣ api
  ┣ go.mod
  ┣ main.go
```

ここでGitHub Actionsで以下を実行しようとすると失敗しました

```yml:run.yml
jobs:
  test:
    name: Run Go and Jest tests
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2
      
    - name: Set up Go
      uses: actions/setup-go@v3
      with:
        go-version: 1.20.3 # Replace with your required Go version

version

    - name: cd api
      run: cd ./api && ls

    - name: Download Go modules
      run: go mod download
```

ここで`go mod download`で以下のエラーになりました

```
Run go mod download
go: no modules specified (see 'go help mod download')
Error: Process completed with exit code 1.
```

`cd api`で移動しているので`go.mod`があるはずなのにおかしいと考えていました

# 解決方法

GitHub Actions内でcdコマンドを使ってディレクトリを変更した場合、その変更はそのステップ内でのみ有効で、次のステップには影響を与えないということが頭から抜けていました

いまの`cd api`はまったく次のステップでは意味がないということでした

以下に修正してうまくいきました

```yml:run.yml
    - name: Download Go modules
      run: |
        cd api
        go mod download
    ```

# おわりに

よくよく考えればそうだなという感じですが、沼ってしまい気づくのにじかんがかかりま
paiza.ioでelixir その225

# 概要
paiza.ioでelixirやってみた。
for、使ってみた。

# サンプルコード

```
arr = for n <- [1, 2, 3, 4], do: n * 2
|> IO.inspect

for n <- [1, 2, 3, 4], do: n * 2
|> IO.inspect


for x <- [1, 2], y <- [2, 3], do: x * y
|> IO.inspect


for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n
|> IO.inspect

[1, 2, 3, 4, 5, 6] 
|> Enum.filter(fn n -> 
    rem(n, 2) == 0 
end) 
|> Enum.map(fn n -> 
    n 
end)
|> IO.inspect

[1, 2, 3, 4, 5, 6] 
|> Enum.filter(&(rem(&1, 2) == 0)) 
|> Enum.map(&(&1))
|> IO.inspect


users = [user: "john", admin: "meg", guest: "barbara"]
for {type, name} when type != :guest <- users do
   String.upcase(name)
end
|> IO.inspect


for x when is_atom(x) <- [1, "hoge", :hoge], do: x
|> IO.inspect


pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
|> IO.inspect
for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
|> IO.inspect


for n <- ["1", "2", "3"], into: "", do: n
|> IO.inspect

Enum.into(["1", "2", "3"], "")
|> IO.inspect

for {key, value} <- [key1: :value1, key2: :value2], into: %{}, do: {key, value}
|> IO.inspect

Enum.into([key1: :value1, key2: :value2], %{})
|> IO.inspect

for x <- [1, 1, 2, 3], uniq: true, do: x * 2
|> IO.inspect

for n <- [1, 2, 3, 4], reduce: 0 do
   acc -> acc + n
end
|> IO.inspect


for n <- [1, 2, 3, 4], rem(n, 2) == 0, reduce: 0 do
   acc -> acc + n
end
|> IO.inspect

for <<x <- "AbCabCABc">>, do: x
|> IO.inspect

for <<x <- "AbCabCABc">>, x in ?a..?z, do: x
|> IO.inspect

for <<x <- "AbCabCABc">>, x in ?a..?z, reduce: %{} do
   acc -> Map.update(acc, <<x>>, 1, & &1 + 1)
end
|> IO.inspect




```

# 実行結果

```
2
4
6
8
2
4
6
8
2
3
4
6
2
4
6
[2, 4, 6]
[2, 4, 6]
["JOHN", "MEG"]
:hoge
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
{213, 45, 132}
{64, 76, 32}
{76, 0, 0}
{234, 32, 15}
"1"
"2"
"3"
"123"
{:key1, :value1}
{:key2, :value2}
%{key1: :value1, key2: :value2}
2
2
4
6
10
6
65
98
67
97
98
67
65
66
99
98
97
98
99
%{"a" => 1, "b" => 2, "c" => 1}
```

# 成果物

https://paiza.io/projects/fKsRrRMGpLHdXCgZe_u_2Q?language=elixir

以上。
Spring Bootアプリケーションの監視および管理

# Spring Boot Actuator
アプリケーションの監視および管理を行う目的で、動作しているアプリケーションの各種情報を取得することができる機能。

Actuatorを使用可能な状態にすると、各種情報にアクセスするためのエンドポイントが公開される。

## エンドポイント
* actuator  
Spring Boot Actuatorによって公開されているすべてのエンドポイントを確認する。
* actuator/health  
正常に起動しているかを確認する。
* actuator/env  
アプリケーションによって使用されているプロファイル　および　環境変数を確認することができる。
* actuator/beans  
アプリケーション内で管理されているBeanの一覧を確認する。
* actuator/mappings  
RequestMappingで指定したパスのリストを確認できる。
* その他エンドポイント  
https://spring.pleiades.io/spring-boot/docs/current/reference/html/actuator.html

### 使用法
spring-boot-starter-actuatorを依存関係を使用すれば基本的に利用可能。

ただし、デフォルトでは/actuator/healthのみしか公開されていないため、application.propertiesにて確認したいエンドポイントを指定する。
~~~ application.properties 
management.endpoints.web.exposure.include=*
~~~

Spring Boot Actuatorでは利用するユーザを制限するために、しばしばSpring Securityも同様に使用する。

Actuatorを参照できるユーザを管理者のみに設定するなど、設定できる。
* @Configrationをクラスに付与すると、@Beanで指定したメソッドのBeanの戻り値が、Springコンテナ上で登録・管理される。  
※Springコンテナで管理されるBeanとして、@Beanメソッドを宣言する場合は、@Configrationをクラスに付与する。
* @EnableWebSecurityをクラスに指定することで、SpringSecurityを使えるようにする。
* SecurityFilterChainを戻り値とするメソッドに@Beanを付与し、Springコンテナに登録する。  
HttpSecurityによって、認証画面　および　許可の条件を指定することができる。
* WebSecurityCustomizerを戻り値とするメソッドに@Beanを付与する。認証を不要とする例外のパスを定義したりすることができる。
* InMemoryUserDetailsManagerを戻り値とするメソッドに@Beanを付与する。Spring Boot Actuatorを使用できるユーザを定義。
~~~ java
@Configuration
@EnableWebSecurity
public class ActuatorSecurity {
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
		http.authorizeHttpRequests(authorize -> authorize
				.requestMatchers("/","/items","/items/**").permitAll() //APIは認証不要
				.requestMatchers("/actuator/**").hasRole("ADMIN") //ActuatorはADMINロールユーザのみ
				.anyRequest().denyAll()
				)
		.formLogin();
		
		return http.build();
	}
	
	@Bean
	public WebSecurityCustomizer webSecurityCustomizer() {
		return (web) -> web.ignoring().requestMatchers("/actuator/health");
	}
	
	//actuatorの認証ユーザ
	@Bean
	public InMemoryUserDetailsManager userDetailsService() {
		UserDetails user = User.withUsername("admin")
				.password("{noop}admin")
				.roles("ADMIN")
				.build();
		return new InMemoryUserDetailsManager(user);
	}
}
~~~

# 参考ハンズオン
https://www.udemy.com/course/spring-bootrest-api/
テーブル定義の基本

# データベースのテーブル定義について

データベースは情報を格納、管理するための重要なツールです。その中心的な概念である「テーブル」は、特定の形式のデータを組織化するのに役立ちます。この記事では、テーブル定義について掘り下げてみます。

## テーブルとは？

テーブルは、データベースの基本的な構造で、行（レコード）と列（フィールド）で構成されます。列は特定のタイプのデータを表し（例えば、名前や住所など）、行はそのデータの特定のインスタンスを表します。

## テーブル定義とは？

テーブル定義は、テーブルの構造を指定するプロセスまたは結果を指します。それは列名、その型、その他の属性（例えば、NULL値を許可するかどうか、デフォルト値など）を含む情報を指定します。

たとえば、次のSQL文は、顧客情報を格納するテーブルを定義します：

```sql
CREATE TABLE Customers (
    ID INT PRIMARY KEY,
    Name VARCHAR(100),
    Email VARCHAR(50),
    Age INT,
    City VARCHAR(50)
);
```
このテーブルには、次のような属性があります：

・ID：一意の顧客ID。これはプライマリーキーとして定義され各顧客を一意に識別します。
・Name：顧客の名前。最大100文字の文字列として定義されています。
・Email：顧客のメールアドレス。最大50文字の文字列として定義されています。
・Age：顧客の年齢。整数として定義されています。
・City：顧客が住んでいる都市。最大50文字の文字列として定義されています。

テーブルを作成したら、その後はINSERT文を使用してデータを追加したり、SELECT文を使用してデータを取得したり、UPDATE文やDELETE文を使用してデータを変更したりできます。

## まとめ
テーブル定義は、データベースのテーブルを作成するための基本的なステップです。それはテーブルの構造を指定し、それぞれの列がどのようなデータを格納するかを定義します。これにより、データの一貫性と整合性が保たれ、データベースが効率的に機能することが可能になります。
golangでメソッドチェーンを実施する方法

### 前置き

現在、Golangで開発を行っているのですが今まで使用してきたオブジェクト指向言語とは違い苦労しています。
今回は、entでメソッドチェーンするのに困ったのでどうすれば良いかを記載してたいと思います！　基本的にやり方は変わらないと思います！

### 環境
- golang 1.19
- ent 0.12.3

### 事前準備

entでは下記のような設定がされているものとします。

``` golang
package schema

import (
	"entgo.io/ent"
	"entgo.io/ent/dialect/entsql"
	"entgo.io/ent/schema/edge"
	"entgo.io/ent/schema/field"
)

// Project holds the schema definition for the Project entity.
type Project struct {
	ent.Schema
}

// Mixin fields related to time.
func (Project) Mixin() []ent.Mixin {
	return []ent.Mixin{
		IDMixin{},
	}
}

// Fields of the Project.
func (Project) Fields() []ent.Field {
	return []ent.Field{
		field.String("name"),
		field.Time("created_at").
			Immutable().
			Default(time.Now),
		field.Time("updated_at").
			Default(time.Now).
			UpdateDefault(time.Now),
		field.Time("deleted_at").
			Optional(),
	}
}
```

### 本題

golangのレシーバーを用いて実装を行います。
レシーバーで指定している構造体のインスタンスを返却する形でメソッドチェーンを実現します！
メソッドチェーンする用のメソッドをまず定義します。
この際に注意することとしては、レシーバーを定義できるのはローカルパッケージのみのため、entの構造体ではなくProjectQueryという構造体を定義して、この中をレシーバーとして定義してあげることで*ent.ProjectQueryを使用します！

また、NewQueryを使用することで、作成を閉じ込めます。

``` 
type ProjectQuery struct {
	query *ent.ProjectQuery
}

func NewQuery() *ProjectQuery {
	return &ProjectQuery{
		query: mysql.GetClient().Project.Query(),
	}
}

func (q *ProjectQuery) WhereId(i string) *ProjectQuery {
	q.query.Where(entProject.IDEQ(i))
	return q
}

func (q *ProjectQuery) WhereNotDeleted() *ProjectQuery {
	q.query.Where(entProject.DeletedAtIsNil())
	return q
}

func (q *ProjectQuery) Only(ctx context.Context) (*ent.Project, error) {
	return q.query.Only(ctx)
}
```

ここまで行えたらあとは呼び出すだけです。

```
	pq := NewQuery()
	p, err := a.WhereId(projectId).WhereNotDeleted().Only(ctx)

```


### 感想

個人的にこの実装を行うことで漸くレシーバになれた気がします。
また、同じメソッドがいろいろな箇所に分散していたので、漸くまとめることができるな。と一安心しました。
golangはまだまだ使い始めたばかりですが、書いているとわかりやすく書けるなと思ってきました。

これからもGoをもっと勉強していきます！
複数のフィルタを噛ましてファイルの一覧を取得

# 概要

一口で言ってしまうと、`DirectoryInfo`の`EnumerateFiles`使うときに`SearchPattern`が指定できますけど、複数のパターンを指定したくなったよー
って話です

これには、大きく分けて二つの方法があります
つまり、先に絞るか・後で絞るか―――

# 先に絞る

先に絞る方は、個々の`SearchPattern`を`EnumerateFiles`に与えて、得られた結果を統合するやり方、ですね

こちらの方式の長所は、`EnumerateFiles(SearchPattern)`を利用するので、プログラムとして分かりやすいとこでしょうかねぇ、いろんな意味でね

短所は、ここの`SearchPattern`が実は謎仕様である点
[こちら](https://tech.blog.aerie.jp/entry/2022/08/13/083935)にある様な謎を秘めているらしいです

総合的には、それらひっくるめて`Windows`の標準的な動作と一致している安心感が得られるんじゃないでしょうか（良し悪しは別にして）、と思ってます

# 後で絞る

後で絞るのは、`SearchPattern`を指定せず全ての結果を受け入れたうえで、複数の`SearchPattern`に合致するファイルだけに着目する方式

こちらの問題点は`SearchPattern`の取り扱いでしょうか
結局、`SearchPattern`を正規表現と見立ててフィルタを自作することになり、色々面倒そう
謎仕様の`SearchPattern`を完全に踏襲するのは更に難しそうです

利点としては、謎仕様ぶん投げて独自仕様の明確な`SearchPattern`を確立できるとこでしょう、裏っ返しですけど

# で、効率的なのはどっち？

という観点では、実装の仕方だけではなく、母集団の状況とフィルタの関連性から、一義的に決まるものではない
と勝手に結論付けました

ではどうするのが一番楽な方法だろうか？
以下ではこの観点で結論を導き出しました

# 結局

「先に絞る」一択です、圧倒的に楽なのは
`SearchPattern`の解釈を`Windows`側に押し付けちゃえますからね

先に絞ってから ⇒ 統合する、って云うのがなんとなくめんどくさそうな響きがありますけど、実はこいつも`Linq`にお任せすれば１行で書けます

それが、こちら！

```C#:C# OneLine snipet
    "*.pdf;*.ppt;*.png".Split(';')
        .SelectMany(filter => new DirectoryInfo("フォルダパス").EnumerateFiles(filter))
```

しまった、２行になってしまった…
<br/>
<br/>
<br/>
いやー、たった２行書くのに引っ張ったなー
生産性爆上IDE「Visual Studio Code」に依存しすぎる３つの危険性

Visual Studio Codeが提供する利便性は、多くの開発者にとって非常に便利であり、プログラミングの生産性を向上させることができます。ただし、以下に挙げるように、過剰に依存してしまうことによって、開発者にいくつかの不利益が生じる可能性があります。

### 1. 依存関係の誤解

Visual Studio Codeによって提供される便利な機能により、開発者は依存関係を簡単に解決することができます。ただし、この機能に頼りすぎると、開発者が依存関係の解決方法を理解することを妨げる可能性があります。そのため、開発者はIDEが提供する機能だけでなく、プログラミングの基本的な概念や原則を理解する必要があります。

### 2. プラットフォームの縛り

Visual Studio Codeはクロスプラットフォームで動作するため、開発者は好きなプラットフォームで開発できます。ただし、開発者がVisual Studio Codeに依存しすぎると、他のIDEやツールを使うことができなくなる可能性があります。そのため、開発者は複数のツールやIDEを使い、柔軟に開発することが重要です。

### 3. カスタマイズの過剰

Visual Studio Codeには多くの拡張機能があります。開発者はこれらの拡張機能を使ってIDEをカスタマイズすることができます。ただし、カスタマイズに時間をかけすぎると、開発者は本来の開発作業から遠ざかってしまう可能性があります。そのため、開発者は必要最低限のカスタマイズに留め、開発に集中することが大切です。

### まとめ

以上のように、Visual Studio Codeに依存しすぎることによって、開発者にいくつかの不利益が生じる可能性があります。開発者は、IDEが提供する機能に依存しながらも、柔軟性を持って開発に取り組むことが重要です。
[無料]Google製BIツール「Looker Studio」の５つの魅力

Looker Studioは、Googleが提供するビジネスインテリジェンスツールの1つで、ビジネス上のデータを分析して可視化するための強力なプラットフォームです。以下に、Looker Studioの魅力をいくつか挙げてみます。

### 1. 柔軟性

Looker Studioは柔軟なデータモデルを提供し、ビジネスの変化に迅速に対応できます。ユーザーは自分たちのビジネスのデータに合わせたデータモデルを構築でき、データの変更に対しても素早く対応できます。

### 2. 可視化の自由度

Looker Studioは、豊富な可視化オプションを提供します。ユーザーは、ドラッグ＆ドロップでダッシュボードを作成し、自分たちのビジネスに必要な情報を簡単に取得できます。

### 3. セキュリティ機能

Looker Studioは、セキュリティ機能に重点を置いています。ビジネス上のデータを扱うため、Looker Studioは、ユーザーアクセスの制御やデータ保護など、重要なセキュリティ機能を提供しています。

### 4. 簡単なデータアクセス

Looker Studioは、さまざまなデータソースにアクセスすることができます。ユーザーは、データベース、API、またはクラウドサービスからデータを取得し、Looker Studioを使用して分析できます。

### 5. モバイル対応

Looker Studioは、モバイルデバイスに最適化されたダッシュボードを提供しています。ユーザーは、どこからでもデータにアクセスし、ビジネス上の情報を確認できます。

### まとめ

以上が、Looker Studioの５つの魅力のです。ビジネスの成長に合わせて、柔軟に対応できるデータモデルや、豊富な可視化オプションを提供することで、ビジネス上の情報をより効果的に活用できるでしょう。
【Filament 2.x】リソースページによって表示するリレーションを変更

## 概要

https://filamentphp.com/docs/2.x/admin/resources/relation-managers

作成したリレーションマネージャーを適応させるには、Resourceクラスに以下のように定義する必要がありますよね。

```php
public static function getRelations(): array
{
    return [
        RelationManagers\TerminalsRelationManager::class,
        RelationManagers\MenusRelationManager::class,
    ];
}
```

そうする事で、ViewページやEditページでリレーションデータを管理出来ます。

が、「Viewページでは`MenusRelationManager`だけ表示したい」時にどうするか

例えばViewページの場合はこうします。

```php
class ViewStore extends ViewRecord
{
    public function getRelationManagers(): array
    {
        return [
            StoreResource\RelationManagers\MenusRelationManager::class
        ];
    }
}
```


editページの場合も同様です。↓の場合リレーションマネージャーが表示されません。

```php
class EditStore extends EditRecord
{
    public function getRelationManagers(): array
    {
        return [];
    }
}
```
三角形の種類の判定

## 問題

三角形の種類を判定する関数`triangle_shape()`を完成させて、正常終了するようにしてください。

```python
from enum import Enum, auto
from itertools import permutations
from math import inf, isinf, isnan, nan


class TriShape(Enum):
    Nontriangular = auto()  # 非三角形
    Equilateral = auto()  # 正三角形
    Isosceles = auto()  # 二等辺三角形
    Scalene = auto()  # 一般の三角形


def triangle_shape(a1, a2, a3) -> TriShape:
    ここを修正
    return TriShape.Scalene


if __name__ == "__main__":
    params = [
        (3, 4, "5", TriShape.Nontriangular),
        (None, 1, 1, TriShape.Nontriangular),
        (nan, 1, 1, TriShape.Nontriangular),
        (inf, 1, 1, TriShape.Nontriangular),
        (inf, inf, 1, TriShape.Nontriangular),
        (-1, 1, 1, TriShape.Nontriangular),
        (0, 0, 0, TriShape.Nontriangular),
        (0, 1, 1, TriShape.Nontriangular),
        (2, 1, 1, TriShape.Nontriangular),
        (1, 2, 3, TriShape.Nontriangular),
        (1, 1, 1, TriShape.Equilateral),
        (1e-2, 1, 1, TriShape.Isosceles),
        (1e-20, 1, 1, TriShape.Isosceles),
        (3, 4, 5, TriShape.Scalene),
    ]
    for param in params:
        expected = param[3]
        for args in set(permutations(param[:3])):
            actual = triangle_shape(*args)
            assert actual == expected, f"{args} {actual}"
```