OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonで書くBuilderパターン

## 概要
- コンストラクタに対して数多くのパラメータをセットする必要がある時に、代わりに使うことが推奨されている実装方法
- メジャーな実装方法
    - GoF
    - Effective Java
- Javaで書かれることが多いので今回はPythonを用いて実装

## Builderパターンなし
```python
#!/usr/bin/env python3
# -*- coding: utf_8 -*-
"""builderパターンなし
"""

class People:
    def __init__(self, name, sex, age):
        self._name = name
        self._sex = sex
        self._age = age

    def __repr__(self):
        return f"People(name = {self._name}, sex = {self._sex}, age = {self._age})"


if __name__ == "__main__":
    people = People("taku ikegami", "男", 26)
    print(repr(people))

```

## GoFのBuilderパターン
```python
#!/usr/bin/env python3
# -*- coding: utf_8 -*-
"""GoFのBuilderパターン
"""

import abc


class Builder(metaclass=abc.ABCMeta):
    """「表現形式」を決定する抽象クラス"""

    @abc.abstractmethod
    def add_name(self, name):
        raise NotImplementedError()

    @abc.abstractmethod
    def add_sex(self, sex):
        raise NotImplementedError()

    @abc.abstractmethod
    def add_age(self, age):
        raise NotImplementedError()

    @abc.abstractmethod
    def get_result(self):
        raise NotImplementedError()


class PeopleBuilder(Builder):
    """「表現形式」を決定するクラス"""

    def __init__(self):
        self._people = People()

    def add_name(self, name):
        self._people.name = name

    def add_sex(self, sex):
        self._people.sex = sex

    def add_age(self, age):
        self._people.age = age

    def get_result(self):
        return self._people


class Director:
    """「作成過程」を決定するクラス"""

    def __init__(self, builder: Builder):
        self._builder = builder

    def construct(self):
        self._builder.add_name("taku ikegami")
        self._builder.add_sex("男")
        self._builder.add_age(26)
        return self._builder.get_result()


class People:
    name: str
    sex: str
    age: int

    def __repr__(self):
        return f"People(name = {self.name}, sex = {self.sex}, age = {self.age})"


if __name__ == "__main__":
    builder = PeopleBuilder()
    director = Director(builder)
    people = director.construct()
    print(repr(people))

```

## Effective JavaのBuilderパターン
```python
#!/usr/bin/env python3
# -*- coding: utf_8 -*-
"""Effective JavaのBuilderパターン
"""


class People:
    def __init__(self, builder):
        self._name = builder._name
        self._sex = builder._sex
        self._age = builder._age

    class Builder:
        def __init__(self, name):
            self._name = name
            self._sex = None
            self._age = None

        def add_sex(self, sex):
            self._sex = sex
            return self

        def add_age(self, age):
            self._age = age
            return self

        def build(self):
            return People(self)

    def __repr__(self):
        return f"People(name = {self._name}, sex = {self._sex}, age = {self._age})"


if __name__ == "__main__":
    people = People.Builder("taku ikegami").add_age(26).add_sex("男").build()
    print(repr(people))

```

