OmniAICreator/Qiita-1.07M huggingface dataset:
【Laravel】エラーレスポンスの統一

## 環境
Laravel v9.5.1 (PHP v8.1.3)

## エラーレスポンスの統一
LaravelのデフォルトのままだとバリデーションエラーとHTTPエラーでエラーレスポンスの形が異なっているため統一する。
エラーメッセージは下記の形で返るようにしたい。

```
{
  "message" => "Forbidden"
}
```

## ①Handlerに登録
HandlerにバリデーションエラーとHTTPエラーそれぞれのエラーレスポンスの処理を登録。
```app/Exceptions/Handler.php
...
public function register()
{
    $this->renderable(function (HttpException $e, $request) {
      $message = $e->getMessage();

      switch ($e->getStatusCode()) {
        case 401:
          $message = __('Unauthorized');
          break;
        case 403:
          $message = __('Forbidden');
          break;
        case 404:
          $message = __('Not Found');
          break;
        case 500:
          $message = __('Internal Server Error');
          break;
        default:
          return;
      }

      $errorMessage = [
        'message' => $message,
      ];

      return (new ErrorResource($errorMessage))->response()->setStatusCode($e->getStatusCode());
    });

    $this->renderable(function (ValidationException $e, $request) {
      if ($request->is('api/*')) {
        $message = $e->getMessage();

        if ($e->errors()) {
          $message = Arr::flatten($e->errors())[0];
        };
        $errorMessage = [
          'message' => $message,
        ];

        return (new ErrorResource($errorMessage))->response()->setStatusCode($e->status);
      };
    });
}
```
## ②ErrorResourceを作成
他のAPIをAPIリソースを使ってJsonで返していたため、そこも統一すべく```ErrorResource```を作成。
ほとんどファイル作成時のままでいいが、```public static $wrap = null;```でデータラッパーをなしにすることで、やりたいメッセージの形にする。

```php
class ErrorResource extends JsonResource
{
  public function toArray($request)
  {
    return parent::toArray($request);
  }

  public static $wrap = null;
}
```

## ③アプリ独自のエラーにもErrorResourceをあてる

```php
class NotSettingProfileException extends Exception
{
  public function __construct()
  {
    parent::__construct('プロフィールを設定してください', 400);
  }

  public function render($request)
  {
    $errorMessage = [
      'message' => 'プロフィールを設定してください'
    ];

    return (new ErrorResource($errorMessage))->response()->setStatusCode(400);
  }
}
```

## 参考

https://zenn.dev/blancpanda/articles/laravel-api-exception-renderer
RaspberryPiOSが動くDockerコンテナにJenkinsをインストールする

# やりたいこと

DockerコンテナにRaspberryPiOSを入れて、Jenkinsをインストールする。
最終邸には、RaspberryPi4の上で動くDockerコンテナにRaspberryPiPicoの開発環境を準備したい。


# 手順

### 1.Dockerコンテナを作る

https://qiita.com/ryot0/items/56a296b33111408595c6


### 2.DockerコンテナにアタッチしてJavaとJenkinsをインストールする

https://qiita.com/ryot0/items/70295d32186938c9f69a


### 3.Dockerコンテナにアタッチしてjenkinsユーザ／グループのuid/gidを変更する & ホームディレクトリを変更する

```shell
sudo groupmod -g 2000 jenkins
sudo usermod -u 2000 jenkins
sudo usermod jenkins -d /data/app/root/jenkins
```


### 4.Dockerコンテナをイメージに固める

```shell
docker stop picobuild01
docker commit picobuild01 raspios_picobuild:0.0.2
```

### 5.ホスト側にjenkinsユーザを作る

```shell
sudo groupadd -g 2000 jenkins
sudo useradd -u 2000 -s /usr/sbin/nologin -d /data/app/main/pico/root/jenkins -g jenkins jenkins
```

ポイントは、コンテナ側のgid/uidに合わせて作ること。


### 6.ホスト側でjenkins用のディレクトリを作る

```shell
mkdir /data/app/main/pico/root/jenkins
sudo chown jenkins /data/app/main/pico/root/jenkins
sudo chgrp jenkins /data/app/main/pico/root/jenkins
```


### 7.Dockerコンテナの作成

```shell
docker create -t --name picobuildcont01 \
    -v /data/app/main/pico:/data/app \
    -v /data/share/main:/data/share \
    -p 8080:8080 \
    -e JAVA_HOME=/usr/lib/jvm/java-11-openjdk-arm64 \
    -e LANG=C.UTF-8 \
    -e TZ=Asia/Tokyo \
    -e JENKINS_HOME=/data/app/root/jenkins \
    -e JAVA_OPTS="-Djava.awt.headless=true -Duser.timezone=Asia/Tokyo -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Tokyo" \
    --user jenkins \
    --net net01 \
    raspios_picobuild:0.0.2 /usr/bin/jenkins
docker start picobuildcont01
```


### 8.接続確認

ブラウザから http://IPアドレス:8080/ にアクセスしてJenkinsの起動を確認する。

Administrator passwordを聞かれるので、docker logsでログに流れているパスワードを確認して入力する。

```shell
docker logs picobuildcont01
```


### 9.再起動ポリシーを設定する

https://qiita.com/ryot0/items/41f1cc2907b375298328
【#29 エンジニア転職学習】DB入門　パフォーマンス

# はじめに
富山県に住んでいるChikaといいます。
毎日投稿を目標に、バックエンドエンジニア転職に向けた学習内容をアウトプットします。

DBの入門をざっくり学習していきます。

バックエンドエンジニアになるまでの学習内容は以前投稿した以下の記事を基にしています。


https://qiita.com/Chika110/items/ef54dddd565a0193ef44

# 本日の学習内容
本日はDBのパフォーマンスに関して学習しました。

* バックアップとリカバリ
* **パフォーマンス概要　←Topics!!**

# パフォーマンス概要
システム全体で見た時に、DBがボトルネックになることが多い。
下記がその2つの要因
1. 扱うデータ量が多く、**ストレージのリソース**がボトルネックになる
1. そのストレージ自体が**スケールアウト困難なコンポーネント**である

リソース不足対策として、**チューニング**＝SQLをどのように早くするか効率化を図る がある。


使用している教材はこちら↓


https://www.amazon.co.jp/%E3%81%8A%E3%81%86%E3%81%A1%E3%81%A7%E5%AD%A6%E3%81%B9%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%81%8D%E3%81%BB%E3%82%93-%E3%83%9F%E3%83%83%E3%82%AF-ebook/dp/B00TTRZFDC/ref=tmm_kin_swatch_0?_encoding=UTF8&qid=1657112653&sr=8-1


# おわりに
最後までお読みいただきありがとうございました。
アドバイス・応援コメント等いただけますと幸いです。
if __name__ != ( '__main__' ) : だったらどう動く？

## 「おまじない」って言われる `if __name__ == ('__main__'):` をちゃんと理解したい

### おさえたいポイント
そもそも、`__name__` と `__main__` ってなに？

#### `__name__`とは：
> クラス、関数、メソッド、デスクリプタ、ジェネレータインスタンスの名前が入るための特殊な**変数**
> モジュールやパッケージがインポートされると、__name__にはモジュールの名前がセットされる


**■インタプリタで実行**
``` 標準モジュール.py
>>> import subprocess
>>> subprocess.__name__
'subprocess'
```
:::note info
**実行フローと結果**
+ import文でモジュールを読み込む
+ _module(ここではsubprocess)_.__name__で特殊属性の__name__を表示する
+ 'subprocess'としてモジュール名が表示される
:::
  
**■ファイルで実行**
```
directory/
| -- my_function.py
| -- test.py
```

``` my_function.py
def show_name():
    print(__name__)
```
``` test.py
import my_function

my_function.show_name()
```
```
$ python test.py
my_function
my_function
```

:::note info
**実行フローと結果**
+ **my_function.py** : show_name()という関数を定義し、__name__をprint文で表示する
+ **test.py** : my_function.pyをモジュールとしてimport文で読み込みshow_name()を実行する
+ ターミナルで **test.py** を実行する
+ ファイルがモジュールとして読み込まれると、拡張子 **.py** を除くファイル名がモジュール名としてセットされる。
:::

:::note warn
**注意点**
+ my_fanction x 2 の状態で結果が返ってきている。
:::

#### あっ、そういうことか❗️
**"基本的な動作として、モジュールは何かしらの方法で制御しないと、importした時点で関数が実行されてしまう。だから、この「おまじない(`if __name__ == '__main__':`)」の文が必要ってこと。** 


#### `__main__`とは：
> トップレベルコードが実行される環境の名前です。"トップレベルコード "とは、最初に実行を開始するユーザー指定のPythonコードのことです。そのコードは必要とする他のすべてのモジュールをインポートするので「トップレベル」と定義されている。 "トップレベルコード "はアプリケーションへのエントリポイントとも呼ばれる。

※**要はターミナルで直接スクリプトを実行したのがこの状態。**

**■　おまじない"有り無し"スクリプトを実行してみる**
``` my_function.py
def show_name():
    print(__name__)

# おまじない有り
if __name__ == ('__main__'):
    show_name()
```
```
$ python my_function.py
$ __main__
```

``` my_function.py
def show_name():
    print(__name__)

# おまじない無し
```
```
$ python my_function.py
$ 
```

:::note warn
**実行フロートと結果**
+ **おまじない有り** : 一番の上位のモジュールとして**my_function.py**が実行されているので、トップレベルの環境名__main__が変数__name__に代入され、if文で**True**と判別される。 次にshow_name()メソッドが実行され、print文で変数__name__つまり、__main__が返ってきている。
+ **おまじない無し** : show_name()メソッドが呼び出されていないので、当然なにも表示されない。

:::

#### 本題に戻って、結局 `if __name__ != ( '__main__' )` にしたらどうなるのか？

``` my_function.py
def show_name():
    print(__name__)


if __name__ != ('__main__'):
    show_name()
```
■ファイルを直接実行
```
$ python my_function.py
$ 
```
■モジュールとして読み込んで実行
```
$ python test.py
my_function
my_function
```

:::note alert
**実行フロートと結果**
+ スクリプトファイルを直接実行した場合、左辺も右辺も値は同じ__main__で同じなので、!=の判定結果でFalseとなりshow_name()は実行されず、結果はなにも表示されない。
+ モジュールとして読み込んだ場合は、逆に読み込まれた時点で1回実行され、次にshow_name()でもう一度実行され、結果my_functionが2つ表示されることになる。 
:::

### 結論
+ このおまじないは、**"スクリプトの実行を制御する文"** ということですね❗️
Zoom Meeting SDKでなんちゃってボイスチェンジャー


# はじめに
ZoomがSDKを提供しはじめたということで盛り上がりを見せている昨今ですが、Qiitaでも盛り上げイベントが開催されています。SDKでいろいろ遊ばせてもらえる上に、アイテムもゲットできるかも、ということで参加するしかないですね。

ということで、これを機にいろいろ実験してみたのですが、面白そうなことがたくさんできそうだったので、いくつか連投する予定です。ご興味を持たれましたら、後続の投稿もご覧ください。