## 参考
- [Builder パターン（TECHSCORE）](https://www.techscore.com/tech/DesignPattern/Builder)
- [Builder パターン（Qiita）](https://qiita.com/takutotacos/items/33cfda205ab30a43b0b1)
Macで3本指の画面切り替えが反応しない場合の対処法！Dock・Mission Controlに原因あり！

# 概要
* Macで作業していたら、突然３本指の画面切り替えが反応しなくなってしまいました。Windowsも嫌いではないのでなんだかPC変えた気分になりましたが（笑）、やっぱりMacでこれ使えないのは不便だと感じ、色々調べて解決しました。

## PC環境
* Mac
    * 機種ID：MacBookPro18,3
    * macOS：Monterey
    * チップ：Apple M1 Pro
    * メモリ：16GB

# 原因
* おそらく原因は主に二つの理由があると思います。
    * システム設定で、３本指の画面切り替えがオフになっている
    * Dockが何らかの理由で機能しなくなっている

## システム設定が原因の場合
* 「システム設定」から「ドラッグにトラックパッドを使用」が入っているか確認しましょう。公式サイトは以下になります。
    * [Mac トラックパッドで「3 本指のドラッグ」を有効にする](https://support.apple.com/ja-jp/HT204609)
* 以下、公式からの引用です

> Apple メニュー  >「システム設定」(または「システム環境設定」) の順に選択します。
「アクセシビリティ」をクリックします。
「ポインタコントロール」(または「マウスとトラックパッド」) をクリックします。
「トラックパッドオプション」ボタンをクリックします。
「ドラッグにトラックパッドを使用」(または「ドラッグを有効にする」) をオンにします。
ポップアップメニューからドラッグ方法として「3 本指のドラッグ」を選択します。
「OK」をクリックします。

* ただし、自分の場合はシステム設定はしっかりオンになっていました。ということで、次のDockが原因の場合を模索します。

## Dockが原因の場合
* Macでは、「ウインドウの整理と切り替え」などをMission Controlが行なっています。そしてそのMission Controlの親プロセスがDockといわれるものです。何らかの理由でこのDockがうまく機能していない場合があるらしいので、Dockを再起動します。
* 再起動は、Terminalから行います。Terminalを起動したら、以下コマンドを実行。
    * `killall Dock`
* 自分の場合、これにより３本指の画面切り替えができるようになりました。
AWSで実現した不動産新着物件情報のサーバレス巡回通知システム

# 不動産新着物件情報のサーバレス巡回通知システム

![名称未設定.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1042708/6e32da1e-6d82-777c-646a-f8cb90416f03.png)

[掲載元](https://github.com/PGtanuki/reproject)

AWS各サービスを連携させ、SUUMOの検索結果を巡回して新着物件情報を通知します。
https://suumo.jp/
※requestsを用いたクローリングは直接的に利用規約違反にはなりませんが、負荷をかけることは禁止されています。どうやら新着物件情報は（SUUMOの場合は）1日に何度も頻繁には更新されないので、1時間以上に1回か、朝昼晩くらいの感じにしましょう。AWS無料枠の範囲に抑える観点からもそのほうが適しています。

## 本システムを利用するメリット
+ 不動産サイト各社にはメール通知サービスはあるが、同一物件が通知されない保証はないのと、何社もメールを登録する個人情報的なリスクを回避できる
+ 当然、自前のシステムなので検索条件は実質無制限に設定できる
+ BSのパーサーは変更が必要になるが、SUUMO以外の不動産サイトにも対応可能（AWSのIPからのアクセスをブロックしていなければ&RequestsでHTMLを取得できる形式なら）
+ 引っ越しを検討しているときだけ作動させればいいので無駄がない

## 使ったAWSサービス
+ EventBridge Scheduler
+ Lambda
+ SNS
+ DynamoDB(SUUMOのみならRDSでOK)
  
## EventBridge Scheduler
2022年末にリリースされた新しいSchedulerの機能を用いています。cron記述が不要で、シンプルに「2分ごと」など指定できます。
設定の変更時に威力を発揮すると思います。cronだと普段から書いてないと書き方から調べ直しになりますが（間違いを防止するためにも）、SchedulerでしたらGUIで簡単に間違えようがなく設定できますし、「1回限りの実行」とテストにおあつらえ向きの機能も備わっています。

## Lambda
コードは[lambda.py](https://github.com/IYT-E/reproject/blob/main/lambda.py)を参照。BeautifulSoupなど外部ライブラリはpipインストールのような文明は使えないので、Dockerを使わないでベタ書き実装する場合はレイヤーを追加する必要があります。
しかし、検索で山程出てきた情報ではなぜかうまく行かなかったので、[arnを提供してくださっている方のarn](https://api.klayers.cloud//api/v2/p3.9/layers/latest/ap-northeast-1/html)をレイヤーに追加すれば問題なくいけました。
巡回するURLと、DynamoDBのパーティションキーとなる「PK」は、環境変数で指定する必要があります。

## SNS
メール送信を用いています。

## DynamoDB 
SUUMOのように単一サイトの場合はBeautifulSoupで整えた形が均一なのでRDBで問題ありませんが、学習のためにもNoSQLを採用しました。サイトごとにDBを変更すれば尚更RDBで問題ないと思います。
設計として、パーティションキーには検索条件のタイトルをLambdaの環境変数に入れています。例えば、渋谷区10万円なら['shibuya10']といった具合です。
ほか、パーサーした各種データを放り込んでいます。
ソートキーには家賃を採用しています。
パーティションキーおよびソートキーを考えればあとは何も考えなくていいというのは、NoSQLのメリットかもしれません。たとえば、駅徒歩何分の情報は3駅まで掲載されるので、それがリストになることでリスト型を格納する必要がありますが、NoSQLでしたら考える必要はありません。

## ビジョン
+ 複数のLambdaで分散設計にしつつ、SUUMO以外の不動産サイトもクローリングできるようにしたい
+ DynamoDBに格納されたデータは当然まとめて閲覧できるが、それらを分析できるようにしたい（分析する意味は除いて、学習のためにも）
go mod tidyでgitlabのリポジトリimportが失敗する

# はじめに
本記事はあくまで個人のタスクにおける備忘録としての投稿であり、細かい設定条件や開発環境の記載は省略しています。

# 事象
`go mod tidy`で依存関係を整理しようとした際に
```terminal
fatal: could not read Username for 'https://gitlab.***/***/***.git': terminal prompts disabled
Confirm the import path was entered correctly.
```
というエラーが出て失敗する。

# 調査
1. エラー内容をそのままググる
    するとこういう記事がヒットする
    https://jnst.hateblo.jp/entry/2016/10/17/210612
    
    要はgitのレポジトリへのアクセスをする際に権限がないため怒られているとのこと。

2. 現状の`.gitconfig`を見てみる。
```
[credential]
	helper = !aws codecommit credential-helper $@
	UseHttpPath = true
[core]
	editor = /usr/bin/vim
[url "git@gitlab.***:****"]
	insteadOf = https:/gitlab-ci-token:<gitlabのアクセストークン>@/gitlab.*****
[user]
	name = *********
``` 

＜分かったこと＞
・アクセストークンは発行してあるし記載されている。
・`insteadOf`の記載もある。
・user情報の`name`は記載されているが、アドレスがない...?(なんで?消した)

# 解決方法
`.gitconfig`のuser情報にアドレスを記載する。

```
[credential]
	helper = !aws codecommit credential-helper $@
	UseHttpPath = true
[core]
	editor = /usr/bin/vim
[url "git@gitlab.***:****"]
	insteadOf = https:/gitlab-ci-token:<gitlabのアクセストークン>@/gitlab.*****
[user]
	name = *********
	email = ******@***.co.jp
``` 

できた。


# そういえば
そういえば`git pull`や`git push`する際にユーザー情報聞かれてたなと。。
ただ`.netrc`には必要情報記載してあるし`.gitconfig`にも名前とトークン情報が記載されていた
（`.netrc`にはemail情報不要のため気付くのが遅れた）
Folium Map上のピンに順番を表示する方法

## 目次
[1.はじめに](#1-はじめに)
[2.コードとは](#2-コード)


## 1. はじめに
数字を表示するアイコンと背景のアイコンを組み合わせることでルートの順番を表示する方法

Folium Mapの標準機能ではルート上の順番表示ができませんでした。
そこで、Folium Map上でピンに順番を表示できるようにした方法です。
参考文献は色々漁っていたので紛失しました。

## 2. コード

数字を表示するアイコンの関数
```
def number_div_icon(color: str, number: int) -> DivIcon:
    icon = DivIcon(
        icon_size=(150, 36),
        icon_anchor=(14, 40),
        html="""<span class="fa-stack " style="font-size: 12pt" >>
                    <!-- The icon that will wrap the number -->
                    <span class="fa fa-circle-o fa-stack-2x" style="color : {:s}"></span>
                    <!-- a strong element with the custom content, in this case a number -->
                    <strong class="fa-stack-1x">
                         {:02d}
                    </strong>
                </span>""".format(
            color, number
        ),
    )
    return icon
```

背景を白色で塗りつぶすアイコンと合わせて使用

```
          # 背景を白色で塗りつぶし
            group.add_child(
                folium.Marker(
                    location=(latitude, longitude),
                    icon=folium.Icon(color="white", icon_color="white"),
                )
            )
          # 順番を入れる　先ほどの自作DivIcon
            group.add_child(
                folium.Marker(
                    location=(latitude, longitude),
                    icon=utils.number_div_icon(color="black", number=1),
                )
            )
```

![114528302-cca2e680-9c40-11eb-87e4-f370b18d5557.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/909180/d69671a6-e72d-7ea8-e9c1-cfaaf2ee59a7.png)
Draw.io を基本からまとめてみた【入門】

## Draw.ioとは?

Draw.ioとは『draw.io』にアクセスするだけで、フローチャートやネットワーク図、ER図といった豊富なテンプレートを使用して簡単に絵を描くことができるサービスのこと。
Draw.ioで作成した図はPNGやJPEGなどの画像形式だけでなく、 SVGやXMLといった豊富な拡張子でエクスポートすることが可能。

公式サイト：[draw.io](https://app.diagrams.net/)

## ER図とは
[ER図を基本からまとめてみた【入門】](https://qiita.com/kanfutrooper/items/7ed3f3229a466d641cdb)

## VsCodeで使用する

Github : https://github.com/hediet/vscode-drawio

①Draw.io Integrationで『install』を押すか、VSCodeの拡張機能検索窓で「Draw.io Integration」と検索してインストールをする

②拡張子を.drawioもしくは.dioでファイルを作成する

ファイルを作成して開くとすぐに描画画面になる。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/60d590f6-e0da-67c5-9045-1192518d1d9c.png)

③左側のshapeから必要なものをドラッグ&ドロップするだけで描画できる

また、描画した図にマウスオーバーすると、矢印が出るので、それをクリックしたままホールドし、他のShapeに伸ばすことで、リレーションを書ける。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/bbae707d-fe5c-7d31-a5a8-cc8f24e3bcca.png)

④いろんなサービスのアイコンを追加して使う
Shapesの下にある[+ More Shapes...]を選択して追加でアイコンを選択することができる。AWSやGCP、AzureのアイコンやKubernetesのアイコンを使える。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/b458e4ed-71b4-cf4c-d751-355debc9d897.png)

⑤上記までで作成した図をエクスポートする

上のメニューバーから[File]→[Export as]→（任意の拡張子）の順で選択することで簡単にエクスポートすることが可能。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/514e84ef-3abc-28cd-516e-0f02b29572a0.png)


## 参考サイト
[VSCodeでDraw.ioが使えるようになったらしい！](https://qiita.com/riku-shiru/items/5ab7c5aecdfea323ec4e)
[Draw.ioが VSCodeに対応。機能をご紹介](https://www.youtube.com/watch?v=iL7nNOtl_mI)
Windows標準機能(cmd)で、フォルダ内の各テキストファイル・CSVファイルの行数カウントする。

## はじめに
基幹システムから出力したデータ、フォルダ内に存在するログファイルの行数を手軽に表示できないかと思い調べました。
結果、Windows標準機能（コマンドプロンプト）を使用して各ファイルの件数を取得できました。

## 各ファイルの行数取得
コマンドプロンプトにて ``` find /v /c "" *.csv ``` を入力しますと以下の結果が得られます。(csvをtxtに置き替えるとテキストファイルの件数取得)
```powershell
find /v /c "" *.csv

---------- メーカコード名称マスタ.CSV: 336

---------- 取込データ.CSV: 381

---------- 出力データ.CSV: 623

---------- 商品マスタ.CSV: 400

---------- 在庫マスタ.CSV: 1455

---------- 発注データ.CSV: 809
```

## findとは
基本的には```find ファイル名 ```
フォルダ内に存在しているかどうか検索することができる。

### 引数の説明
/v 指定した文字列を含まない行を検索対象とする
/c 検索対象の行数を表示する


> Microsoft 検索(find)
> > https://learn.microsoft.com/ja-jp/windows-server/administration/windows-commands/find
ノンプロ研 GAS初級講座21期 補講 #1

## 概要
この記事は **ノンプログラマーのためのスキルアップ研究会**
GoogleAppsScript初級講座の **非公式** 補講記事です。

第21期講師のTaitoが執筆しています。
私見が多く含まれる内容になっていますので予めご了承ください。

:::note info
ノンプログラマーのためのスキルアップ研究会 は[こちら](https://plannauts.co.jp/nonproken/)
:::

https://plannauts.co.jp/nonproken/

## 講座のルール
#### ここだけ覚えるポイント
- 質問は正義です。
- 宿題は絶対やりましょう。遅れてもいいです。間違っててもいいです。


## GoogleAppsScript(以下GAS)の基本

#### ここだけ覚えるポイント
- 新規作成はChromeアドレスバーの `sheet.new` や `script.new` が便利
- `function` を単位として実行
- `コンテナバインド`はコンテナ(スプレッドシート等)から共有
    - スクリプトまで見せたい時は`編集権限あり`でリンクを共有
- `スタンドアロン`はスクリプトから共有

スクリプトの共有は、ノンプロ研メンバーにコードを見せたい時、頻繁に使います。
共有方法で躓いてしまう方が多いので覚えておきましょう。

## 変数と定数

#### ここだけ覚えるポイント
- 変数とは`データを入れる名前付きの箱`です
- `const` 宣言が基本
- `let` を使う状況は限定的
- `var` は古い書き方なので使いません

#### let と const
講座ではletを「変数」、constを「定数」と呼びましたが
区別して呼び分ける意味はあまり無く、まとめて「変数」と呼ぶことが多いです。
講座中でもconstでの宣言を「変数」と呼ぶので混乱しないようご注意ください。

:::note warn
学習を始めたばかりの時は「用語の混乱」を起こしがちです。
あまり拘らずに「いろんな呼び方があるんだなぁ」ぐらいの認識でいるのが正解です！
:::

重要なのは `変数はデータを入れる箱である` というイメージです。
この箱にはなんでも入ります。なんでもです！
- 数値
- 文字列
- 日付
- スプレッドシートの表データ
- GoogleドライブのPDFファイル
- インターネットの天気予報
- Slackの新着メッセージ etc

あらゆる`データ`を`箱`に入れて管理していくのが、プログラミングの基本です。
考え方に慣れてゆきましょう。

##### ちょっと発展

変数は`名前付きの箱`ですが、名前のつけ方は重要です。
`x`や`y`のような抽象的な名前をつけてしまうと、
何のデータが入っているのか？一見して分からなくなってしまいます。
中身がわかりやすい名前をつけましょう。

変数名は`キャメルケース`での記述を推奨しています。
（ex. sheetName, taxRate... 小文字で始まり、単語毎に大文字になります）

`定数`であることを強調したい場合
（ただの変数宣言ではなく、上書きすべきでないデータであるとアピールしたい場合）
`アッパースネークケース`などのその他の記法を使うこともあります。

```javascript
const sheetName = "フォームの回答 1"; // キャメルケース
const SHEET_ID = "xxxxxxxxx"; // アッパースネークケース
```

変数名はどのように記述しても挙動に影響しません。
なので、いろんな書き方をする人がいます。
人間の目で見た時に分かりやすい書き方になっていることが重要です。
社内にコーディングガイドラインがある場合は、それに沿って書いていきましょう。

また、**宣言**と**初期化**を分けて説明しましたが
本講座では**宣言のみ**のコードはほとんど書きません。
特にconstの場合は**宣言のみ**が出来ないので、こちらもご注意ください。

```javascript
let lastName = "田中"; // 宣言と初期化を同時におこなう

let firstName;
firstName = "太郎"; // 2行に分けて書くことはあんまりない

const age; // 宣言のみはエラー
```

##### もっと発展
変数と定数の違いを詳しく説明しようとすると

* データを箱に入れるとは実際には何をやってるの？
    * メモリの話へ
* letとvarはどっちも変数だけどなにが違うの？
    * スコープの話へ
* constで宣言したのに中身が上書きされているんだけど？
    * オブジェクトの参照の話へ

どんどん難しい話になっていきます。
それでもやってやるぜ！という方は、以下の記事からご覧になってみてください。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

## データ型と演算

#### ここだけ覚えるポイント
- データにはいくつかの`型`(種類)がある
- 同じ種類のデータ同士は`演算`(足し算や連結など)ができる
- `${テンプレート文字列}`は使えるととても便利です

`データ型`によって`リテラル`(書き方)が変わります。
文字列は
- " ダブルクォート
- ' シングルクォート
- ` バッククォート（テンプレート文字列用）

どれで囲んでもOKです。
例えば Hello, I'm Taito. のような文章は、
'で囲むと I'm が区切りとみなされてしまいます。
状況によって使い分けましょう。

##### ちょっと発展
`データ型`と言われると聞いたことのない概念のように感じますが
スプレッドシートの`表示形式`と似たようなものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412979/91715179-be10-46d6-3f26-3a381a54d3a7.png)
スクリプトエディタだと表示形式は選べないので
"文字列"や [配列] のように、囲い文字で種類を指定してあげる必要があるのですね。

* * *

次回更新は1/24(火)の予定です。
引き続きよろしくお願いします！

### 参考書籍
https://www.amazon.co.jp/dp/4798064742

* * *
### (余談) AIにも聴いてみましょう

https://openai.com/blog/chatgpt/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2412979/ee2aeb5a-1b6b-f266-7da7-ab47a2706e18.png)

……間違ってませんね……。
AIは間違ったことを言う場合もあるので、すぐに信じてはいけません。
来週は間違えてくれるといいなー。
CodeSandboxでGitHub連携後にconsoleが出てこない問題について

Reactなどで高速で開発環境を作成できるCodeSandboxと言うツール（サイト）があります。
こちらのサイトでGitHubと連携するとUIが変わり、consoleなどの位置がわからなくなります。

before
![スクリーンショット 2023-01-17 21.19.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1008681/91cfe7c8-badf-e12e-bc02-0942793adb96.png)


after
![スクリーンショット 2023-01-17 21.18.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1008681/8dc45b01-f476-ce5f-da76-1129a7091b6e.png)

このUI変更をもとに戻す方法を探すために「codesandbox UI変わる」などで調べても記事が出てこなかったので原因はわからないので、もしご存知の方がいれば教えていただけますと幸いです。

ともあれ、このままではどこを探してもconsoleを見つけることができなかったのでReactの学習をしようにもデバックが捗らず難航します。
そのため、暫定の解決方法を見つけたので共有します。

### 結論
画像の右側のレンダリングされているブラウザのさらに右上の「・・・」の左のマークをクリックします。

![スクリーンショット 2023-01-17 21.25.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1008681/286795da-a39a-c97b-771e-b910311e095a.png)

すると新たなタブでこのHTMLが表示されます。
あとはそのタブで右クリックして「検証」を押すとconsoleをみることができます。

![スクリーンショット 2023-01-17 21.30.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1008681/82b91ee2-bc13-51f4-5d98-c16faeebee1b.png)

## 後書き
もし根本原因をご存知の方がいらっしゃれば教えていただけると嬉しいです！
リモートGPUサーバーに立てたDocker上で、jupyter labを動かしてローカルからアクセスする方法（JAX用）


# 前提

パスワードを入れなくてもsshのみで入れるようにする。 → [参考](https://qiita.com/ir-yk/items/af8550fea92b5c5f7fca)

# Dockerfileの用意
リモートサーバーの中に作業フォルダを作成してDockerfileを入れる

```python
FROM nvcr.io/nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04

ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update
RUN apt-get -y install vim
RUN apt-get -y install software-properties-common
RUN add-apt-repository ppa:deadsnakes/ppa
RUN apt-get install -y python3.9 python3.9-venv
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 100
RUN update-alternatives --config python3
RUN apt-get install -y python3-pip

RUN pip install --upgrade numpy scipy
RUN pip install --upgrade "jax[cuda]" -f https://storage.googleapis.com/jax-releases/jax_releases.html
RUN pip install jupyterlab matplotlib
RUN pip install tqdm optax

WORKDIR /work
CMD jupyter-lab --no-browser --port={好きなport番号(8888がデフォルト)} --ip=0.0.0.0 --allow-root --NotebookApp.token=''
```

# Docker起動

作業フォルダ配下に`/work` フォルダを作成

 `作業フォルダ/work`  はdocker 内にマウントされる。使いたいnotebook等はここに置く。


```python
# docker のイメージを作成（一番最初だけで良い）
docker build -t {任意のイメージ名} .  

# イメージからdockerを起動 --gpus 2 で使用するGPUの数を指定
nvidia-docker run --name {任意のコンテナ名} -p {指定したport番号}:{指定したport番号} -v {作業フォルダの絶対パス}/work:/work  --gpus 2 {任意のイメージ名}

# 使用するGPUを指定する場合
nvidia-docker run --name {任意のコンテナ名} -p {指定したport番号}:{指定したport番号} -v {作業フォルダの絶対パス}/work:/work  --gpus '"device=0,1"' {任意のイメージ名}
```



# ローカルからアクセスしてGPUの接続テスト

ローカルからsshでjupyterにつなぐ。

```python
ssh -L {指定したport番号}:localhost:{指定したport番号}  {ユーザー名}@{IPアドレス}
```


ブラウザからjupyter labに接続

```python
localhost:{指定したport番号}
```
これでjupyter labの画面が出てきたら成功


# GPUが使えているかのテスト
適当なipynbファイルを開いて以下を実行。 `gpu` と出てくればGPUに接続できている。

```python
from jax.lib import xla_bridge
print(xla_bridge.get_backend().platform)
```


# GPUでJAXを使う場合の注意点
複数のノートブックで同時にGPU上でjitをしようとするとエラーになる。

複数のノートブックを使う場合は、以下のコマンドで別々のGPUを割り当てる。

```
import os
# dockerから２つのGPUが見えているのであれば、0 or 1を指定することで、切り替わる。
# 0と1の両方をこのnotebookで使いたいならば、 '0,1'に設定する。
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
```
草莽Erlang ── 28. lists:sort

口で言うより行うことがErlang習得への近道と信じています。

https://qiita.com/mnishiguchi/items/9237780e849d80a85e11

## 異なる型の比較

Erlangではどんな型の組み合わせでも大小の比較判定が可能です。

```
number < atom < reference < function < port < pid < tuple < map < list < bitstring
```

https://www.erlang.org/doc/reference_manual/expressions.html#term-comparisons

## lists:sort/1

```
sort([term]) -> [term]
sort(リスト) -> ソートされたリスト
```

リストをソートして新しいリストを返します。どんなリストに対しても実行可能です。

https://www.erlang.org/doc/man/lists.html#sort-1

```erlang
> lists:sort([2, 1, 4, 3]).
[1,2,3,4]

> lists:sort([2.0, 1.0, 4.0, 3.0]).
[1.0,2.0,3.0,4.0]

> lists:sort([[5], #{x => 4.0}, <<6>>, {3}, two, 1]).
[1,two,{3},#{x => 4.0},[5],<<6>>]
```

結果から重複する要素を取り除きたい場合は、代わりに[lists:usort/1](https://www.erlang.org/doc/man/lists.html#usort-1)を使います。

```erlang
> lists:usort([2, 1, 4, 3, 1, 2]).
[1,2,3,4]
```

## lists:sort/2

```
sort(fun((A::term, B::term) -> boolean), [term]) -> [term]
sort(オレオレ大小比較関数, リスト) -> ソートされたリスト
```

オレオレ大小比較ルールでリストのソートができます。どんなリストに対しても実行可能です。オレオレ大小比較関数は、AとBとを比較して小さいか等しい場合に`true`を、それ以外の場合に`false`を返すようにします。

https://www.erlang.org/doc/man/lists.html#sort-2

```erlang
% 降順でソート
> lists:sort(fun (A, B) -> B =< A end, [2, 1, 4, 3, 2, 1]).
[4,3,2,2,1,1]

% '闘魂'アトムが必ず前に来るような並び順
> ToukonFirst = fun ('闘魂', _) -> true; (_, '闘魂') -> false; (A, B) -> A =< B end.
#Fun<erl_eval.41.3316493>

> lists:sort(ToukonFirst, [2, 1, '闘魂', 3, '闘魂']).
['闘魂','闘魂',1,2,3]
```

結果から重複する要素を取り除きたい場合は、代わりに[lists:usort/2](https://www.erlang.org/doc/man/lists.html#usort-2)を使います。

```erlang
> lists:usort(fun (A, B) -> B =< A end, [2, 1, 4, 3, 2, 1]).
[4,3,2,1]
```

## lists:keysort/2

```erlang
keysort(integer, [tuple]) -> [tuple]
keysort(タプルの要素の位置, タプルのリスト) -> ソートされたタプルのリスト
```

タプルのリストをソートして新しいリストを返します。 ソートはタプルの指定された位置の要素に対して行われます。

https://www.erlang.org/doc/man/lists.html#keysort-2

```erlang
> L1 = [{"B", 3}, {"C", 1}, {"A", 2}, {"A", 2}].

% １番目の要素でソート
> lists:keysort(1, L1).
[{"A",2},{"A",2},{"B",3},{"C",1}]

% 2番目の要素でソート
> lists:keysort(2, L1).
[{"C",1},{"A",2},{"A",2},{"B",3}]
```

結果から重複する要素を取り除きたい場合は、代わりに[lists:ukeysort/2](https://www.erlang.org/doc/man/lists.html#ukeysort-2)を使います。

```erlang
> lists:ukeysort(1, L1).
[{"A",2},{"B",3},{"C",1}]

> lists:ukeysort(2, L1).
[{"C",1},{"A",2},{"B",3}]
```

[listsモジュール](https://www.erlang.org/doc/man/lists.html)には他にもリスト処理のための関数がたくさんあります。

## Elixirにも挑戦したい

闘魂ElixirシリーズとElixir Schoolがオススメです。

https://qiita.com/torifukukaiou/items/3b65e5c04fa8c55f526e

https://elixirschool.com/ja

https://qiita.com/piacerex/items/09876caa1e17169ec5e1
pymysql.err.programmingerror inf can not be used with mysql 対策

## pymysql.err.programmingerror inf can not be used with mysql というエラーがでた場合

例えばデータフレームのカラムの pct_change() などで予想外の値が生じて
MySQLのテーブルに格納できない場合など、、、

```
df = df.replace([np.inf, -np.inf], np.nan)
```

正負の inf を replace
[Blender]1つのオブジェクトに複数のアニメーションを登録する手順

# 概要
Blenderで作ったオブジェクトに複数のアニメーションを登録する手順です。
Unityへのエクスポートまでやっていきます。
# 本文
## 前準備
画面上部のタブで「Animation」を選択
![スクリーンショット 2023-01-17 9.07.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/a930be6a-5c89-23c8-a3bf-c7a3832402aa.png)
画面下側のスクショ部分が「DopeSheet」になっていることを確認してください。
![スクリーンショット 2023-01-17 8.49.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/cdf1667f-e14d-baad-5a0f-5627943a0f37.png)
その右隣を「Dope Sheet」から「Action Editor」に切り替えます。
![スクリーンショット 2023-01-17 8.50.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/1234cb6e-0d38-8a11-df76-d901e12565e9.png)
## Animationを作成
「+ New」を押すとAnimationを用意できます。一つ目のAnimationを作成します。
![スクリーンショット 2023-01-17 9.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/1aeba2ea-6fb8-5d25-29eb-84dd4127d8ac.png)
## Animationの追加
Animationを追加するときは「New Action」を押します。
![スクリーンショット 2023-01-17 8.52.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/468ef1b9-2149-83bc-97a8-792aa862d28a.png)
## Animationの切り替え
スクショのボタンを押すことでアニメーションを切り替えられます。また、その右隣の文字入力欄を変更することでアニメーションの名前を変更することもできます。
![スクリーンショット 2023-01-17 8.54.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/9d32d851-c95c-5324-0c76-ffc782c1f9c1.png)
## Unityへのエクスポート
以上のように作成してエクスポートすると、Unityへ導入した時オブジェクトに複数のアニメーションが登録されている事を確認できます。
![スクリーンショット 2023-01-17 9.00.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/7c44a856-9e38-4fbf-2877-bd6626857c00.png)

# 補足
エクスポートの一例も載せておきます。それぞれの目的に合わせて調整してください。
(File > Export > FBX)
![スクリーンショット 2023-01-17 21.19.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/b0945829-0046-7934-e74a-6a8865e7e63e.png)
Node.js でタイムスタンプリクエストを作成する

Node.jsの標準ライブラリにはタイムスタンプリクエストを生成するライブラリは存在しないので、今回は[jsrsasign](https://kjur.github.io/jsrsasign/)を使用してタイムスタンプリクエストを生成します。

## jsrsasign のダウンロード

NPMでダウンロードします。

```
> npm install jsrsasign jsrsasign-util
```


## サンプルプログラム
[KJUR.asn1.tsp.TimeStampReq](https://kjur.github.io/jsrsasign/api/symbols/KJUR.asn1.tsp.TimeStampReq.html), [KJUR.asn1.tsp.MessageImprint](https://kjur.github.io/jsrsasign/api/symbols/KJUR.asn1.tsp.MessageImprint.html) を使用して、タイムスタンプリクエストを作成します。

```javascript
const {createHash, } = require('node:crypto');
const {KJUR, ASN1HEX} = require('jsrsasign');


//ハッシュ値の生成
const hash = createHash('sha256');
hash.update('Hello World');

//MessageImprintの作成
var messageImprint = new KJUR.asn1.tsp.MessageImprint({
    alg: 'sha256',
    hash: hash.digest('hex')
});

//TimeStampReqの作成
var timestampReq = new KJUR.asn1.tsp.TimeStampReq({
    messageImprint: messageImprint,
    policy: null,
    nonce: null,
    certreq: true
});

//ファイルへ出力
const buf_arr = Uint8Array.from(Buffer.from(timestampReq.tohex(), "hex"));
const fs = require('fs');
fs.writeFile('HelloWorld.req', buf_arr, err => {
    if (err) throw err;
});

//TimeStampReqを解析した内容を表示
console.log(ASN1HEX.dump(timestampReq));
```

## 実行結果

プログラムを実行した結果です。

```
>node HelloWorld_TimestampReq.js
SEQUENCE
  INTEGER 01
  SEQUENCE
    SEQUENCE
      ObjectIdentifier sha256 (2 16 840 1 101 3 4 2 1)
      NULL
    OCTETSTRING a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
  BOOLEAN TRUE
```
dbfluteとDOMA2の特徴とSQLの書き方の比較

## 初めに

以前、O/Rマッパーの選定でdbfluteとDOMA2の比較調査をする機会があったのでメモ程度に。
公開してみたものの、あくまでも自分用メモの位置づけなので人に読まれる想定はしていないため、わかりづらい点があるかもしれませんがご容赦ください。

## SQLの比較

### 実行したいSQL

```sql
SELECT
  hoge.id,
  , hoge.name
  , hoge.age
  , fuga.height
  , fuga.weight
FROM
  hoge
INNER JOIN
  fuga
  ON hoge.id = fuga.id
WHERE
  hoge.id = 1234
```

### dbfluteでの書き方

```kotlin
    hogeBhv.selectEntity { // Bhv＝From句
      it.specify().columnId() // SELECT句
      it.specify().columnName() // SELECT句
      it.specify().columnAge() // SELECT句
      it.setupSelect_Fuga() // ここでINNER JOIN
      it.specify().specifyFuga().columnHeight() // SELECT句（結合先のテーブルは書き方が冗長になる）
      it.specify().specifyFuga().columnWeight() // SELECT句（結合先のテーブルは書き方が冗長になる）
      it.query().setId_Equal(1234) // WHERE句の条件指定
    }
```

### DOMA2(DSLスタイル)での書き方

```kotlin
     // 自動生成されるクラス
    val hoge = Hoge_()
    val fuga = Fuga_()

    // 以下、dbfluteと違ってDOMA2初見でも大体わかると思う。
    // コメントをつけるまでもない。
    entityql
      .from(hoge)
      .innerJoin(fuga) {
        eq(hoge.id, fuga.id)
      }.where {
        eq(hoge.id, 1234)
      }.select(
        hoge.id,
        hoge.name,
        hoge.age,
        fuga.height,
        fuga.weight
      ).fetch()
```

## dbfluteの特徴

### メリット

* Entityを実際のDBを基に自動生成できる。
  * 外部キー制約を基にEntity同士の関連付けも自動で行われる。
  * 故にDBの変更に強い。
* タイプセーフなクエリビルドができる。
* 外だしSQLという機能を使えばSQLを直接書くこともできる。

### デメリット

* 初見だとdbfluteの記法がわかりづらく、発行したいSQLがよくわからない。（前述）
* マイナー故に参考になる記事がほぼなく、使い方もクセ強めなので学習コスト高め。
* 複雑なSQLが表現できない。
  * GROUP BYしてCOUNTするとか
  * SELECT句で文字列結合するとか
  * CASE文やIF文を使えない
* Entityクラス以外にもテーブルごとにBehaviorとかいろいろクラスが生成されるので、クラスの数がかなり増える。

複雑なSQLは書けないが、その代わりにシンプルなSQLを強制することで可読性が上がるというメリットも。
たぶん、**そもそもSQLで複雑なことすんなよ**という作者の意図が含まれているんじゃないかと思う。<br>


## DOMA2の特徴

### メリット

* DSLスタイルを利用すればタイプセーフなクエリビルドができる。
  * DOMA2公式がDSLスタイルを推奨している。
* DOMA2の記法を知らなくても発行したいSQLが直観的にわかる。（前述）
  * dbfluteよりも圧倒的に見やすい。
  * 故に可読性高め。
* dbfluteよりもSQLの自由度が高い。
* Entityを実際のDBを基に自動生成できる。
* DAOスタイルを利用すればdbfluteで言う外だしSQLと同じことができる。
* Entityの項目にValueObjectを利用できる。（DDD信者が好きなやつ）


### デメリット

* Entityの自動生成をした際に、dbfluteと違ってEntity同士の関連付けまではしてくれない。
  * Entity同士の関連付けは実装者が手動でやらないといけないのでひと手間かかる。
* DSLスタイルの記事が少なく、学習コストがほんのり高め。（dbfluteほどではない）

## どっちを選ぶのか

以前比較調査をしたときは、DOMA2の方が良さそうな気がしたけど、以下の要因により消去法でdbfluteを採用しました。

* GradleでDAOスタイルに必要な設定を入れたつもりが何かエラーになってDAOスタイルが使えなかった（原因不明のまま）
* SpringBootを使用していたが、やり方の問題かDao、Entityql、NativeSqlのDIが良い感じにできなかった。（原因不明のまま）
* 上記2つの原因調査に時間がかかるくらいならdbfluteで良いんじゃないかという意見が出た。

皆様はぜひDOMA2を使ってください。そんで感想を教えてください。
paizaラーニング レベルアップ問題集 スタック・キューメニュー応用編 JavaScript warp

warp (paizaランク B 相当)

https://paiza.jp/works/mondai/stack_queue_advanced/stack_queue_advanced__warp

>下記の問題をプログラミングしてみよう！
paiza 君は 1 〜 N の番号がつけられた N マスを移動します。
移動のルールは次の通りです。
・i 回目の移動をマス x からおこなう場合、その移動先はマス to[x][i] となる。
to[x][i] が -1 の場合、現在のマスに移動してくる 1 つ前のマスが次の移動先となる。
paiza 君はマス 1 から移動を始めます。
マス 1 から K 回移動をしたとき、paiza 君が止まったマスの番号を止まった順に出力してください。



# 解答例(C++の場合を参考)
難易度が高くなっていますが、題意を正しく理解するのが難しいところに起因している気がします。問題文に以下のように書かれています。
>to[x][i] が -1 の場合、現在のマスに移動してくる 1 つ前のマスが次の移動先となる。

この書き方ですと、移動先(to[x][i])が-1のとき、スタックの現在のマスはそのままに、移動先の１つ前のマスを新たにスタックに加えると解釈できます。

具体例を挙げれば、
訪れたマスを保持しておくスタックが[12, 15, 24]で、移動先-1の時、
現在のマスは24で、
１つ前のマスは15ですから、
`スタックは[12, 15, 24, 15]`、
移動後の現在のマスは15です。

さらに移動先が-1で、１つ前のマスに移動するとなった時、
現在のマスは15、
１つ前のマスは24ですから、
`スタックは[12, 15, 24, 15, 24]`、
移動後の現在のマスは24とです。

しかし、題意は違うようです。

模範解答のC++のコード例を見ると、
`S.pop();//スタックの末尾(現在のマス)削除`
 `now = S.top();//スタックの末尾(１つ前のマス)を取得`
なので、スタックの現在のマスを削除し、移動先の１つ前のマスは加えない、となっています。
これは、"スタックの現在のマスを取り消して、1つ前のマスに戻る"ということです。

同じように、具体例を挙げれば、
訪れたマスを保持しておくスタックが[12, 15, 24]で移動先-1の時、
現在のマスは24で、
１つ前のマスは15ですから、
"現在のマスを取り消して、1つ前のマスに戻る"ので、
`スタックは[12, 15]`、(←ここが違う)
移動後の現在のマスは15、
とするようです。

さらに移動先が-1で、１つ前のマスに移動するとなった時、
現在のマスは15、
１つ前のマスは12ですから、(←ここが違う)
"現在のマスを取り消して、1つ前のマスに戻る"ので、
`スタックは[12]`、(←ここが違う)
移動後の現在のマスは12、(←ここが違う)
とするようです。

以上のように、2回続けて移動先が-1で１つ前のマスに移動するとなったときに、問題点が浮かび上がります。

１つ前のマスに移動するというのは、"新しい"１つ前のマスに移動することなのか、履歴の１つ前のマスに"戻る"ことなのか。
現在のマスの履歴を残して、"新しい"1つ前のマスに移動して、これも履歴を残すのか、
それとも、現在のマスの履歴を消して、元の１つ前のマスに移動して、"戻る"のか、
これがハッキリしません。

模範解答から推察するに、題意は、履歴の１つ前のマスに戻ることですので、現在のマスの履歴をスタックから削除することになります。

問題文からここまで読み取るのは難易度が高いなと感じました。

C++の模範解答をJavaScrptにしたものを載せておきます。
```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//マスの個数 N と移動する回数 K
const [N, K] = lines[0].split(" ").map(Number);
//移動元、現在のマス
let now = 0;
//移動先マスを表す二次元配列to、インデックスなので-1する
const to = lines.slice(1).map(line => line.split(" ").map(v => v = Number(v) - 1));

//移動先を記録するスタック
let S = [0];//初めのマスは1

//i回目の移動
for (let i = 0; i < K; i++) { //toのインデックスなので-1して0からK未満まで
  if (to[now][i] === -2) {//移動先が-1なら
    S.pop();//末尾(現在地)を取り出し
    now = S[S.length - 1]; //一つ前に移動    
  } else {
    now = to[now][i];//移動
    S.push(now);//移動先を記録
  }
  //プレイヤーが止まるマスを順に改行区切りで出力
  console.log(now + 1);
}
```

# 題意に沿わなかった解答例
移動先が-1のとき、新しい１つ前のマスに移動すると解釈して、現在のマスの履歴を残して、解答しました。
```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const [N, K] = lines[0].split(" ").map(Number);
//移動先マスを表す二次元配列to
const to = lines.slice(1).map(line => line.split(" ").map(Number));
//移動先記録
let S = [1];//初めのマスは1
//i回目の移動
for (let i = 1; i <= K; i++) {
  const x = S[i - 1];//現在のマス
  if (to[x - 1][i - 1] === -1) { //移動先が-1の時
    ans.push(S[i - 2]);//"新しい"現在のマスに移動してくる 1 つ前のマス
  //現在のマスの履歴は削除しない
  } else {
    ans.push(to[x - 1][i - 1]);//移動先記録
  }
}

//プレイヤーが止まるマスを順に改行区切りで出力
ans.shift();//初めの位置は取り除く
console.log(S.join("\n"));
```
MissingPluginException(No implementation found for method Firebase#initializeCore on channel plugins.flutter.io/firebase_core)

多くの記事で同じようなエラーの解決方法が載っていますが,自身の場合には解決方法が違ったのでメモしておきます。


`Flutter run`をしたときに毎回エラーは起きないのですが、実機の画面が真っ白になってました.

<h2>環境</h2>
OS: M1 mac<br>
Flutter version: 2.10.0

<h1>解決方法</h1>

firebase_coreのversionが`1.19.0`にしていると、エラーが出ていたみたいです。
そのため、yamlファイルにて、firebase_coreのversionを下げることで解決しました。

```dart:pubspec.yaml
firebase_core: ^1.12.0
````

しかし、同じ開発チームの他の方はversionが`1.19.0`のままでも実装できていました。
深い原因の解明はわかりませんが、versionが高いことで上記のエラーが出ている可能性もあることを覚えておきましょう
Symfony FormBuilderでlabelを消したい

Symfony初心者です。
twigを使っていますが、通常、twig上でphpの関数が使えず、viewでロジックを書かせないという強い意図を感じます。

さて、FormBuilderでlabelを消そうとして、調べても情報が見つかりませんでした。
古い情報も多いですね。

ソースの中を見てもわからなかったのですが、試行錯誤した結果、labelをfalseにするとラベルを消すことができました。

これに限らず、symfonyでよいドキュメントがあったらコメント欄で教えてください。
公式に載ってないこともあり、調べるのに時間がかかってしまいますね。

#### 前提
Symfony 5.4

```XxxxxxxxController.php
   #[Route('/xxxxx', name: 'xxxxx')]
    public function index(Request $request): Response
    {
         $form = $this->createFormBuilder()
             ->add('input', NumberType::class, [
                 'label' => false
             ])
             ->add('save', SubmitType::class, ['label' => 'submit'])
             ->getForm();

        return $this->render('xxxxxxxx/index.html.twig', [
            'form' => $form->createView(),
        ]);
    }
```
数学_02

演習上のn点から２つ選んで対角線となる確率と辺となる確率が一緒となるnはいくつ？の問題。

ChatGPTに聞いてみた。

だけど、、、答えに行き着くには道のり長い。
数学はあまり得意でない感じかな。

---

![Screenshot from 2023-01-17 20-19-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213343/714cd90a-7103-2a7c-7930-3e7878b00cbc.png)
AI画像生成(Stable Diffusionなど)を行うDiscordのBotを追加するサイトをBlazorで作成したお話

Zennにも同様の記事を投稿しています。
https://zenn.dev/kawa0x0a/articles/d2a1ca23be053a

# TL;DR
  * WebサイトをBlazor Serverで作った。
  * AIで画像生成を行うDiscordのBotを作った。
  * 作成したWebサイト : https://stablediffusiondiscordbot.azurewebsites.net/
  * リポジトリ : https://github.com/kawa0x0A/StableDiffusionDiscordBot

# Blazorで作ったWebサイト
  * Blazor Server製
  * ホスティングはAzureで決済サービスはStripeを使用
  * 勉強になったこと : ログインの認証回りの処理の書き方, Webサイトのデザイン, Bootstrapの使い方
  * 感想 : C#でWebサイト作れるの最高。あとはVisual Studioの補完が無いと生きていくことができないことがわかった
  * 課題 : なんかサイトの初回の読み込みにちょっと時間がかかる (原因が謎)

# DiscordのBot
  * Python製
  * 作り方はたぶん一般的なBotの作り方と同じ
  * 使用できるAIモデルはStable Diffusion, Stable Diffusion 2, Waifu Diffusion, Anythingモデルに対応
  * 個人的にC#以外のプログラミング言語がそんなに好きじゃないのでコードなどは割愛
  * 課題 : Botを動かすサーバーにGradient Paperspaceを使っているため時々Botが動作しないタイミングがある (GCPとかも検討したけど24時間動かし続けるとやっぱりお値段が高い……)

# 技術メモ (挫折したこと一覧)
  * 最初はBlazor WebAssemblyで作っていたが途中からBlazor Serverに変更
    * 理由 : StripeのAPIキーのなどの秘匿したい情報が秘匿にできないためBlazor WebAssemblyからBlazor Serverに変更
Ubuntuでapt installすると「E: パッケージ　が見つかりません」

# 経緯  
Linuxが使いたく、USBブート(Ubuntu 20.04)できる環境を作った。  
個人的に必要だったバイナリをapt installしようとすると何故か見つからない。  
「E: パッケージ　が見つかりません」のエラー。


# 原因  
見ているリポジトリの範囲が狭いから。  

# やったこと
```
$ sudo apt-add-repository universe
```

また、必要なバイナリ名のtypoでも本事象が起きうる。

# 参考
https://www.web-dev-qa-db-ja.com/ja/apt/aptでパッケージをインストールしようとしているときに「パッケージが見つかりません」/961584046/amp/
https://sqlazure.jp/r/ubuntu/857/
svelte classを動的につける

# 結論

```html
<div class:クラス名={条件}></div>
```

# 例

クリックされた文字に`selected`クラスをつけて文字を赤くする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2529362/9528bd11-d8ed-e826-2d57-eb80b0ddde05.png)

```html
<script>
	let current = 'apple';
</script>

<div>
	<ul>
		<li class:selected={current === 'apple'} on:click={() => (current = 'apple')}>
			りんご
		</li>
		<li class:selected={current === 'banana'} on:click={() => (current = 'banana')}>
			バナナ
		</li>
		<li class:selected={current === 'grape'} on:click={() => (current = 'grape')}>
			ぶどう
		</li>
	</ul>
</div>

<style>
	.selected {
		color: red;
	}
</style>
```

# その他

上記の例では、**あるクラスをつけるかつけないか**しか制御できない。  
条件により違うクラスをつける場合は、

```html
<div>
	<ul>
		<li class:selected={current === 'apple'} on:click={() => (current = 'apple')}>
			りんご
		</li>
		<li class:selected={current === 'banana'} on:click={() => (current = 'banana')}>
			バナナ
		</li>
		<li class:selected={current === 'grape'} on:click={() => (current = 'grape')}>
			ぶどう
		</li>
	</ul>
</div>
```
firebaseコマンドでcommand not foundが表示される時の恒久的な解決方法

## はじめに
Qiita初めての投稿です。
解決できた事象があり、Qiitaの過去記事に同様の記事が見つからなかったので状況と解決方法を書き留めておこうと思います。

## 課題
ターミナルにおいて、firebaseインストール済みにも関わらず
```
$ firebase --version
$ firebase deploy
$ firebase login
```
など、firebaseコマンドを実行した際に`zsh: command not found: firebase`が返ってきて実行できませんでした。
firebaseコマンド（npm）のPATHが通っていないことが原因です。
[こちらの記事](URL "https://qiita.com/ebichan_88/items/e3e30461ad4ddd9368f5")を参考に、
```
$ export PATH=$PATH:`npm bin -g`
```
をコマンド入力するとPATHが通り、firebaseコマンドを使用できるようになりますが、ターミナルを再起動するとまた同様の現象が発生します。
対症療法として都度上記コマンドを入力していたのですが、いい加減恒久的な解決を図ろうと思い本記事の執筆に至ります。

## 環境
macOS
.zsh



## 発生理由と解決方法
npmのPATHの設定がうまくいっていないことが原因で上記の現象が発生しています。
従って、.zshrcファイルにPATHを設定することで恒久的に解決することができます。
<br>


:::note info
そもそもPATHとは、「ファイルやフォルダの場所を示す情報」です。
PATHを通すということは、コマンド操作において「ここを探す」と事前に登録しておくことを指します。（.zshrcファイルに登録します。）
上記の場合はfirebaseコマンドがPATHに通されていないため、`command not found`という結果が返ってきます。
:::

## 実際の操作
ホームディレクトリから操作します。
まずは前提条件の確認から行います。

```
$ firebase --version
```
firebaseはインストール済みですが、`zsh: command not found: firebase`が返ってきます。


<br>
<br>

以下コマンドでfirebase（npm）のPATHが通っていないことが確認できます。

```
$ npm bin -g
```
`/Users/${username}/.npm-global/bin`
`npm ERR! bin (not in PATH env variable)`
ここでnpm binファイルの位置がわかります。

<br>
<br>

PATHを通すために.zshrcファイル（PATHを通すファイル）を開きます。

```
$ open .zshrc
```

<br>
<br>


.zshrcファイルの末尾に`export PATH=$PATH:(npm binファイルの位置)`を付け足します。



```
$ export PATH=$PATH:/Users/${username}/.npm-global/bin
```  
  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2991958/5d119da8-ed6f-f02c-4fbc-ddcd4f5d75e6.png)

<br>
<br>
.zshrcファイルが存在しないときは、

```
$ touch .zshcr
```
上記のコマンドで.zshrcファイルを作成し、同様に行います。

<br>
<br>

.zshrcファイルを保存してターミナルを再起動することで、ホームディレクトリよりfirebaseコマンドが使えるようになります！
firebaseコマンドに限らず、PATHが通らないときはこの手法を取ってみてください。


## 参考文献
https://qiita.com/ebichan_88/items/e3e30461ad4ddd9368f5
https://wa3.i-3-i.info/word18471.html
https://daeudaeu.com/mac-path/
幅優先探索のメモ

[参考サイト](https://qiita.com/drken/items/996d80bcae64649a6580)

メモ
1. 色をつけた番号に紐づく番号をまずキューに入れる。
0が1,4,2、1が3,8に紐づいているのなら、先に1,4,2に色をつけた後、1と紐づいている3,8に色をつける。 

練習サイト
[アルゴ式、グラフ]( https://algo-method.com/courses/19)

[ABC284 C](https://atcoder.jp/contests/abc284/tasks/abc284_c)
ランダムで表示するリマインダーを作ってみる

自分の直したい事を思い出したいと思うことありますよね？
紙に書いておこうとも思いましたが、家族に見られたくないのでターミナルを起動した時に表示させたいと思いました。

## まず、Elixirで表示部分を作ってみよう


```elixir:ymn_remind.exs
#! /usr/bin/env elixir
defmodule Main do
  def memo do
    """
    文章1行目
    文章2行目
    文章3行目
    文章4行目
    """
  end

  def main do
    memo()
    |> String.split("\n", trim: true)
    |> Enum.shuffle()
    |> Enum.at(0)
    |> IO.puts()
  end
end

Main.main()
```

def memo の内容を表示したい文章に書き換えて下さい。
ランダムで１行選択されて表示します。

## fish起動時に表示させる
ymn_remind.exsを置いてある場所はパスが通っていることが前提です
 
```sh:~/.config/fish/config.fish
ymn_remind.exs
```

実行結果
![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/71f74914-1a4f-d2fa-7a65-bf336a5e895a.png)
プリザンターのサーバースクリプトで分類項目に複数名登録されている際のメールの自動送信方法を知りたい

 # はじめに
すでに分類Aと分類Bには各1名のユーザーが設定されており、そのユーザーあてにメールは送信可能。しかし、分類Cに複数人設定でき、その複数人にもメールを自動送信したい

 # ソースコード
```javascript

try {
    //ユーザーIDを格納する配列を作成
    var address1 = new Array();
    //変数address1にClassAに設定されているユーザーIDを格納
    address1.push('[User' + String(model.ClassA) + ']');
    //変数address1にClassBに設定されているユーザーIDを格納
    address1.push('[User' + String(model.ClassB) + ']');
    //ClassCにJSON文字列として設定されている複数のユーザーIDをJavaScriptオブジェクトとして格納
    const userIdList = JSON.parse(model.ClassC);
    //値に変換されたuserIdListを順にuserIdに格納
    for (const userId of userIdList) {
        //変数address1にClassCに設定されているそれぞれのユーザーIDを格納
        address1.push(`[User${userId}]`);
    }
    //notifibationsをインスタンス化
    var notification1 = notifications.New();
    //notifications内で定義されている変数AddressにユーザーIDを格納
    notification1.Address = Array.from(new Set(address1)).join();
    //notifications内で定義されている変数Titleに件名を格納
    notification1.Title = '件名';
    //notifications内で定義されている変数Bodyに内容を格納
    notification1.Body = '内容';
    //メールを送信
    notification1.Send();
} catch (e) {
    context.Log(e.stack);
}


```

 # 工夫
分類Cに複数人設定されている場合、配列でユーザーIDが設定されますので、そのユーザーIDをひとつづつAddressに設定している。最も、難しかったです。また、重複を削除するため、Setオブジェクトを使用しているます。
VBScriptで共有フォルダへNET USEで接続方法

# 共有フォルダへNET USEで接続方法

共有フォルダへのアクセスをバッチ処理などのプログラムで実行させることで、作業ミスの減少と作業効率化を図ることができる、共有フォルダへNET USEで接続方法です。
（VBScriptからDOSコマンドを実行する一連の処理）

```vb
Sub Main()

'変数宣言の強制
Option Explicit

Dim objShell
Dim dstTargetHost,dstUserName,dstPassword

dstTargetHost = "\\共有フォルダのIP"
dstUserName = "ユーザー名"
dstPassword = "パスワード"

'共有フォルダへNET USEで接続します。
Set objShell = CreateObject("WScript.Shell")
objShell.Run "net use " & dstTargetHost & " " & dstPassword & " /user:" & dstUserName,0,true

objShell.Run "cmd /c " & ×××.exe & " /install /quiet " , 0, true
'👆例：cmdで.exe のインストール処理です。必要に応じて、実行したい処理内容を記述してください。

'共有フォルダへのNET USEの接続を切断します
objShell.Run "net use " & dstTargetHost & " /DELETE",0,true

'生成したオブジェクトを破棄します。
Set objShell = Nothing

End Sub
```
プリザンターのサーバースクリプトを使用し、日付を加算して別の項目に転記する

 # はじめに
日付Cに日付を入力すると日付Dに180日後の日付を自動で入力したい。

 # ソースコード
```javascript

//DateC項目が変更されたときの関数
$p.on('change', 'DateC', function () {
    //DateC項目に入力された値を取得・格納
    let dateC = new Date($p.getControl('DateC').val());
    //取得されたDateC項目の値に180日を加算
    dateC = dateC.setDate(dateC.getDate() + 180);
    //DateD項目に180日加算したDateC項目の値を設定
    $p.set($p.getControl('DateD'), $p.shortDateString(new Date(dateC)));
})

```

 # 工夫
180日を加算するところは少し苦労しました。また、慣れないサーバースクリプトでの実装でしたので、その点も今までとのギャップを感じ、大変でした。ちなみに、今回は日付に180日加算したが下記ソースコードのように月でも加算することができる。

```javascript

$p.on('change', 'DateC', function () {
    let dateC = new Date($p.getControl('DateC').val());
    dateC = dateC.setMonth(dateC.getMonth() + 7);
    $p.set($p.getControl('DateD'), $p.shortDateString(new Date(dateC)));
})

```
もともとのソースコードの「取得されたDateC項目の値に180日を加算」した行のコードを修正ました。具体的には下記参照
```javascript

dateC = dateC.setDate(dateC.getDate() + 180);
//↓
dateC = dateC.setMonth(dateC.getMonth() + 7);

```

set~メソッドとget~メソッドを修正すれば正常に動作する。以下の記事を参照。
https://www.tohoho-web.com/js/date.htm
センサー値に応じてリレースイッチに繋がる機器をON/OFFする

以前投稿した水位センサーとリレースイッチの記事の内容を組み合わせて、「水位センサーの値に応じてAC100V機器をON/OFFさせる」という仕組みを作りました。
今回は水槽の換水作業を想定して、「一定の水位に達したらポンプを止めて水の供給を止める」「水位が足りなくなったらポンプを稼働して水を供給する」という仕組みを実現しています。

https://qiita.com/s_fujii/items/45f4eab31df13367a57c

https://qiita.com/s_fujii/items/d9ef99b5ab6a3f0f1206

# 環境
- MacOS Monterey 12.6.2
- Arduino IDE 1.8.20

# 材料
- Arduino （今回は[KeyStudioのLeonard R3開発ボード](https://www.amazon.co.jp/gp/product/B0786LJQ8K/ref=ppx_yo_dt_b_asin_title_o00_s01?ie=UTF8&psc=1)を使用しました）
- [リレースイッチ](https://www.amazon.co.jp/gp/product/B08QML2PTZ/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1)
- [フロートスイッチ（水位センサー）](https://www.amazon.co.jp/gp/product/B01NAMST3R/ref=ox_sc_act_title_1?smid=A3NIKCTAX8545C&psc=1)
- 半固定抵抗10kΩ
- ジャンパーワイヤ
- ブレッドボード
- 分解してもよい延長タップ
- 100V入力機器

# 事前準備
[リレースイッチ記事の『事前準備』](https://qiita.com/s_fujii/items/d9ef99b5ab6a3f0f1206#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99)を参考にして、延長タップのケーブルの一部を裁断し、皮膜を剥いて導線を剥き出しにします。

# 配線
下記画像を参考に配線してください。
![スクリーンショット 2023-03-29 1.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/a5304ad9-c819-c205-d771-6f8498c9b5cc.png)

# コード
<details><summary>コード</summary>

```c:waterlevel_and_relayswitch.ino
#include <stdio.h>
#include <time.h>

#define SIGNAL_PIN  13
#define ANALOG_PIN  A0
#define FILL  0     // 水位が足りている場合のアナログ値

int water_level = 0;
bool relay_state = false;

void setup() {
  Serial.begin(9600);
  pinMode(SIGNAL_PIN, OUTPUT);
  pinMode(ANALOG_PIN, INPUT);

}

void loop() {
  
    water_level = analogRead(ANALOG_PIN);
    Serial.print("water_level_value :");
    Serial.println(water_level);

    if(water_level != FILL && relay_state == false){  // 水位が足りないかつリレースイッチがOFFの時
      relay_on();   // リレースイッチをONにして機器を動かす
    }else if(water_level == FILL && relay_state == true){ // 水位が足りているかつリレースイッチがONの時
      relay_off();  // リレースイッチをOFFにして機器を止める
    }else{  // それ以外の時は何もしない
    }

    delay(500UL);
  
}

void relay_on() {
  digitalWrite(SIGNAL_PIN, HIGH);
  relay_state = true;
  Serial.println("relay_on");
  delay(1 * 1000UL);
}

void relay_off(){
  digitalWrite(SIGNAL_PIN, LOW);
  relay_state = false;
  Serial.println("relay_off");
  // 動作停止時間10秒を最低限確保(水位境目での動作不良防止)
  delay(10 * 1000UL);
}

```

</details>

# 結果
実際に動作させた時の様子がこちらです。
水位現象時のアナログ出力値はセンサーによって個体差があります。
![スクリーンショット 2023-01-17 17.49.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/f70571ae-9be3-8bc6-715a-0ff730067b0e.png)
センサー値の変化を検知して、リレースイッチの状態が変化していることが分かります。
今回は水槽の換水作業を想定して水位センサーを使用しましたが、センサーの種類を変えれば、様々な分野に応用が効きそうです。
Nuxt3とtailwindcssでナビゲーションメニューを作った

# 目的
Nuxt3がリリースされたので遊んでみる。ページを追加してナビゲーションとヘッダーを作ります。

# インストール

[本家のインストール方法](https://nuxt.com/docs/getting-started/installation)

```
npx nuxi init frontend
cd frontend
yarn install
yarn run dev
```

http://localhost:3000/

# ページ

```vue:app.vue
<template>
  <div>
    <NuxtPage/>
  </div>
</template>
```

```vue:pages/index.vue
<template>
    <div>トップ</div>
</template>
```

```vue:pages/about.vue
<template>
    <div>アバウト</div>
</template>
```

http://localhost:3000/

http://localhost:3000/about

# tailwindcss

[本家のインストール方法](https://tailwindcss.com/docs/guides/nuxtjs#3)

```
yarn add --dev tailwindcss postcss autoprefixer
npx tailwindcss init
```

```assets/css/main.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```

```typescript:nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  css: ['~/assets/css/main.css'],
  postcss: {
    plugins: {
      tailwindcss: {},
      autoprefixer: {},
    },
  },
})
```

```javascript:tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./components/**/*.{js,vue,ts}",
    "./layouts/**/*.vue",
    "./pages/**/*.vue",
    "./plugins/**/*.{js,ts}",
    "./nuxt.config.{js,ts}",
    "./app.vue",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

```vue:pages/index.vue
<template>
    <div class="text-3xl font-bold underline">トップ</div>
</template>
```

```
yarn run dev
```

http://localhost:3000/

# ヘッダーとナビゲーションを構築

```vue:components/Header.vue
<template>
  <div class="h-10 bg-green-900 text-yellow-500 flex items-center pl-3">
    <div>Nuxt3サンプル</div>
  </div>
</template>
```

```vue:components/Navigation.vue
<template>
  <div class="bg-gray-500 p-3 text-white flex flex-col items-center">
    <div><nuxt-link to="/">トップ</nuxt-link></div>
    <div><nuxt-link to="/about">アバウト</nuxt-link></div>
  </div>
</template>
```

```vue:app.vue
<template>
  <div class="flex flex-col h-screen">
    <Header />
    <div class="flex flex-grow">
      <Navigation />
      <NuxtPage/>
    </div>
  </div>
</template>

```

http://localhost:3000/

# まとめ
Nuxtの作法に従うと簡単に複数ページやナビゲーションを付けることができました。ちょっとしたWebを作るのに気軽に使っていきたいです。
【DBチューニング】その設計と実装、整合性とれてますか？3分かかるクエリを5秒に。

そのまま載せるわけにもいかないので置き換えて記載していきますが、運営サイドで、ユーザーの記事を検索する機能のある画面にて検索ボタンを押したらくるくるまわったまま他の画面にも遷移できないのでなんとかしてほしい、というような依頼があった。

この案件では監視ツールとして[datadog](https://www.datadoghq.com/ja/?utm_source=advertisement&utm_medium=search&utm_campaign=dg-google-japan-apac-brand&utm_keyword=%2Bdatadog&utm_matchtype=b&utm_campaignid=8700484236&utm_adgroupid=84283271981&gclid=Cj0KCQiAq5meBhCyARIsAJrtdr6x0rbM7CrdyT9TDSzUWzbMW5qbgUMp9s21xrw0j34MtEyFSjomxr8aAilkEALw_wcB)を使っているので、まずは検索ボタンを押下したときの処理時間の割合調査から入っていき調査したところ３minかかっており、その割合はPDOが100%であることが判明した。

https://www.datadoghq.com/ja/?utm_source=advertisement&utm_medium=search&utm_campaign=dg-google-japan-apac-brand&utm_keyword=%2Bdatadog&utm_matchtype=b&utm_campaignid=8700484236&utm_adgroupid=84283271981&gclid=Cj0KCQiAq5meBhCyARIsAJrtdr6x0rbM7CrdyT9TDSzUWzbMW5qbgUMp9s21xrw0j34MtEyFSjomxr8aAilkEALw_wcB

発行SQLは以下のようなものとなる。

```sql
SELECT count(id) AS count 
FROM posts
WHERE status = "publish"
    AND user_id IN (1) 
ORDER BY created_at DESC
```

ローカル環境でもSTG環境でも再現できなかったので、システム構成が異なるかデータ量によるものと推測。
いづれにしてもpostsには1億レコード以上があり、これをローカルに持ってくるのは非常に手間。
とはいえ、本番環境で検索ボタンを押下したときに処理が中断されるときもあるので調査にならない。

SQLの処理時間が長くて　結果的にサーバーが処理継続できる上限時間を超えているせいかERR_CONNECTION_CLOSEDのエラーがでるが、根本はPDOが原因のはずなのでredashで本番環境に対して上記SQLを実行してみる。

相変わらず３minかかっている。
犯人はもうこいつで確定なので3minかかっているSQLをなんとかして早くする方法をググりまくる。

ヒントを得たのは以下の記事。

https://qiita.com/mtanabe/items/47509943c9cb08c8e269

まさかとは思いながらもposts.user_idの型を見ると、なんと！
varcharであった！！！

システム開発において一般的にほにゃららIDはint型であることが多いが、なにか理由があってvarcharにしたのでしょう。
以下にint型の値をstring型に変えて実行してみるとあら不思議、3minかかってたSQL処理が5secで完了！

```sql
SELECT count(id) AS count 
FROM posts
WHERE status = "publish"
    AND user_id IN ('1') 
ORDER BY created_at DESC
```

EXPLAIN.typeもALLになっていたのが、refに改善されました。
設計と実装しはセットなのでこういうところも意識して開発を進めていかないと、プロダクトが小さいうちは気付きませんが、大きくなっていくにつれて発覚してくることも多くあります。

参考までに、全体のレコード数は1億6000万件程度で発行SQLで取得したレコードは110万件程度でした。

使っているFWはsymfony3なのですが、laravelだとモデルにキャスト定義できてselect時に自動的に型変換してくれるので、アプリ側で別のモデルに条件設定するときにキャストするとかしないとか意識しなくていいわけです。
分かりやすさに努めているのでレイテンシーとかプロセスとかそんな言葉は使いません。
paiza.ioでelixir その177

# 概要
paiza.ioでelixirやってみた。
aiffファイルを解析してみた。

# 参考にしたページ

https://qiita.com/naoya@github/items/1fb72638989e50bbfad2


# サンプルコード

```

:inets.start
:ssl.start

defmodule CommonChunk do
	defstruct [:id, :size, :num_channels, :num_sample_frames, :sample_size, :sample_rate, ]
	def new(id, size,
		<<
			num_channels :: unsigned-integer-size(16),
			num_sample_frames :: unsigned-integer-size(32),
			sample_size :: unsigned-integer-size(16),
			_x :: unsigned-integer-size(16),
			sample_rate :: unsigned-integer-size(16),
			_y :: binary
		>>) do
		%CommonChunk{
			id: id,
			size: size,
			num_channels: num_channels,
			num_sample_frames: num_sample_frames,
			sample_size: sample_size,
			sample_rate: sample_rate,
		}
	end
end
defmodule SoundDataChunk do
	defstruct [:id, :size, :offset, :block_size, :sound_data, ]
	def new(
		<<
			id :: bitstring-size(32),
			size :: unsigned-integer-size(32),
			offset :: unsigned-integer-size(32),
			block_size :: unsigned-integer-size(32),
			sound_data :: binary
		>>) do
		%SoundDataChunk{
			id: id,
			size: size,
			offset: offset,
			block_size: block_size,
			sound_data: sound_data,
		}
	end
end
defmodule FormChunk do
	defstruct [:id, :size, :type, :common, :sound_data]
	def new(
		<< 
			id :: bitstring-size(32), 
			size :: unsigned-integer-size(32),
			type :: bitstring-size(32),
			common_id :: unsigned-integer-size(32), 
			common_chunk_size :: unsigned-integer-size(32),
			common_data :: binary-size(common_chunk_size),
			sound_data_chunk :: binary
		>>) do
		%FormChunk{
			id: id,
			size: size,
			type: type,
			common: CommonChunk.new(common_id, common_chunk_size, common_data),
			sound_data: SoundDataChunk.new(sound_data_chunk)
		}
	end
end
defmodule Main do
	def main() do
		{:ok, {_status, _headers, body}} = :httpc.request('https://resolvenet.org/modules/contrib/webform/tests/files/sample.aiff')
		body = IO.iodata_to_binary(body)
	    IO.inspect FormChunk.new body
	end
end
Main.main


```

# 実行結果

```
%FormChunk{
  common: %CommonChunk{
    id: 1129270605,
    num_channels: 1,
    num_sample_frames: 37478,
    sample_rate: 44100,
    sample_size: 16,
    size: 18
  },
  id: "FORM",
  size: 75002,
  sound_data: %SoundDataChunk{
    block_size: 0,
    id: "SSND",
    offset: 0,
    size: 74964,
    sound_data: <<255, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 255, 0, 255, 0, 255,
      0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0,
      0, ...>>
  },
  type: "AIFF"
}
```

# 成果物

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


以上。