第１回の記事はこちら⇒[Zoom Meeting SDKでタイムキーパーちゃんを作る](https://qiita.com/wok/items/205c086f19a7ff73718d)
第２回の記事はこちら⇒[Zoom Meeting SDKでアバターにいろいろしゃべらせる](https://qiita.com/wok/items/0450c8620f11a371bd8b)
第３回の記事はこちら⇒[Zoom Meeting SDKをつかって会議にアバターで参加する](https://qiita.com/wok/items/1bccd567e844ac4e8979)
第４回の記事はこちら⇒[Zoom Meeting SDKでなんちゃってボイスチェンジャー](https://qiita.com/wok/items/08c9505d5c3c95d8956d)　**←本記事**
番外編の記事はこちら⇒[Zoom Meetingにアバターで参加するぞ。番外編](https://qiita.com/wok/items/4f51e1a72d735b75f73f)
第５回の記事はこちら⇒[Zoom Meeting SDKとVosk browserでZoom会議のリアルタイム文字起こし](https://qiita.com/wok/items/e83c49c530354a7b8b42)


前回は、Zoom で使えるアバターにユーザの動作を同期させてみました。今回はユーザの会話をボイスチェンジャー的に別の声に変換して伝えてみたいと思います。しかし、リアルタイムに声を綺麗に変換するのは難しく、機械音っぽくなってしまいます。今回は、ちょっとタイムラグがありますが、音声認識をしてからText-To-Speechでしゃべらせるという方向で考えてみたいと思います。なんちゃってボイスチェンジャーですね。

こんな感じに動きます。

<iframe width="560" height="315" src="https://www.youtube.com/embed/9qmEQAyWsM8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


お聞き苦しいですが男の声は私の声です(涙)。その後の女性の声がシステムから出力されたものです。内部的にはText-To-Speechなので、発声時のイントネーションとかは完全に失われますが、それなりに良い感じで動いていると思います。
なお、素のマイク入力を相手に送信するかしないかは設定で変えることができます。

今回からDockerイメージの作成に加えて、Herokuにもデプロイしています。とりあえず、使ってみてください。

■ Dockerコンテナの起動

```
$ docker run -p 8888:8888 dannadori/zoom-meeting-plus:v04
$ docker run --rm -it -p '127.0.0.1:50021:50021' voicevox/voicevox_engine:cpu-ubuntu20.04-latest
$ docker run -it -p 5500:5500 synesthesiam/opentts:en
```

http://localhost:8888/ にアクセスすれば使えるようになります。
なお、httpで起動しているのでローカルで使用する必要があります。

■ Heroku上のアプリにアクセス

Herokuは一定時間アクセスがないと停止してしまい、再起動に少々時間がかかります。アプリ開始されるまで気長にお待ちください。(多分１分もかからないと思います。)


https://zoom-meeting-plus.herokuapp.com/



操作は次の通りです。

![image](https://user-images.githubusercontent.com/48346627/178978030-92ecbac4-417f-4eff-b7ad-ea1cde351fe8.png)

マイク入力をそのまま相手に送信したい場合は、ギアのアイコンから設定画面を開いてください。マイクを選択して"connect"をチェックすると相手に送信されます。

![image](https://user-images.githubusercontent.com/48346627/178978649-ad767a71-b67c-4401-a570-7430cf146057.png)


# 構成
今回は、音声認識をしたうえでText-To-Speechを行って音声に戻しています。音声認識にはChromeのWeb Speech APIを使用しています。音声認識により出力されたテキストを、以前の記事で紹介したVoiceVox, あるいはOpenTTSに入力して音声を生成しています。生成された音声は、これまでご紹介してきたWeb Audio APIのノードチェインに流し込んで相手に送信します。

![image](https://user-images.githubusercontent.com/48346627/178979459-c8556364-04be-46bb-90f5-3daab3c448ec.png)

# デモ
記事の冒頭で示した通りです。

# リポジトリ
ここで紹介したデモは次のリポジトリで公開しています。
https://github.com/w-okada/zoom-meeting-plus

どんどんアップデートしていくので最新版は安定しない可能性があります。
次のタグでcloneして使ってください。
```
$ git clone https://github.com/w-okada/zoom-meeting-plus -b v04
```

# 宣伝
弊社 FLECT は、Salesforce を中心に AWS, GCP, Azure を用いたマルチクラウドシステムの提案、開発を行っております。 また、ビデオ会議システムについては、Zoom SDK, Amazon Chime SDK, Twillio など各種ビデオ会議用 SDK を用いたシステムの開発しており、サービス化の実績も多数あります。 ご興味をお持ちいただけたら是非ご相談ください。
【PHP】巨大なダウンロードファイル作成時にtmpファイルを作成する

# 何故作ったのか
​
業務中にDBのSELECT結果を加工しcsvで出力する処理があったが、
出力されるファイルサイズが大きく、加工処理も複雑なものであった。
この処理でメモリ多く食ってしまうのではないかと思い簡単な対策として作成した。
​
## ソース
​
```php
$filename = '好きなファイル名を入れてください.csv';
​
//tmpファイル作成
$csvFile = tmpfile();
$metaData = stream_get_meta_data($csv);
//tmpファイルのパスを取得する
$csvpath = $metaData['uri'];
​
/*
​
tmpファイルに大量に書き込む処理
​
*/
​
//この例ではcsvなのでファイルによってContent-Typeを変える事
header("Content-Type: text/csv");
//-- ウェブブラウザが独自にMIMEタイプを判断する処理を抑止する
header("X-Content-Type-Options: nosniff");
//tmpファイルのサイズを渡す
header("Content-Length: " . filesize($csvpath));
//ファイル名を指定する
header("Content-Disposition: attachment; filename={$filename}");
​
//readfile()の前に出力バッファリングを無効化する
while (ob_get_level()) { ob_end_clean(); }
​
//tmpファイルのパスを取得し、出力する
readfile($csvpath);
//tmpファイルを破棄する
fclose($csvFile);
```
​
# 解説
​
ダウンロード処理に関しては以下の方が詳しいとのでこちらを参照されたし
【PHP】正しいダウンロード処理の書き方
<https://qiita.com/fallout/items/3682e529d189693109eb>
​
### tmpファイル作成
​
```php
//tmpファイル作成
$csvFile = tmpfile();
$metaData = stream_get_meta_data($csv);
//tmpファイルのパスを取得する
$csvpath = $metaData['uri'];
​
//~~中略~~~
​
fclose($csvFile);
```
​
`tmpfile()`は書き込み可なユニークなファイルを作成し、そのハンドルを戻り値として返す関数である
ただファイルの名前や、保存先までは返してくれない為、`stream_get_meta_data`で取得している。
PHP4ではuriが戻り値の配列に入っていないようだが、未だにPHP4を使っていると事はないだろう
~~未だに5.6.4を使っている筆者の現場も大差ないが~~
​
## Content-Type
​
```php
//この例ではcsvなのでファイルによってContent-Typeを変える事
header("Content-Type: text/csv");
​
//jsonの場合
header("Content-Type: application/json");
​
```
​
この箇所はダウンロードさせるファイルによって違うので以下を確認するといい
[公式MIMEタイプ一覧](https://www.iana.org/assignments/media-types/media-types.xhtml)
上では数が多く探すのが大変であれば、MDNの以下のページが分かりやすい
[よくある MIME タイプ](https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types)
HTTPステータスコードまとめ

# なにこれ？
エンジニアをやっているとHTTPのステータスコードを目にする機会が圧倒的に増えます。
ステータスを見て、どこに原因があるのかを突き止めていくのです。

私は、これまではなんとなく覚えている程度でしたが、もう少ししっかり覚えるためにまとめておきます。

# レスポンスの分類
レスポンスは3桁の数字で返されます。
それらは、何番台のエラーかで大まかに分けることができます。

| ステータスコード | 意味 |
|:-|:-|
| 100番台  | 情報  |
| 200番台  | 成功  |
| 300番台  | リダイレクト  |
| 400番台  | クライアントエラー  |
| 500番台  | サーバーエラー  |


# 100番台
100番台は**情報**を意味します。

| ステータスコード | 名前 | 説明 |
|:-|:-|:-|
| 100  | Continue  | 継続  |
| 101  | Switching Protocols  | プロトコル切り替え  |
| 102  | Processing  | 処理中  |
| 103  | Early Hints  | 早期のヒント  |


# 200番台
200番台は**成功**を意味します。

| ステータスコード | 名前 | 説明 |
|:-|:-|:-|
| 200  | OK  | リクエストは成功した  |
| 201  | Created  | リクエストが成功し、新しいリソースが作られた  |
| 202  | Accepted  | リクエストは成功した  |
| 204  | No Content  | コンテンツなし  |


# 300番台
300番台は**リダイレクト**を意味します。

| ステータスコード | 名前 | 説明 |
|:-|:-|:-|
| 300  | Multiple Choices  | 複数のリソースが存在する  |
|  301 | Moved Permanently  | リソースは恒久的に移動した  |
| 302  | Found  | リクエストしたリソースは一時的に移動している  |
| 303  | See Other  | 他を参照  |
| 304  | Not Modified  | 前回から更新されていない  |
| 307  | Temporary Redirect  | リクエストしたリソースは一時的に移動している  |


# 400番台
400番台は**クライアントエラー**を意味します。

| ステータスコード | 名前 | 説明 |
|:-|:-|:-|
| 400  | Bad Request  | リクエストが正しくない  |
| 401  | Unauthorized  | 認証が必要  |
| 403  | Forbidden  | アクセスが禁止されている  |
| 404  | Not Found  | 指定したリソースがない  |
| 405  | Method Not Allowed  | 指定したメソッドは使うことができない  |
| 406  | Not Acceptable  | Accept関連のヘッダに受理できない内容が含まれている  |
| 408  | Request Timeout  | リクエストが時間以内に完了しなかった  |
| 409  | Conflict  | リソースが矛盾した  |
| 410  | Gone  | 指定したリソースは消滅した  |
| 413  | Request Entity Too Large  | リクエストボディが大きすぎる  |
| 414  | Request-URI Too Long  | リクエストされたURIが長すぎる  |
| 415  | Unsupported Media Type  | サポートされていないメディアタイプが指定された  |
| 429  | Too Many Requests  | リクエスト回数が多すぎる  |


# 500番台
500番台は**サーバーエラー**を意味します。

| ステータスコード | 名前 | 説明 |
|:-|:-|:-|
| 500  | Internal Server Error  | サーバ側でエラーが発生した  |
| 503  | Service Unavailable  | サーバが一時的に停止している  |


# まとめ
何番台だとどんな内容のレスポンスなのかは掴んでおくと良いでしょう。
休日にslackを開かない技術

## 問題提起

休日にslackの更新が気になってつい開いてしまうことありませんか。
そして、開いたら休日にslackを開いて平日終業後に飛んできていたメンションに返信をしたり、仕事のことを思い出したりして、せっかくの休日気分が台無しになったりすることはありますよね。私はあります。

ふと、少し時間があってslackをチラ見。
あ・・・、問い合わせ来てる・・・。すぐに返さないと。ぽちぽち。返信。

って今日は休日。損した気分。タダ働きだ。slack見なければよかった。

こんな感じ。

これではリフレッシュできません。リフレッシュが良い仕事を生みます。
そこでここでは休日にslackを開かない技術を紹介します。

## slackを開かない技術

いくつか紹介します。全て実践することで快適な休日を送ることができます。

### DnDを設定する

基本です。通知を止めましょう。私は基本的には月曜日の朝9時から金曜の18時のみ通知を受けとるようにしています。お休みする場合は、前日就業時に休み明けの朝9時に設定しています。

本当にやばい時はこの設定を無視してDMを送ることができる仕組みがあるので、DMを送る側に通知を鳴らすかどうかの判断を委ねましょう。確か1日１回限定とかだったはず。本当に本当にやばい時は電話が来ます。

システムのアラートに気付きたい？
はい、そうですよね。わかります。
こういう時は、アラート見張り番の業務を用意して担当者をローテーションし、日当を出してはいかがでしょうか。もしくは電話でアラートが来る仕組みにしましょう。常にslackの通知を気にしていては、リフレッシュできません。

### 名前、表示名に休みの日付を書く

slackのプロフィール画面へいき、名前の末尾に休みであることを明示しましょう。
例えば、`山田 太郎` という名前でしたら、`山田 太郎 7/14 休み` です。
こうすることで、メンションをする際に「あ、休みなんだ。返信は今日来ないな」と思ってもらえ相手を待たせることはなくなります。急いでいれば他を当たってもらえるでしょう。返信しなくても誰も傷付きません。

ただ、この運用を行うと「休みだからメンションは控えよう」と思われてしまいます。
例えば「休み明けの山田さんへ XXXの件ですが・・・」などと書かれてしまいます。これだと大切な要件を見落としてしまう可能性があります。

これの対策は下記の２点です。

1. slackの通知は受け取る側がコントロールし、送る側はいつでもメンションをしても良いというルールを徹底する
2. 通知のキーワード登録で自身の名前を登録する

これで休み明けにメンション（とメンション相当）が把握できるようになります。

### プライベートのPCで会社のワークスペースにログインしない

基本的には普段使う端末ではログインしないようにします。未読マークで読もうかな、と思ってしまうのが人間の心です。赤いバッチが付いたらもうslackを開くのが我慢できなくなってしまうでしょう。

### スマホではslackのアプリをファーストビューに配置しない

スマホのホーム画面などにslackのアプリをおくと、つい平日の癖でslackを開いてしまいませんか？私は以前はそうでした。開きたくないのに、つい押してしまう。これが嫌でした。

対策としてはスマホの奥深くにslackアプリを配置します。これにより、つい開いてしまうことを防止できます。平日はどうせPCメインでslackを開くわけ（謎の決めつけ）なので、スマホからすぐに開ける必要はありません。すぐに開けない位置に配置しましょう。


## まとめ

以上、休日にslackを開かない技術でした。ぜひ活用してよい休日を過ごし、平日の仕事にいかしましょう。
コマンドのエイリアスを作成する

自分用の忘備録
freebsdにて、composerコマンドを叩いたら見つからなかったので、エイリアスを設定する。

まず、ホームディレクトリに存在する「.cshrc」を編集する。
<pre>vi .cshrc</pre>

設定したいコマンドを以下のように記述する。
以下はcomposerコマンドを設定する場合の記述。
<pre>alias composer php composer.phar</pre>

追記したら以下のコマンドにて「.cshrc」を再度読み込む。
<pre>source ~/.cshrc</pre>
再読み込み後、作成したコマンドを使ってみると...
<pre>composer -V
Composer version 2.3.10 2022-07-13 15:48:23</pre>
無事機能していることを確認。
あのOSSで有名なEvil Martians正式に日本上陸！（職務執行者就任！）

## 日本のオフィスは大阪の江戸堀!

![iOS の画像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462614/14cebb35-93df-3a6b-eaaa-99f69de6ba48.jpeg)


突然ですが、報告です！

Evil Martians Inc.（アメリカ）の子会社、合同会社イービルマーシャンズの職務執行者に就任しました。
日本で外資系企業設立もいろいろ大変なこともありましたので、興味ある人は絡んでください。

Evil Martians Inc.はアメリカのユニコーンも含むスタートアップと開発した経験が豊富にあるので、今後は日本のIT企業のアメリカ進出も支援できればと思っています！

日本でも火星人のコンサルティングと開発サービスを日本語で提供できるようになっています。ちなみに、9月に開催されるRuby Kaigiでは、チーム全員に会うことができますよ！

火星人の多くはアニメが好きで、その影響で日本語もできますので、AnyCable Proやimgproxy Proなどの製品のサポートもできます！
今後はQiitaを通じてAnyCableを使ってのRailsアプリの開発など、Evil MartiansのOSS関連情報もいっぱい発信していきたいと思っています！

火星への連絡は日本語でどうぞ！

### オフィスで遊ぼう

![iOS の画像 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462614/cf434a80-d3a2-2dec-e910-66961b810335.jpeg)


オフィスにはまだ何もないですが、今後セガメガドライブとかの最新ゲーム機など充実していきたいです。
近くにジェラートが美味しいお店もあるので、大阪の江戸堀に来ていたら是非遊びに来てください！

追記。Sidekiqでも採用されている有名なGemの開発者も近いうち日本へ来る予定ですので、それも楽しみにしてください！実際に日本に来てもらったら「ユーは何しに日本へ？」風にレポートしていきたいと思います。
Target class [app\Http\Controllers\TestController] does not exist.

Target class [app\Http\Controllers\TestController] does not exist.
というエラーになったので備忘録。

![errro.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180727/6e7a5a66-3020-e943-b41a-f516413226c2.png)


# コード

```php:routes\web.php
Route::get('/test', [\app\Http\Controllers\TestController::class, 'index']);
```

```php:resources\views\test.blade.php
Hello<?php echo $name ?>さん,
あなたのレベルは、<?php echo $level ?>です。
```

```php:app\Http\Controllers\TestController.php
<?php

namespace app\Http\Controllers;
use Illuminate\Http\Request;

class TestController extends Controller
{
    public function index() {
        $data['name'] = 'TEST';
        $data['level'] = 11;

        return view('test', $data);
    }
}
```

# 対応

\App\Http\ControllersのAppをappにした。


# なぜフォルダ名はAppなのに、名前空間はappでないとダメなのか？

composer.jsonにて、このフォルダはこの名前空間名で呼び出す、というルールになっているため、らしい。

![com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180727/a86be1de-36d4-7545-c547-18b02151f7b4.png)

※バージョン
Laravel Framework 9.18.0
Arduino UNOでI2C EEPROで遊ぶ1(書き込み、読み込み)



目的
I2C EEPROMのテスト

使用ハード

ＩｃｈｉｇｏＪａｍ用外部記憶装置キット　ＩｃｈｉｇｏＲＯＭ
[AE-IchigoROM]
通販コード　K-12061
発売日　2017/08/23

I2C アドレス　0x50 と　0x51(2バンク目)


![o_con576.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/0be11923-9487-8e60-d474-15d40209f030.jpeg)



![o_con574.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1cd68e95-4f8b-a417-e09a-f6d2ab5ccd7e.jpeg)


![o_con575.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/976ae1e6-00b0-6ad0-4d8a-6c9e89844c41.jpeg)



書き込み


```c++


//SER_I2C_EEPROM_0_15_UNO_1

#include <Arduino.h>
#include <Wire.h>

//初期化処理
void setup() {

  //シリアルの初期化
  Serial.begin(9600);

  //I2Cの初期化
  Wire.begin();

  for (int ii = 0; ii < 16; ii++) {

    Wire.beginTransmission(0x50);
    //アドレス
    Wire.write(0x00);     //H
    Wire.write(ii);       //L
    //データ
    Wire.write(ii);
    Wire.endTransmission();
    delay(5);

  }//for

  Serial.println("Eeprom Write OK!!");

}//setup

//メインループ
void loop()
{
}//loop





```

読み込み


```c++



//SER_I2C_EEPROM_0_15_read_UNO_1

#include <Arduino.h>
#include <Wire.h>

//初期化処理
void setup() {

  //シリアルの初期化
  Serial.begin(9600);

  //I2Cの初期化
  Wire.begin();

  for (int ii = 0; ii < 16; ii++) {

    Wire.beginTransmission(0x50);
    //アドレス
    Wire.write(0);     // H
    Wire.write(ii);    // L
    Wire.endTransmission();

    //データ
    Wire.requestFrom(0x50, 1);
    while (Wire.available() == 0 ) {}
    Serial.println(Wire.read());

  }//for

}//setup

//メインループ
void loop()
{
}//loop



```
Ubuntu22.04 で マウスカーソルを大きくする

「Ubuntu20.04 で マウスカーソルを大きくする」
https://qiita.com/nanbuwks/items/59a9939ffcb5c2118ce5

では デスクトップとGoogleChromeで異なる設定が必要だったが Ubuntu22.04 ではもっと簡単に設定できた。

# 設定

「設定」-「アクセシビリティ」-「カーソルの大きさ」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b2928dff-7e8b-774a-275e-ea77f74ee9dc.png)

で設定できます。再起動しなくてもすぐ反映できました。また、Ubuntu20.04 では Google Chrome にはこの変更は反映しませんでしたが今回は Google Chrome 上でも正常に反映されました。

Google Chromeではマウスカーソルが大きくなると指し示す座標とアイコンがずれてしまったのですがその問題もなく、快調です。

# マウスカーソルのデザインを変更する

Tweaks を使って変更します。


「ソフトウェア」で tweaks を検索し、インストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/05dcfe56-4b24-508a-3411-dfa74c21be71.png)

CUI でインストールする場合は、以下のようにします。
```
$ sudo apt install gnome-tweaks

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/f85e85f9-f7c3-a729-de17-ed2a8c0e9452.png)
起動して、「外観」の「カーソル」で変更できます。

# マウスカーソルのカスタムデザインを導入する

https://www.gnome-look.org/browse?cat=107&ord=latest

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/f8301f7b-2e70-d9fd-8d1c-8fa4a8f771e5.png)

こちらに利用できるマウスカーソルが集まっています。Webブラウザからインストールできるように見えますが、アシストアプリを入れないといけないのでめんどくさそうです。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/7f49d807-8195-a910-d144-72ebea6d4f78.png)

Piling-Store を AppImageで落として、マウスカーソルのインストールの為だけに起動することにしました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/4015e63f-04ac-7d64-139a-8a0e4bc9978b.png)

~~https://www.pling.com/p/1175480/ から、pling-store をダウンロードします。~~

(2023/10/31 追記) バージョンアップなどによりURL が変更になるので以下から pling-store で検索します。


https://www.pling.com/find?search=pling-store
```


$ chmod 777 pling-store-5.0.2-1-x86_64.AppImage 
$ ./pling-store-5.0.2-1-x86_64.AppImage 
```

で実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/1e4ed311-662d-9422-8a29-0808d64f0635.png)
しばらくしたら起動するので、欲しいマウスカーソルをインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d90896e3-867f-c476-eac9-8224a49ddf55.png)

うまくできたら tweaks の選択肢が増えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b3f55486-436b-22a0-f41b-41b2e902b84e.png)
異常検知　SPADE系譜の原点「Deep Nearest Neighbor Anomaly Detection」を解説してみた

# はじめに
こんにちは。機械学習初心者の恐竜です。
勉強したことのアウトプットとして何かやらないとな～と思いつつもなかなか手が出なかったときに、[このイベント](https://qiita.com/official-events/97ae2edf9e1a01bde1fe)が開催されていたので投稿してみました。
初めての投稿記事＆英語論文解説なので、至らぬ点が多くあると思います。ミスリード等あればご指摘いただけると幸いです。
***

# この記事の目的
この記事ではSPADE～PaDiM~PatchCoreと続く異常検知手法の原点とされる論文「Deep Nearest Neighbor Anomaly Detection」の内容紹介をします。
***

# DN2とは
・論文：[Deep Nearest Neighbor Anomaly Detection[2020/02]](https://arxiv.org/abs/2002.10445)

シンプルな異常検知手法としての最近傍法（K-NN）と、特徴量の抽出器として事前学習モデルを利用した手法。
従来の転移学習や自己教師あり学習と比較して、少ない学習枚数で精度・学習時間の短縮・データノイズに対する高いロバスト性を得ています。

***

# 結論
結果として、比較している従来手法の精度が62~90%に対して、92.5%の精度を達成しています。（データセットはCifar10)
画像の特徴量を上手く分けることで、高い精度が得られるとのこと。

***
# 利点
驚きなのはこの精度を追加学習無で達成しているということです。
少ない学習データでも高い汎化性能が得られていることを下記図で示しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2569819/81b9db3d-a504-053d-8172-4eb181613e60.png)

左図の緑色が提案手法のDN2です。学習画像数が10枚でもかなり高い精度となっています。


また事前学習モデルと類似性の無い画像でも応用が可能とのことで、論文中では顕微鏡画像や航空写真に対しても異常検知できることを実験で検証しています。
***

# 手法のキモ
なぜDN2は高い精度が得られるのかを説明するために、CIFAR10の画像データの特徴量を可視化した図を用いています。上が飛行機、下が自動車のデータです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2569819/9ee1cad8-fea1-9136-5726-2106471fc424.png)
一番右の上下2つがDN2の結果です。正常データは黄色、異常データは青色で表示されています。
見てわかるように、DN2は正常データが固まっていて特徴量が上手く抽出されています。
この正常データの密度が高いことがK-NNの精度向上の理由だと論文で述べています。

***

# おわりに
SPADE系の出発点ともいえる「Deep Nearest Neighbor Anomaly Detection」について解説しました。
ここから引用されていく論文の紹介記事も書いていければと思います。

***
# 参考記事
[異常検知　SPADE登場からの1年間](https://qiita.com/h1day/items/e65d840e557a14cf1a37)
[プログラミング初心者のためのQiita記事投稿テンプレート](https://qiita.com/YutaManaka/items/611069745ff0f68af879)
テスト73

```vb
Sub Excel_SQL()

Dim str_SQL As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim MyPath As String

'ExcelでExcelのデータを使ってSQLを使う時の例文...〆(･ω･´)ﾒﾓﾒﾓ

'自己結合での内部結合
'str_SQL = "SELECT DISTINCT p1.prod_name, p1.prod_category " & _
'          "FROM [products$] p1 INNER JOIN [products$] p2 " & _
'          "ON p1.prod_category = p2.prod_category " & _
'          "AND p1.prod_name <> p2.prod_name " & _
'          "ORDER BY 2,1;"

'自己結合での右外部結合
str_SQL = "SELECT DISTINCT p1.prod_name, p1.prod_category " & _
          "FROM [products$] p1 RIGHT OUTER JOIN [products$] p2 " & _
          "ON p1.prod_category = p2.prod_category " & _
          "AND p1.prod_name <> p2.prod_name " & _
          "WHERE p1.prod_name IS NOT NULL " & _
          "ORDER BY 2,1;"

'自己結合での左外部結合
'str_SQL = "SELECT p1.prod_name, p1.prod_category " & _
'          "FROM [products$] p1 LEFT OUTER JOIN [products$] p2 " & _
'          "ON p1.prod_category = p2.prod_category " & _
'          "AND p1.prod_name <> p2.prod_name " & _
'          "ORDER BY 2,1;"
   
'自己結合での完全外部結合
'str_SQL = "SELECT p1.prod_name, p1.prod_category " & _
'          "FROM [products$] p1 LEFT OUTER JOIN [products$] p2 " & _
'          "ON p1.prod_category = p2.prod_category " & _
'          "AND p1.prod_name <> p2.prod_name " & _
'          "WHERE p1.prod_name IS NOT NULL " & _
'          "UNION " & _
'          "SELECT p1.prod_name, p1.prod_category " & _
'          "FROM [products$] p1 RIGHT OUTER JOIN [products$] p2 " & _
'          "ON p1.prod_category = p2.prod_category " & _
'          "AND p1.prod_name <> p2.prod_name " & _
'          "WHERE p1.prod_name IS NOT NULL " & _
'          "ORDER BY 2,1;"
                 
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset

MyPath = ThisWorkbook.FullName

cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
        "Data Source=" & MyPath & ";" & _
        "Extended Properties='Excel 12.0;HDR=YES' "
               
Set rs = cn.Execute(str_SQL)
                                
With ThisWorkbook.Sheets("製品結果")

'    .Cells.Clear
     .Range("A1").CopyFromRecordset rs
   
End With

rs.Close
cn.Close

Set rs = Nothing
Set cn = Nothing

End Sub
GKE Autopilot上にNginx環境を構築

## はじめに
本記事ではGKE Autopilot上にNginxを構築してみます。GKE Autopilotとは2021年にリリースされたGKEの新しい運用モードで、ノードやPodの管理はマネージドになりGKEが自動でプロビジョニング、スケーリングしてくれます。

## 構築してみる

### 1. クラスタの作成
「Kubernetes Engine」 →　「クラスタ」を選択します。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/14307c49-8dfb-aaf9-6cb0-0911aa017e46.png)

Kubernetes Engine APIを有効化します。Kubernetes Engine APIの有効化は初回のみ実施する必要があります。「有効にする」ボタンをクリックするだけですが、有効化されるまで少しだけ時間がかかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/b9b1ed55-299d-6a07-f1e3-2f66a1530079.png)

「作成」リンクをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/813dedeb-2302-81c8-1309-c3a9b4de49e3.png)

GKE Autopilotの「構成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/e7a2cc56-dae3-5d8c-d9b8-2f8dc7ec3efe.png)

リージョンのみ「asia-northeast1」(東京リージョン)に変更して、そのまま「作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/2d052f66-544b-61bf-adb8-188b71fcee97.png)

クラスタの作成には5分以上かかりますが無事作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/f1d9ec49-15a7-3d48-7160-12c8a5bd1506.png)

### 2. Nginxのデプロイ
早速デプロイしてみます。画面上部の「デプロイ」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/dcac2deb-5bfc-3ca2-4d0b-a647f01f5819.png)

デフォルトでnginx:latestが入力されています。今回はNginxを構築するので何もせずそのまま「続行」ボタンをクリックします。ちなみに「選択」リンクをクリックすることで、Container RegistryやArtifact Registryにあるイメージも選択可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/1b5480a1-d6f5-7fd2-a924-539f11139666.png)

先ほど作ったクラスタが選択されているかを確認し「デプロイ」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/3f21cef7-a9e7-65c4-c8df-85973512b3a7.png)

しばらく待つと無事デプロイが完了します（今回は一度デプロイに失敗しました。失敗の理由は不明でしたが再度上記手順でデプロイをやり直したところ無事完了しました）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/554f4a3b-e475-3a69-0420-2c69a958e531.png)

### 3. サービスの公開
「公開」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/7da1ab63-4410-c05a-a74e-11aa25fe9d0a.png)

公開が完了すると、アクセス用の外部エンドポイントが割り当てられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/dc681541-8850-797a-ddff-34d3cf7cbd89.png)


### 4. 動作確認
上記で割り当てられた外部エンドポイントにアクセスしてみます。以下のような形でNginxのWelcome画面が表示されればGKE上にNginxが構築されたことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/a5e62606-09a5-8792-e957-c521c7c839e4.png)

以上です。
システムコール サービスコール とは

## 勉強前イメージ

OSからの連絡的な？

## 調査

### システムコール とは

コンピュータの上で動く、カーネルを動かす権限の無いプログラムが特権モードで動作しているカーネルを呼び出すことをシステムコールと言います。
他にもサービスコールやカーネルコールとも呼ばれます。

コンピュータ上では様々なプログラムが稼働しています。
どのプログラムもOSの動作に影響を与えると変な動きになってしまうので、大きな権限を与えていないです。
しかし、権限が必要なプログラムもあります。
その場合に変わりにカーネル関数を使ってカーネルに依頼を行います。

そもそもカーネルとはOSの中核を構成するソフトウェアで
メモリの割り振りなどソフトウェアとハードウェアの間で仲介する役割になります。
システムコールを使用するときは、そのプログラムがハードウェアの制御をするときになります。
`アプリケーション(プログラム) → カーネル → HW` の順番になるので
カーネルに依頼を行うことで動作を変更することが出来ます。

カーネルの詳細は [こちら](https://qiita.com/miyuki_samitani/items/cb3b654add9fa07d6a5e) をごらんください。

## 勉強後イメージ

要するに
プログラムがHW(メモリとか)を制御するときに権限がないから代わりにカーネルに依頼することをシステムコールっていうって感じかな

## 参考

- [システムコールについてどれくらいご存じですか？](https://atmarkit.itmedia.co.jp/ait/articles/1111/16/news161_2.html)
- [システムコール 【system call】 スーパーバイザコール / supervisor call / SVC](https://e-words.jp/w/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB.html)
RaspberryPi4にJenkinsをインストールする

# やりたいこと

RaspberryPiにJenkinsをインストールする。


# 手順


### 1.openjdkをインストール

```shell
sudo apt install openjdk-11-jdk
```


### 2.keyringのダウンロード

```shell
curl -o /usr/share/keyrings/jenkins-keyring.asc -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key
```


### 3.source.listに追加

```text:/etc/apt/sources.list.d/jenkins.list
deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian-stable binary/
```


### 4.jenkinsのインストール

```shell
sudo apt update
sudo apt install jenkins
```
flowchart.js でフローチャートを作成

フローチャートを作成する必要があり、なにかいいソリューションはないかと探したところ、以下を見つけました。

https://flowchart.js.org/

簡単に書けて、出力は SVG でされるので、よいですね。

F12 を押下して、インスペクターで svg のタグを探すと、出てきます。
TypeScriptでclass名の文字列から動的にclassのインスタンスを生成する

クローラーの開発をしていて、Site1Crawler,Site2Crawlerといった形で数字の違うクローラーを作成、class名から動的に別々のクローラーのインスタンスを生成したかったのでその方法です。

ディレクトリは適当でいいのですが、今回4つのファイルを作成します。

- Crawler.ts
    - 量産されたclassをまとめて書いておくファイル
- BaseCrawler.ts
    - 継承される元となるclass。インターフェースとして書いてもいいが今回はclassで作成する
- Site1Crawler.ts(Site2Crawler.tsなどこのファイルを量産していく)
    - BaseCrawlerを継承した動的に呼び出されるclass
- CrawlerFactory.ts
    - 動的に呼び出す部分を記載するFactory class

まず最初に継承元となるBaseCrawlerのclassを作成します。

```Javascript:BaseCrawler.ts
export default class BaseCrawler
{
  public siteId: number = 0;

  constructor() {
    this.init();
  }

  public init()
  {
  }

  public crawling()
  {
    console.log(url);
  }
}
```

次に量産するSite1Crawlerのclassを作成します。

```Javascript:Site1Crawler.ts
import BaseCrawler from "./BaseCrawler";

export default class Site1Crawler extends BaseCrawler
{
  init(): void {
    super.init();
    this.siteId = 1;
  }

  crawling(): void {
    super.crawling();
    console.log("site1 crawler crawling");
  }
}
```

量産されたclassをまとめて記載しておくファイルを作成します。

```Javascript:Crawler.ts
import Site1Crawler from "./Site1Crawler";

const Crawler: any = {
  Site1Crawler,
  (...量産していったらここにどんどん追加していく)
}

export default Crawler;
```

Factoryクラスの作成

```Javascript:CrawlerFactory.ts
import Crawler from "./Crawler";
import BaseCrawler from "./BaseCrawler";

export default class CrawlerFactory extends BaseCrawler{
  constructor(className: string)
  {
    super();
    if (Crawler[className] === undefined || Crawler[className] === null) {
      throw new Error(`Class type of \'${className}\' is not in the store`);
    }
    return new Crawler[className]();
  }
}
```

これで完了です。
呼び出す際には、

```Javascript:index.ts
import CrawlerFactory from "./CrawlerFactory";
import BaseCrawler from "./BaseCrawler";

const main = () {
  //class名の文字列で呼び出す
  const className = 'Site1Crawler';
  const crawler: BaseCrawler = new CrawlerFactory(className);
  crawler.crawling();

  //loopさせて動的に呼び出して実行させる
  //クローラーを10個作って順番に実行させたい場合
  for (let i = 0; i < 10; i++) {
    const className = `Site${i}Crawler`;
    const crawler: BaseCrawler = new CrawlerFactory(className);
    crawler.crawling();
  }
}
```
のように記載可能です。
evalとかの方法もあると思いますが、この方法が一番しっくりきたので。

---
参考
- https://buddhiamigo.medium.com/how-to-create-typescript-classes-dynamically-b29ca7767ee5
ソラカメを監視用にサーボモータで動かしてみた。

### ALGYAN　7th　記念イベントのプレゼントで戴いた「ソラカメ」を見張り用に、M5stick Cを使ってサーボモーターを動かしてみました。
![0190a977d4ebcbb43759dbaa9aa58ac4228ae07e71.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/b6091ca4-efb7-6270-bcb4-55db657bacbe.jpeg)

### １）サーボモーターの取り付け
　「ソラカメ｝が結構重たいのロボット用の丈夫なサーボモーターを使いました。安定させるために下部のヨー軸サーボをプラボックスの内側に固定しました。その上にピッチ軸のサーボを取り付けました。
![015f8f8ce2c2606d980ae8bc4983b53708e2c64f8d.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/4c579648-324f-fb0e-35f7-2b1029963c1d.jpeg)

　ソラカメ付属品の丸い鉄板を両面テープで張り付けました。
![01636e8f595a61f9c31771791d235a4187ba2345fd.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/fa3d9c75-5045-c17a-c27a-72a32396c4dd.jpeg)

### ２）M5stick Cのサーボモーター用自作HAT
　今回使ったサーボモーターが使ったサーボモーターが11.5V用のHVサーボモーターのためサーボ用電源に１２Vノ安定化電源を使うため、HATを自作しました。
![01fbca4129716dfc61b827c9b54f06537ba3fd0bd2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/23f6665b-7397-a737-09d5-fd497a3ebc43.jpeg)
　接続はこれだけです。
![01db18c9f02e165909bf9b27fb826804aec58296cf.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/405b5aa7-b1c5-eaab-e778-681b8c759abc.jpeg)

### ３）サーボモーター駆動ベース完成
　「ソラカメ｝の駆動用モータードライブが出来ました。
YouTube 動画　↓
https://youtu.be/1EUtlfsC5xM

### ４）作動テストの動画
サーボモーターで「ソラカメ」を動かした時の「ソラカメ」の動画です。

<iframe width="560" height="315" src="https://youtu.be/1EUtlfsC5xM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### ５）部品表
| 　　名称　　　 | 　仕様　　| 　メーカー　　| 数量 |
|:-----------|:-----------:|:------------:|:------------:|
|カメラ     　 | ソラカメ     | ソラコム       | １ |
|サーボモーター  |KSR4024 HV     | KONDO      | ２  |
|コントローラー  | M5stick C      | M5STACK        | １  |
| HAT        | 自作HAT         | 自作         | １  |
|BOX     | プラボックス    | ニチドウ      | １  |

### ６）回路図
![0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/3a4d13c0-d307-abe3-3209-b3f9a7e1ab2f.jpeg)

### ７）プログラム
![sora cam Y P.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526217/e021dd41-919c-1600-137b-fa2941c69a8e.jpeg)

### ７）終わりに
　取り合えず、サーボモーターを使って、プログラムで自動操作ができることを確認できました。
もっと滑らかに映るように調整をしたいと思います。
さらに遠隔操作や、色々なセンサーによる自動運転を目指して改良したいと思います。
Terraformでdynamicを活用して複数×複数のブロックを書く



* 配列になっている変数を複数行並べる
* dynamicブロックの中でfor_eachを活用し、行数分ブロックが作成される
* 複数ブロックの中の値はeach.valueの要素番号で取得する

# 例
```terraform
locals {
  Setting = {
    custom_error_response = {
      res-403-403 = [10, 403, 403, "/error-403.html"],
      res-502-502 = [10, 502, 502, "/error-502.html"]
    }
  }
}

resource "aws_cloudfront_distribution" "cdn" {
  dynamic "custom_error_response" {
    for_each = Setting.custom_error_response
    content {
      error_caching_min_ttl = custom_error_response.value[0]
      error_code            = custom_error_response.value[1]
      response_code         = custom_error_response.value[2]
      response_page_path    = custom_error_response.value[3]
    }
  }
}
```
Eclipse MRAA on Raspberry Pi 4 - Python

Eclipse MRAAを、Raspberry Pi 4で動かしてみました。Pythonで。

# MRAAとは？

GPIOやI2Cといった、ハードウェア操作するC/C++ライブラリです。
実装はC/C++ですが、JavaやPython、JavaScriptからも呼び出すことができます。

詳しくはこちら。

* [Eclipse MRAA project description](https://projects.eclipse.org/proposals/eclipse-mraa)
* [Eclipse MRAA source code](https://github.com/eclipse/mraa)

# Raspberry Pi 4

使用したラズパイとOSはこちらです。

* Raspberry Pi 4 Model B
* Raspberry Pi OS Lite (32-bit) - 2022-04-04

## インストール

ソースを取得してコンパイル、インストールします。

```
sudo apt update
sudo apt install git build-essential swig python3-dev cmake libjson-c-dev
git clone https://github.com/eclipse/mraa
cd mraa
mkdir build
cd build
cmake -DBUILDSWIGNODE=OFF ..
make
sudo make install
sudo ldconfig
```

> ```ImportError: libmraa.so.2: cannot open shared object file: No such file or directory```エラーが発生したときは、```sudo ldconfig```を忘れている可能性大です。→[参考？](https://twitter.com/matsujirushi12/status/1547032441730187264)

## デジタル入力

```python:digitalread-switch.py
#!/usr/bin/env python3

import mraa
import time

switch = mraa.Gpio(7)  # Pin7 - GPIO4
switch.mode(mraa.MODE_PULLUP)   # IT DON'T WORK :-P
switch.dir(mraa.DIR_IN)

while True:
    if switch.read() == 1:
        print("ON")
    else:
        print("off")
    time.sleep(1)
```

> ハードウェアの結線図は、参考文献1のP181 図5です。

## デジタル出力

```python:digitalwrite-led.py
#!/usr/bin/env python3

import mraa
import time

led = mraa.Gpio(7)  # Pin7 - GPIO4
led.dir(mraa.DIR_OUT)

while True:
    led.write(1)
    time.sleep(0.2)

    led.write(0)
    time.sleep(0.8)
```

> ハードウェアの結線図は、参考文献1のP189 図2です。

## PWM出力

(TBC)

## I2C

```python:i2c-accel.py
#!/usr/bin/env python3

import mraa
import time

sensor = mraa.I2c(0)
sensor.address(0x1d)

# print(sensor.readReg(0x00))

sensor.write(bytearray([0x2d, 0x08]))

while True:
    sensor.write(bytearray([0x32]))
    val = sensor.read(6)

    x = int.from_bytes([val[0], val[1]], "little", signed=True)
    y = int.from_bytes([val[2], val[3]], "little", signed=True)
    z = int.from_bytes([val[4], val[5]], "little", signed=True)
    print(f"{x} {y} {z}")

    time.sleep(0.2)
```

> ハードウェアの結線図は、参考文献1のP218 図6です。

## SPI

```python:spi-dac.py
#!/usr/bin/env python3

import mraa
import time

dac = mraa.Spi(0)

while True:
    for i in range(100):
        val = int(i / 100 * 3 / 4.096 * 4096)
        val_bytes = val.to_bytes(2, "big")
        dac.write(bytearray([(0b0001 << 4) | val_bytes[0], val_bytes[1]]))
        time.sleep(0.001)
```

> ハードウェアの結線図は、参考文献1のP247 図5です。

## UART

2022/7/14時点、Raspberry Pi 4でUARTが動かない🤔。→[issue#1090](https://github.com/eclipse/mraa/issues/1090)

# 参考リンク

* **[参考文献1]** [ラズパイ自由自在電子工作パーツ制御完全攻略@日経BP](https://info.nikkeibp.co.jp/media/RAS/atcl/books/031100013/)
pandasでUTCの日時の文字列を読み込んで、JSTに変換する

`tz_convert()`が`pandas.Series`型に直接適用できないのがハマりポイント。


```python
# datetime列に文字列で日時を登録する
df = pd.DataFrame([{"datetime":"2022-07-03 00:00:00"},{"datetime":"2022-07-03 01:00:00"},{"datetime":"2022-07-03 02:00:00"}]) 

# datetune列の内容をDatetime型かつaware(UTC)で、"datetime_utc"列に入れる 
df["datetime_utc"] = pd.to_datetime(df["datetime"], utc=True) 

# JSTに変換したデータを"datetime_jst"列に入れる
df["datetime_jst"] = df["datetime_utc"].dt.tz_convert('Asia/Tokyo')
```

書いてから気付きましたが、Python3のタイムゾーンの扱い的に`'Asia/Tokyo'`の部分は環境依存かもしれません。

# 参考にしたページ
https://note.nkmk.me/python-pandas-tz-convert-tz-localize/
jQuery要素追加、削除　簡易まとめ

# 初めに

今回の記事ではAjaxを用いたアプリケーション作成を行った際に多用したjQUeryでの要素追加、削除の方法を復習も兼ねて
簡単にまとめていこうと思います。

## 1　要素追加
アプリケーション作成の際に`要素追加`で用いたのは下記の4つです
```js
$(a).append(`HTML要素`);
$(a).prepend(`HTML要素`);
$(a).after(`HTML要素`);
$(a).before(`HTML要素`);
```
基本的な使い方を説明するとコードの`a`の箇所にはclass名、id名、タグ（div、pなど）を入れて要素をどこに追加するのかを指定します。
具体的な使い方としては下記の通りです。
```html
<body>
<ul class="name">
   <li>佐藤さん</li>
   <li>田中さん</li>
   <li>加藤さん</li>
</ul>

<script>
    $(".name").apppend(`<li>渡辺さん</li>`);
</script>
</body>
```
上記のようなコードを書くことによって`<li>加藤さん</li>`の下に`<li>渡辺さん</li>`が追加されます。
``$(".name").prepend(`<li>渡辺さん</li>`);``の場合は`<li>佐藤さん</li>`の上に`<li>渡辺さん</li>`が追加されます。
``$(".name").after(`<div>後ろに追加</div>`);``の場合は`</ul>`の下に`<div>後ろに追加</div>`が追加されます。
``$(".name").before(`<div>前に追加</div>`);``の場合は`<ul class="name">`の上に`<div>前に追加</div>`が追加されます。

## 2 要素削除
アプリケーション作成の際に`要素削除`で用いたのは下記の3つです
```js
$(a).remove();
$(a).children().remove();
$(a).empty();
```
基本的な使い方は要素追加の時と同じで`a`の箇所にclass名などを指定します。
要素削除は各機能ごとにコードを用いて説明したいと思います。
```html
<body>
<ul id="name_list">
   <li id="name1">佐藤さん</li>
   <li id="name2">田中さん</li>
   <li id="name3">加藤さん</li>
</ul>

<script>
    $("#name1").remove();
    $("#name_list").children().remove();
    $("#name1").empty();
</script>
</body>
```
`` $("#name1").remove();``を実行した場合は`<li id="name1">佐藤さん</li>`の要素が削除され下記のようなHTMLになります。
```html
<body>
<ul id="name_list">
   <li id="name2">田中さん</li>
   <li id="name3">加藤さん</li>
</ul>
</body>
```

`` $("#name_list").children().remove();``を実行した場合は下記のようなHTMLになります。
```html
<body>
</body>
```

`` $("#name1").empty();``を実行した場合は`<li id="name1">佐藤さん</li>`の要素の中身が削除され下記のようなHTMLになります。
```html
<body>
<ul id="name_list">
   <li id="name1"></li>
   <li id="name2">田中さん</li>
   <li id="name3">加藤さん</li>
</ul>
</body>
```

# 最後に

上記は基本的な要素追加、削除方法です。この他にも様々な方法がありますのでその都度自分がやりたいことにあった方法を探していきましょう！
git実装前に確認したい概要や用語

## 初めに
どうも。python歴3か月で転職を目指す者です。

今回は前々から聞くgitとgithubについて簡単にまとめてみました。
やれ実務で必須だのチーム開発で必須だのよく聞きますからね。
印象としては複雑で難しく感じました。なので忘備録と復習用に記事を作成致しました。

### 関連記事
- [(コマンドプロンプト)cmd解説](https://qiita.com/The_Boys/items/50d608f139e04f157d19)：gitは基本的にCUIを使用します。以前作成したこちらの記事がお役に立てば幸いです。

- [とりあえずgit実装](https://qiita.com/The_Boys/private/aa85e1d6d107b5802f73)：次の記事です。とにかく触ってみる､分かりやすくを意識して作成致しました。


# 学習

今回は基本的な役割や用語をまとめて行こうと思います。
大枠をまず掴んでおくとイメージしやすいですから。
実装は次回の[こちら](https://qiita.com/The_Boys/private/aa85e1d6d107b5802f73)です。


## gitとは

データをのバージョンを管理できるツールです。主な特徴として、

- ファイルのバージョンの管理
ファイルの数を増やさずに、現在のバージョンを残しつつ、最新の状態を記録する事が出来ます。


- 以前の状態に戻す事も可能。
上書き保存しても変更前と変更後の違いを可視化する事が出来ます。具体的には｢いつ、誰がどんな変更をしたか｣を記録し、以前のバージョンに戻すことも可能です。


- チームでデータを扱う際に重宝。
オンライン上で複数人の共同編集でも上記の機能がありますので、事故(思わぬ上書き)を防ぐ事が出来ます。


## githubとは
gitで管理されているデータをネット上に公開して共有することができるプラットフォームです。

- gitデータの簡単な共有

gitはデータの変更履歴やバージョンを確認するもので、それをチームで簡単に共有することができます。データはオンライン上で保存や公開も可能です。
方法としてはローカルでgitにてリポジトリを作成し、その後にgithubにアップし公開する事で共有します。是非チーム開発に役立ててください。

①git hubからリポジトリを取得
②変更や修正
③git hubへアップロード

- OSSである。

<font color="red">オープンソースソフトウェア</font>：ソースコードが公開され、誰でも改変、再配布が可能なソフトウェア。
公開されている世界中の優れたコードを確認し勉強する事が出来ます。更にリクエストを送り改善案を出す事も可能です。





## gitの抑えるべき用語とポイント。





- リポジトリ

ファイルやフォルダの保存場所の事です。。
その保存場所単位(リポジトリ単位)で変更/追加/削除を行い記録し履歴をためる事が出来ます。
保存していないリポジトリをuntracked fileといいます。
<font color="red">リモートリポジトリ</font>:サーバー上にある保存先。Github上に作成し管理。
<font color="red">ローカルリポジトリ</font>:自分のパソコン上にある保存先。自分のPCに作成し管理。


- コミット

データを記録する事。

- ブランチ

履歴の流れを分岐して記録していく手法です。分岐したブランチは他の影響を受けないため、同リポジトリ中で複数の変更を同時に進める事が可能です。下記の画像でイメージが湧くでしょうか。

![2022-07-16_04h33_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/46c2b774-c050-e951-9d47-05a0ed818e40.png)

合っているか分かりませんが、｢複数のセーブデータを保存できるゲームソフト｣と私は認識しております。長男､次男､三男が同じゲームソフトで個別のセーブデータで遊んでいる…みたいな。
masterやmarge等含めるとこの例えは少しズレますが…(笑)



- マージ

ブランチに対して別のブランチで変更した結果を取り込むこと。
ブランチで分離し、ある程度目途が立ったら、他ブランチと合流(マージ)して、一つのブランチにまとめ直す。


使用例として、
1. 他のメンバーの作業の影響を受けないように、<font color="red">メインのブランチから自分の作業専用のブランチを作成する。</font>

1. 作業の終わったメンバーは、<font color="red">メインのブランチに自分のブランチの変更を取り込んでいきます。</font>
このようにすることで、他のメンバーの作業による影響を受けることなく、自分の作業に取り込むことができます。また、作業単位で履歴を残すことで、問題が発生した場合に原因となる変更箇所の調査や対策を行うことが容易になります。


![2022-07-16_04h36_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2719278/93f35d79-8666-d07f-0cb3-3ca837cb899b.png)


## まとめ

この記事をご覧になっている時点でgitやCUIから逃げられない職種だと存じます。
私もいまだに苦手意識が強いですが是非上手に泳げるようになる為にまずは一緒に溺れましょう。
不慣れ故色々間違いもあるかと存じますが、その点はコメントや訂正依頼を頂けますと幸いです。

前提知識としてはこのくらいでOKで、とにかく触ってみようかと。
習うより慣れろという事で実装してみましょう。
実は実装の記事も作成しました。⇒[こちら](https://qiita.com/The_Boys/private/aa85e1d6d107b5802f73)









### 参考サイト

https://www.youtube.com/watch?v=6SLMB7BPG9E

https://www.youtube.com/watch?v=WHwuNP4kalU

https://www.itmanage.co.jp/column/oss/

https://u-tan-web.com/git-repository/

https://backlog.com/ja/git-tutorial/stepup/01/

↑特におすすめ。
【備忘録】AWS CloudShellを利用してEC2に接続する際のセキュリティグループについて

## はじめに

普段EC2に接続する際にセッションマネージャーやローカルでTera Termを使用していましたが、以前AWS CloudShellを利用してEC2に接続する際に、セキュリティグループのソース設定で困った時があったので備忘として残します。

---

## CloudShellって何？

AWS CloudShellは、ブラウザーベースの事前認証済みシェルで、AWSマネージメントコンソール上から簡単にシェルを実行出来るサービスです。
シェル(Bash, PowerShell, Z Shell)やAWS CLIがインストールされているため、各コマンドラインツールが利用可能です。

詳細については、下記の**ユーザーガイド**を参照してください。
[・Web版](
https://docs.aws.amazon.com/ja_jp/cloudshell/latest/userguide/welcome.html)
[・PDF版](https://docs.aws.amazon.com/ja_jp/cloudshell/latest/userguide/awscloudshell.pdf)

---

## やってしまっていたこと

普段EC2接続を設定する際に、ローカルでTeratermやセッションマネージャーを使用していたので、セキュリティグループのインバウンドルールのソースをマイIPを設定していました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2411418/59237b62-c372-0f02-e72f-98907fc9f44a.png)

しかし・・・CloudShellでEC2に接続しようとすると以下のように出てしまい接続できません！

```
[cloudshell-user@ip-10-0-XXX-XXX ~]$ ssh -i .ssh/XXXX-key.pem ec2-user@XX.XX.XX.X
ssh: connect to host XX.XX.XX.XX port 22: Connection timed out
```

---

## 原因は・・・

<font color="Red">**セキュリティグループのソースにCloudShellのグローバルIPを指定してないことが原因でした！！**</font>
そうです・・・CloudShellを使用するには、**ローカルのグローバルIPではなく、Cloud ShellのグローバルIPをセキュリティグループに設定する必要がありました。**

おバカでした・・・自分！！

---



## 再度やってみよう


てことで、気を取り直して<font color="Red">**CloudShellのグローバルIP**</font>を確認していきましょう！

以下コマンドでグローバルIPアドレスを確認します。
```
[cloudshell-user@ip-10-0-XXX-XXX ~]$ curl http://checkip.amazonaws.com/
XX.XX.XX.XX　←グローバルIP
```

<br>

上記で表示されたグローバルIPをセキュリティグループのソースに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2411418/be1d2e31-33f8-72c3-2a9a-4b2cc887f1f5.png)


**これでセキュリティグループがうまく設定されて問題なくCloudShellを利用してSSH接続が可能になります！**

---

## さいごに

セキュリティグループのソース設定次第では不正アクセス等の原因となってしまいます。
重要な部分となりますので、理解を深めて必ず押さえておきたい部分ですね！

---
t4g(Armベース)のEC2にunisonを入れて2台間で同期をとる

t4gは安くて早いのでどんどん使っていこう！と思っていたのですが、Armベースなので、バイナリパッケージがない場合があり、ちょっと一般的ではないものを入れようと思うとちょっと大変な場合があります。
というわけで、unison を入れるのに手こずったので、記録を残そうと思います。
ちなみに unison は、サーバー間のファイル同期とかができるツールです。
https://www.cis.upenn.edu/~bcpierce/unison/

# opam のインストール

unison は OCaml という言語で書かれていて、opam は OCaml のパッケージマネージャーだそうです。
というわけで、まず opam をインストールします。

[opamのインストール手順](https://opam.ocaml.org/doc/Install.html)に書いてあるように、スクリプトを実行します。

```shell
$ sudo bash -c "sh <(curl -fsSL https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh)"
```

途中、どこにインストールするか聞かれて、デフォルトだと /usr/local/bin/opam ができます。

# その他に必要なものをインストール

opam を使い始める時に、`opam init`を実行するのですが、必要なコマンドがないと言われるので入れておきます。

```shell
$ sudo yum install patch
$ sudo amazon-linux-extras install epel
$ sudo yum install bubblewrap
```

unison をインストールする時に必要なものも入れておきます。

```shell
$ sudo yum install gtk2-devel
```

# opam 初期設定

```shell
$ opam init
```

実行したユーザーの `~/.opam/` 以下に色々できます。
最後に
>`# Run eval $(opam env --switch=default) to update the current shell environment`

と出ているので実行します。
```shell
$ eval $(opam env --switch=default)
```

# unison インストール

```shell
$ opam install unison
```

最後に
>`# Run eval $(opam env) to update the current shell environment`

と出るので実行します。
```shell
$ eval $(opam env)
```

`unison -version` を実行して、unison と ocaml のバージョンが出たらインストールできています。

```shell
$ unison -version
unison version 2.52.0 (ocaml 4.14.0)
```

これで unison が使えるようになった！…と思ったのですが、EC2の2台間で同期するには ssh する必要があって、`~/.opam/` 以下に実行ファイルがあって、`~/.bash_profile`の設定でパスを通しているので、
```shell
ssh 同期先サーバー
(接続後)
unison -version
```

は実行できても、

```shell
ssh 同期先サーバー unison -version
```
では実行できません。(sshの設定については割愛します。)

というわけで、実行ファイルを `/usr/local/bin/` にコピーします。

```shell
$ which unison
~/.opam/default/bin/unison
$ sudo cp -i ~/.opam/default/bin/unison /usr/local/bin/
```

私は2台それぞれで同じようにインストールしたのですが(ちなみに2台に同じバージョンのunisonを入れる必要があります)、1台にインストールして、できた unison ファイルをもう1台にコピーするので良かったのかもしれません。(未確認ですが)

# 同期の設定

同期を実行するユーザーの `~/.unison/` に設定ファイルを作成します。(片方のサーバーのみ)

```~/.unison/hoge.prf
# times ファイルの時刻をコピー元のままに保存する
times = true

# prefer newer 新しい方を優先する
prefer = newer

# ルート・ディレクトリの指定
root = /path/to/testdir
root = ssh://同期先サーバー//path/to/destdir

# 無視するディレクトリやファイル名を指定
ignore = Path */cache/*
```

設定したファイルを実行してみます。

```
$ unison -batch hoge
```

実行したサーバーの `/path/to/testdir` と同期先サーバーの `/path/to/destdir` が同期されたら成功です。
定期的に同期したい場合は、crontabなどで実行します。
macにpyAudioを入れるときに"portaudio.h"が見つからないと言われる

詰まったのでメモしておきます。

こんなエラーが表示されます。

```Shell
src/_portaudiomodule.c:29:10: fatal error: 'portaudio.h' file not found
      #include "portaudio.h"
               ^~~~~~~~~~~~~
      1 error generated.
      error: command '/usr/bin/clang' failed with exit code 1
```

# 解決方法

pipとhomebrewが最新であることを確認してください。

その後以下を実行

```Shell
brew install portaudio
```

その後以下を実行

```Shell
pip install --global-option='build_ext' --global-option="-I$(brew --prefix)/include" --global-option="-L$(brew --prefix)/lib" pyaudio
```