OmniAICreator/Qiita-1.07M huggingface dataset:
Open AI APIを利用したFine-tuning

# 概要
Open AI APIのモデルを自分好みにチューニング（調整）するFine-tuningを行う。
Microsoftは微調整と呼んでいるが意味としては同じである。
ChatGPTでも入力に学習させたいデータを入れることで性格や情報を記憶させることは可能だが、この方法だと情報が再学習されたり使用されるトークン数が膨大になったりするため、簡易的なものに過ぎず業務利用も現実的では無い。

# 環境構築
以下の環境でFine-tuningを行う。
Windows11
Python3.9


前提として、pythonのインタープリターとpipをWindowsのpathに通しておく必要がある。
環境によって詳細は異なるが、python.exeとpip.exeが存在するディレクトリをPathという名の環境変数に設定しておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3475862/6317bc8a-1eb1-18ea-cd9c-5aefb7122822.png)

openaiのPythonライブラリをインストールしておく。
``` cmd:cmd
pip install openai
```


# Fine-turning
まずはモデルに学習したい情報を整理する。
OpenAI APIでは入力をprompt、出力をcompletionと呼び、これらの組み合わせを学習していくことになるので、対になるように整理しておくと楽になる。
学習させる情報はjson形式でもCSVでも入力できる。ただし、体裁があっていないとAPIに怒られてしまう。以下に今回学習に使用するデータを示す。文字コードはUTF-8指定。本当は数百の情報を入れないと実用性が無い。
``` csv:tune.csv
prompt,completion
こんにちは,はいはい
テスト,おいしいごはんです。
トヨタ,世界一の完成車メーカー
アンパンマン総合ランキング1位,てんどんまん
```
``` json:tune.json
[
    {
        "prompt": "こんにちは",
        "completion": "はいはい"
    },
    {
        "prompt": "テスト",
        "completion": "おいしいごはんです。"
    },
    {
        "prompt": "トヨタ",
        "completion": "世界一の完成車メーカー"
    },
    {
        "prompt": "アンパンマン総合ランキング1位",
        "completion": "てんどんまん"
    }
]
```
（トヨタさんすみません。やなせたかしさんすみません。）

ファイルさえ作ることができれば、あとはコマンドを叩くだけで完了する。

``` cmd:cmd
openai tools fine_tunes.prepare_data -f tune.json
```
これにより学習に使用可能なjsonl形式に変換される。
tune_prepared.jsonlというファイルが作成されるので、次のコマンドで実際に学習される。
``` cmd:cmd
openai api fine_tunes.create -t tune_prepared.jsonl -m ada  
```
モデルはada, babbage, curie, davinciから選ぶことができる。
このコマンドは途中で中断されてしまう可能性が高く、APIからもコマンドを再開できる以下のようなコマンドを案内される。
``` cmd:cmd
  openai api fine_tunes.follow -i <一意のID>
```
これを何度か叩くと学習が完了する。（10回くらい叩いてもダメだったので、30分くらい待ってから実行したらいけました。）
最後に次のようなコマンドが案内される。
``` cmd:cmd
openai api completions.create -m davinci:<今回作成したモデル名> -p <YOUR_PROMPT>
```

YOUR_PROMPTの部分に好きな語句を入れると返答が返ってくる。
``` cmd:cmd
openai api completions.create -m <今回作成したモデル名> -p アンパンマン総合ランキング１位は何ですか？
どんどん世界に広が
はいはいどんどんまん
openai api completions.create -m <今回作成したモデル名> -p トヨタとはどんな企業？
トヨタとはどんな企業？世界一の完成車メーカー
openai api completions.create -m <今回作成したモデル名> -p こんにちは
こんにちは）はいはいはいはいはいはいは
```
正直まともな日本がどうか怪しいレベルである。。。
かなり精度は低いが、それとなく学習させたデータを返そうとしていることは伺える。

# まとめ
今回はOpenAI APIを利用してdavinciモデルにFine-tuningを実施した。
davinciモデルに4組のプロンプトしか学習させていないためか、ChatGPTのGTI3.5やGPT4モデルのようなレベルの会話を実現することはできなかった。
しかし、学習させたデータが出力に影響することを確認することができた。
これを使用して独自のデータや個人的な情報をFine-tuningさせることで、オリジナルのAIを容易に作成することが可能となる。
さらにAPIを使用しているので入力が再学習される恐れがなく、業務での使用も期待できる。
【できなかった】Automatorを使用して、ChatGPTで文字生成→Twitter投稿させる

# やりたかったこと
1. ChatgptのWebページにアクセスし、プロンプトを入力
1. 操作を用いて、プロンプトの内容をコピー(できなかった)
1. それを、Twitterに投稿させる。（文字列がクリップボードにコピーさえできればいけそう。）


# 以下の内容をもとにすすめていく。

https://alwayscrazy.me/mac-autoplay-tips/

Webページ内でクリックなどを実行し、ボタンを操作させるには、GoogleChromeだと反応しなかった。
そのため、Safariで起動させる必要がある。また、AutomatorでWebページを自動起動させるとデフォルトのブラウザが立ち上がるため、デフォルトのブラウザをSafariに変更する事。

参考サイト

https://qiita.com/gittokkunn/items/b9a88a9dde8f752f16dc

macのAutomatorを起動させる。その後、ワークフローを選択する。

![スクリーンショット 2023-06-11 22.35.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/c9eaa257-4901-6835-2d4b-ff6a88cc0261.png)
そして、今回は以下のように設定した。
1. 指定したURLを取得
    1. ここで、chatgptのログインページのアドレスを貼る。
1. Webページを表示
1. その後、右上の赤丸である記録を押す。
1. その後、やりたい操作を行う。
    1. ここでは、chatgptのフォームをクリックする。
    1. プロンプトを入力する
    1. 選択してコピー（できなかった。トリプルクリックも試したが、うまく選択できなかった。特定のテキストのみ選択できるのみだった。）
    1. `Comannd + C`でコピーをする。
1. 指定したURLを取得
    1. ここで、Twitterのリンクを指定する。
1. Webページを表示
1. その後、もう一度赤丸の`記録`を押す。
1. そして、やりたい操作を行う。
    1. Ctrl＋dでペーストする。その後、Tweetボタンを押す。
![スクリーンショット 2023-06-11 19.12.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/504c0d3a-9d3d-b3c7-51ea-043d401c35f9.png)
しかし、おそらくコピペのところでうまくいかなかった。Webページをテキストで取得しようと思ったのだが、なぜかログイン画面の時のテキストしか取得できなかった。
また、ユーティリティのクリップボードのコピーを試したのだが、なぜか元のTwitterのURLをコピーしていた。
![スクリーンショット 2023-06-11 22.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/ebcda2a4-b797-ae14-d611-50b72f227221.png)


# 結論

- youtubeの動画を自動再生するとか、クリックのみで完結する操作をするのであれば使えそう。
- ドラッグ＆ドロップはautomatorでは使えない
- chatgptの内容を逐次ファイルに出力できれば、まだ希望はあるかも。



# 参考サイト

https://alwayscrazy.me/mac-autoplay-tips/

https://qiita.com/gittokkunn/items/b9a88a9dde8f752f16dc
「EASING解析」動画の補足

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

EASING解析の動画を見たものの、数学が苦手なので、せっかくの説明が理解できなかった。
理解できなかったところを調べたので、書き残したいと思います。
同じような人の参考になれば。

## 理解できなかったところ

動画の中で、下記の式変形をするのですが（2:38～）、なぜそうなるのかが分からなかった。
これは「特性方程式」と呼ばれるものを使った解き方らしく、
「特性方程式」で検索すると、解説した動画が出て来る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/69253503-a21f-b7ea-e654-e72cc58b5b29.png)

まず、4行目が5行目に変化するところが分からなかったけど、
これは、等比数列の公式 $q_n=q_1r^{n-1}$ に当てはめているだけだった。
※ただ、$(1-\alpha)^n$ のところは $(1-\alpha)^{n-1}$ ではないかと思う。

``` math
P_0=0という条件が最初に与えられているので、\\
```
``` math
q_0=P_0-s=0-s=-s\\
```
``` math
r=(1-\alpha) より\\
```
``` math
q_n=-s(1-\alpha)^{n-1}
```

また、下から2行目（※右側）の $s=T$ が分からなかったけど、
これは、上から2行目の $s=(1-\alpha)s+T\alpha$ を展開して整理しているだけだった。

``` math
s=(1-\alpha)s+T\alpha\\
```
``` math
s=s-\alpha s+T\alpha\\
```
``` math
s-s=-\alpha(s-T)\\
```
``` math
0=-\alpha(s-T)\\
```
``` math
0=(s-T)\\
```
Pugが微妙な件について

## はじめに
HTMLテンプレートエンジンPugについて微妙だと思ったところを羅列していきます。
モダンなフロントエンド開発をやっている現場ではJSXやTSXなどを使って、JavaScriptでHTMLを書いているのでしょうが、そういう現場ではない限り効率よくHTMLを書く方法について考えた結果、Pugを使っているところもあると思います。

Pugを使っている方、これから使おうとしている方に向けて、Pugのこういうところがダメだよね、もっとPugを生かす方法はないかな？と問題提起する目的や自分の中の課題感の備忘録として残しておきます。

## pug-cliの開発が止まっている
Pugはnode.jsモジュールなので、npm installで持ってこようとするとインストール自体はできるのですが、パッケージマネージャの脆弱性診断に引っかかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2520632/9ae626c0-2823-4314-a327-3c37f4068153.png)


脆弱性自体はpug-cliをインストールするときに古いpugが入ってしまうようで、package-lock.jsonにpugのバージョンを手動で変更すればいけるみたいですが、そもそもそのバグが公式で直されてない。

GitHubを見ると2017年で更新止まってますね。
https://github.com/pugjs/pug-cli

※ちなみにpugとpug-cliは違います。pug単体だとJavaScript上で使うモジュールで、pug-cliはpugファイルをhtmlファイルにコンパイルするのにpugコマンドが使えるというもの。

## CSS Peekが使えない
IDEのプラグインでHTMLファイル上からCSSクラスの定義が参照できる「CSS Peek」というものがあります。
このプラグインはわざわざCSSファイルを参照してどういう定義がされているのか見に行く必要がなくなるので、非常に開発が捗ります。

しかし、Pugを利用している場合このCSS Peekが使えません。
そのため、ここにこのCSSクラス使われているけどどういう定義がされているのかな？と思ったときにCSSファイルを見に行くか、サーバーを立ち上げてディベロッパーツールで見るしかありません。

## 他に代替手段がある
Pugの魅力は「HTMLコンポーネントのモジュール化」「インデントによる記述の簡易化」「閉じタグなしによる見やすさ」などがあるのですが、モジュール化であればJavaであればThymeleafが使えるし、記述の簡易化ならEmmetを使えばいいんじゃないか？っていうことであまり旨味がないように思えてきます。

## 単純に手間が増える
HTMLファイルにコンパイルしないといけない、Pugファイル分の管理リソースが増えるといった単純に手間が増えるというデメリットがあります。

それにHTMLファイルを直に編集して、Pugファイルと差分が生まれるリスクもあります。
あとはPug自体は単純なのでそんなに覚えることはないのですが、学習コストは少なからずあります。

## 最近Pugの名前を聞かなくなった・・・
Reactなどのモダンフロント開発が流行っている昨今、Pugの名前はあまり聞かなくなった気がします・・・。
私自身、普段技術書籍やIT勉強会やUdemyなどの学習サイトをよく覗いているのですが、どこも最近はPugを扱っていません。

人が使わなくなっていくと、そのツール自体どんどん廃れていくので今後Pugの将来を考えるとあまり先行きはよくないのかなって感じます。

## まとめ
バックエンドを気にしないWebデザインの制作会社であればまだまだ使っていそうですが、開発の現場ではもうあまり使わないのかなっていう所感です。使うとしても開発初期のモックアップとかでしょうか？

モックであればコンパイル後のHTMLを使っていけば、Pug自体はすぐに捨てられるのでリスクはありませんが、継続的に使うとしたらいつかPugの代替を探すときに苦労するかもしれません。それよりかは最初からもうHTMLで書いたほうがいいのではと感じました。
【SwiftUI】座標から都道府県名を取得する

# はじめに
座標で記述された場所を人間が読める住所または地名に変換するプロセスを逆ジオコーディングというらしいです。
今回はその逆ジオコーディングをSwiftで行います。

# 実装
```swift
import SwiftUI
import MapKit

struct ContentView: View {
    @State private var administrativeArea: String?
    var body: some View {
        Text(administrativeArea ?? "取得中")
            .task {
                self.administrativeArea = await fetchAdministrativeAreaFromLocation(location: .init(latitude: 35.61311498476422, longitude: 140.11382960595478))
            }
    }
    
    private func fetchAdministrativeAreaFromLocation(location: CLLocation) async -> String? {
        let location = CLLocation(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
        let geocoder = CLGeocoder()
        return await reverseGeocodeLocationWithGeocoder(geocoder, location)
    }

    private func reverseGeocodeLocationWithGeocoder(_ geocoder: CLGeocoder, _ location: CLLocation) async -> String? {
        await withCheckedContinuation { continuation in
            geocoder.reverseGeocodeLocation(location) { placemarks, _ in
                continuation.resume(returning: placemarks?.first?.administrativeArea)
            }
        }
    }
}
```

:::note info
今回は千葉駅の座標を使用します
`35.61311498476422`,`140.11382960595478`
:::

千葉県と表示されました
![simulator_screenshot_1CA485B0-AFD0-4BB8-BDD5-350CA185412A.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/98505b82-1ea1-4301-606e-595daf78f6e0.png)

# おわり
MapKitを使用することで簡単に座標から都道府県名を取得することができました
RenderのDockerコンテナは起動時に/tmp/をクリアする（ようになった）


# 発端

授業でCI/CDを紹介するために，dockerコンテナを[render.com](https://render.com/)にデプロイする最小のサンプルコードを作っていました．

```dockerfile:修正前のDockerfile
FROM python:slim

COPY requirements.txt /tmp/
RUN pip install -U pip \
    && pip install -r /tmp/requirements.txt

COPY main.py start.sh /tmp/
RUN chmod +x /tmp/start.sh
CMD [ "/tmp/start.sh" ]
```

中身はそれほど大したものではなく，

- ホストのファイルをコンテナの`/tmp/`にコピーして
- pip installやスクリプトを実行をする

だけのものです．

これをテストしてたときには問題なく動いていたのに，授業当日（というかオンデマンド用に録画してた時）に，以下のエラーが出てデプロイできず．．．

```text:renderのイベントログ
Exited with status 128 while running your code.
Check your deploy logs for more information.
```

当然ログには何も表示されず（renderはログが出ないことで有名），原因特定には数時間を費やすことに．対面授業中のデモとしてやってなかったのが不幸中の幸い．


# 試行錯誤と原因

ローカル環境ではおなじdockerfileで何も問題なく動くため，最初は全く原因が分からず．

docker imageを変えてもだめ，でもCMDを変えると動く．ふと思いつき，CMDに`pwd`や`ls`を入れて挙動やファイル構造を確認すると（これならrenderのlogに出てくる），`/tmp/`にコピーしたはずのファイルが，renderでデプロイしたときに消えているらしい．

# 対策

ということで，dockerfileのCOPY先を，`/tmp/`ではなく`/root/`にしたら，問題なく動作しました．

```dockerfile:修正後のDockerfile
FROM python:slim

COPY requirements.txt /root/
RUN pip install -U pip \
    && pip install -r /root/requirements.txt

COPY main.py start.sh /root/
RUN chmod +x /root/start.sh
CMD [ "/root/start.sh" ]
```

まあたしかに`/tmp/`は起動時にクリアするのは妥当だけれども，ローカルのdockerコンテナはそんな挙動はしていないし，修正前のものはrenderでも1ヶ月前は問題なく動いていたのに．
Google Cloudアップデート (5/25-5/31/2023)

- v1:
  - BigQueryのパーティションテーブルへエクスポート/ストリームするのがGAになったよ！

### VMware Engine: [May 25, 2023](https://cloud.google.com/vmware-engine/docs/release-notes#May_25_2023)
- europe-west12で使えるようになったよ！

### GKE: [May 26, 2023](https://cloud.google.com/kubernetes-engine/docs/release-notes#May_26_2023)
- Observabilityタブでephemeral storageの情報が取れるようになったよ！

### VPC: [May 26, 2023](https://cloud.google.com/vpc/docs/release-notes#May_26_2023)
- private.googleapis.comとrestricted.googleapis.comをIPv6でアクセスできるのがGAになったよ！
- Private Service Connectバックエンドがexternal regional TCP proxy LB経由でサービスにアクセスできるのがプレビューになったよ！

### Dataproc: [May 24, 2023](https://cloud.google.com/dataproc/docs/release-notes#May_24_2023)
- 2.0, 2.1でCloud Storage Connectorをバージョンアップ
- 権限がない呼び出しがこれまで404だったのが403になったよ
- ワイルドカードを使ってpersistent history sreverを設定した場合のエンドポイントURLが誤っていたのを修正したよ

### Cloud Storage: [May 26, 2023](https://cloud.google.com/storage/docs/release-notes#May_26_2023)
- JSON APIのresumable uploadの最後のリクエストにカスタムメタデータを追加できるようになったよ！

### Cloud Bigtable: [May 29, 2023](https://cloud.google.com/bigtable/docs/release-notes#May_29_2023)
- クライアントライブラリ更新

### Chronicle Security: [May 25, 2023](https://cloud.google.com/chronicle/docs/release-notes#May_25_2023)
- 7/1/2023以降に`udm_events`テーブルが`events`にリネームされるよ
  (既に新しいテーブルで使えるのでマイグレしてね)

### Cloud NAT: [May 30, 2023](https://cloud.google.com/nat/docs/release-notes#May_30_2023)
- Standard Tier egressがプレビューになったよ！

### Cloud Healthcare API: [May 30, 2023](https://cloud.google.com/healthcare-api/docs/release-notes#May_30_2023)
- v1:
  - Pub/Sub通知がGAになったよ！
  - `notificationConfig`がdeprecatedになったよ

### Identity Platform: [May 30, 2023](https://cloud.google.com/identity-platform/docs/release-notes#May_30_2023)
- パスワードの強度を設定できる[password policy](https://cloud.google.com/identity-platform/docs/password-policy)がGAになったよ！

### Cloud LB: [May 30, 2023](https://cloud.google.com/cdn/docs/release-notes#May_30_2023)
- external HTTP(S) LBでワイルドカードを利用して処理を切り替えられるのがGAになったよ！

### Cloud CDN: [May 30, 2023](https://cloud.google.com/cdn/docs/release-notes#May_30_2023)
- Global External HTTP(S) LBでワイルドカードを利用して処理を切り替えられるようになったよ！

### Cloud Composer: [May 30, 2023](https://cloud.google.com/composer/docs/release-notes#May_30_2023)
- 7/2023でAirflow CLIを実行するのに新しい権限が必要になるよ！
- web server workerの数がCPUとメモリの割当によって自動で調整されるようになったよ！
- 機能追加とバグフィクス
- composer-2.2.1-airflow-2.5.1, composer-2.2.1-airflow-2.4.3リリース
- 2.0.14, 2.0.13, 1.18.10, 1.18.9がEoLに

### Compute Engine: [May 31, 2023](https://cloud.google.com/compute/docs/release-notes#May_31_2023)
- Managed Instance Groupでラベルやメタデータを新しいインスタンステンプレートを作らなくても変更できるようになったよ！
- CentOS Stream9, 8をインポートできるようになったよ！

### Config Connector: [May 26, 2023](https://cloud.google.com/compute/docs/release-notes#May_31_2023)
- 1.105.0リリース
  - 複数のフィールド追加


### ディスクレーマ
- アップデートの内容はあくまでも個人の理解していることを書いています。正しい内容はリリースノート等を確認下さい。
- リリース内容できるだけ拾うようにしているのですが、抜けていそうなリリースノートは追加するのでぜひ教えて下さい！(一覧[ここ](https://qiita.com/kenzkenz/items/ac18a359b803dbb6b490)で更新してます。)
GoogleSpreadsheetのデータをGoogleCalendarに登録する

いるいろ苦労しながら、とりあえず作ったので世の中に出したかった。

``` createcalevent
/*
  このコード、動くぞ！
 */

function createcalevent() {


  var spreadsheet = SpreadsheetApp.openById('xxxxxxxx'); //スプレッドシートＩＤ
  var sheet = spreadsheet.getSheetByName('cal'); // シート名
  let calendar = CalendarApp.getCalendarById('xxx@google.com'); // アカウント
  let rowmax = sheet.getLastRow();

  console.log('rowmax',rowmax);
  for(var i = 2; i < rowmax+1; i++) {
    //getrange()の行列番号は１スタート
    let yyyy = sheet.getRange(i,4,1,1).getValue(); //なんかここだけletだった。
    var mm = sheet.getRange(i,5,1,1).getValue()-1;
    var dd = sheet.getRange(i,6,1,1).getValue();
    var eventday = new Date(yyyy,mm,dd,0,0,0,0);
        // Dateの月は１月がゼロ扱い。そういう仕様。
        // だから、var mm のところで -1 してる。

    var summary   = sheet.getRange(i,7,1,1).getValue();
    var location  = sheet.getRange(i,8,1,1).getValue();
    var description  = sheet.getRange(i,9,1,1).getValue();
    var option = {
        'location': sheet.getRange(i,8,1,1),
        'description':sheet.getRange(i,9,1,1)
      };
    if(summary != '') {
      console.log(eventday);
      console.log(summary,location,description);   
      var event = calendar.createEvent(summary,eventday,eventday,option);
                  // これは、getCalendarById()を使うことでエラーが出なくなる。
    }
  }
}
```


↓こういう表をつくっておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/793262/33430a07-bf62-03d3-0ffa-ac7f01ec29ec.png)

とりあえずは、うまく動きました。良かったです。
いろいろなサイトを参考にしましたが、記録取れなかったので記載できません。
参考にしたサイトを作成した方、ありがとうございます！
ランダムに一つピックアップするというテスト

## はじめに

最近、TDD・クリーンアーキテクチャについての理解を深めようと、
**麻雀ゲーム**をNextjs + TypeScriptで作り始めました。

「**牌を引く**」というユースケースについてのテストを書こうと思いましたが、

素人である私は、
**ランダムに引かれるものをどうやってテストしたらいいんだ？**
**数千回回して統計を取るのか...??**

という疑問を持ったので記事として残します。

## 結論

**Math.randomのテストはいらない**

これを早く意識するべきでした。
ランダム値を出力する機能は私の用意したものではなく、TypeScript(JavaScript)で用意されているものなので、
ここのテストをあえてする必要はありません。

**Math.randomのモックを用意し、それを使ってしまえばいい**だけでした。

```typescript
// 一旦オリジナルのMath.randomを保存
const originalMathRandom = Math.random

// テスト実行
Math.random = () => 0.2
const tile = tileUsecase.drawTIle()
expect(tile).toEqual(expected)

// 最後に保存しておいたMath.randomをリストア
Math.random = originalMathRandom
```

## 実行コード


<details><summary>(テスト) TileUsecase.test.ts</summary><div>

```typescript: src/__tests__/usecase/TileUsecase.test.ts
import { expect, describe, it, vi } from 'vitest'
import { TileUsecase } from '@/usecase/TileUsecase'
import { Tile } from '@/entities/Tile'
import { TileUsecasePort } from '@/usecase/port/TileUsecasePort'

describe('TileUsecase', () => {
  let tileUsecase: TileUsecase
  const deck: Tile[] = [
    new Tile(1, 'man', '1'),
    new Tile(2, 'pin', '2'),
    new Tile(3, 'sou', '3')
  ]

  it('tileUsecase.drawTile select a random tile from deck', () => {
    const tileUsecasePort = {} as TileUsecasePort

    tileUsecasePort.drawTile = vi.fn(() => {
      const randomNum = Math.floor(Math.random() * deck.length)
      return deck[randomNum]
    })

    tileUsecase = new TileUsecase(tileUsecasePort)

    const originalMathRandom = Math.random
    let tile: Tile

    // 0.2 is the random number of Math.random()
    Math.random = () => 0.2

    tile = tileUsecase.drawTile()
    expect(tile).toEqual(new Tile(1, 'man', '1'))

    // 0.5 is the random number of Math.random()
    Math.random = () => 0.5

    tile = tileUsecase.drawTile()
    expect(tile).toEqual(new Tile(2, 'pin', '2'))

    // 0.8 is the random number of Math.random()
    Math.random = () => 0.8

    tile = tileUsecase.drawTile()
    expect(tile).toEqual(new Tile(3, 'sou', '3'))

    Math.random = originalMathRandom
  })
})
```

</div>

</details>

<details><summary>(ユースケース) TileUsecase.ts</summary><div>

```typescript: src/usecase/TileUsecase.ts
import { TileUsecasePort } from "./port/TileUsecasePort";

export class TileUsecase {
  constructor(private inputPort: TileUsecasePort) {}

  drawTile() {
    return this.inputPort.drawTile();
  }
}
```

</div>
</details>

<details><summary>(ポート) TileUsecasePort.ts</summary><div>

```typescript: src/usecase/port/TileUsecasePort.ts
import { Tile } from "@/entities/Tile";

export interface TileUsecasePort {
  drawTile(): Tile;
}
```

</div>
</details>

<details><summary>(エンティティ) Tile.ts</summary><div>

```typescript: src/entities/Tile.ts
type Suit = "man" | "pin" | "sou" | "zi";

export class Tile {
  constructor(
    private _id: number,
    private _suit: Suit,
    private _value: string
  ) {}

  get id() {
    return this._id;
  }

  get suit() {
    return this._suit;
  }

  get value() {
    return this._value;
  }
}
```

</div>
</details>
Python ＋mysql + mediumtextで文字化け的おかしなことを直す

Mysqlにpythonでつなぐと、varcharなら化けないのにmediumtextにすると化ける（ように見える）

解決策） bytes to str でOK
import mysql.connector
：
contents = row[2]
とすると↓
'\xe3\x81\x82\xe3\x81\x84\xe3\x81\x86'
のようになったら、当該フィールドがなぜかbytesで読み込まれているので
contents = row[2].decode('utf-8')
とすれば文字化けしなくなります。というか文字化けじゃなくてbyteをそのままだそうとしたらそうなるわな。

疑問）なんでvarcharならstrで受けるのに、mediumtxtではbytesになるのかが謎。
Homebrewインストール後に「zsh: command not found: brew」でコマンドを受け付けなかった場合

### 経緯
MacをM2に新調したためHomebrewをインストールした
OSはVentura 13.4
brewの[公式サイト]("https://brew.sh/index_ja.html")に沿って以下コマンドでインストール
使用シェルは`zsh`

```zsh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

インストール後に無事インストールできていたかの確認のため`brew -v`を実行したところ以下エラー

```zsh
zsh: command not found: brew
```

### 解決方法

以下コマンドを実行して解決

```zsh
(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/user/.zprofile
    eval "$(/opt/homebrew/bin/brew shellenv)"
```
※`user`の箇所は自PCのユーザー名が入る

`brew -v`、`which`どちらもコマンドが通るようになり無事パスが通った模様
```zsh
Homebrew 4.0.21
```
```zsh
/opt/homebrew/bin/brew
```

### 補足
brewインストール時に以下のwarningとNext stepsに解決方法が出ていた。それをそのまま実行しただけ

```zsh
Warning: /opt/homebrew/bin is not in your PATH.
  Instructions on how to configure your shell for Homebrew
  can be found in the 'Next steps' section below.
```

```
==> Next steps:
- Run these two commands in your terminal to add Homebrew to your PATH:
    (echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/user/.zprofile
    eval "$(/opt/homebrew/bin/brew shellenv)"
- Run brew help to get started
- Further documentation:
    https://docs.brew.sh
```

# 後日症状再発とその解決方法
後日、nodebrewをインストールしようとbrewコマンドを実行したところまたcommand not foundとなってしまっていた
`source ~/.zshrc`を打った時があったのだが、それがきっかけっぽい
再度、[先に挙げたコマンド]("https://qiita.com/drafts/b2809083af3cf967d70f/edit#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95")を実行すればbrewコマンドが使用できるようになるがsourceコマンドを使用するたびこれになるのは困ると思った

# 解決方法
`.zshrc`にパスを追記して解決

```
vi ~/.zshrc
```
で.zshrc内に入り
以下追記
```
export PATH=/opt/homebrew/bin:$PATH
```
これでsourceコマンドを使用してもbrewコマンドが使用できるようになった
はじめからパスに追記するだけが一番の早道だったかもしれない
AWS CDK初学者が押さえるべき参考リンク

### CDKベストプラクティス

https://aws.amazon.com/jp/blogs/news/best-practices-for-developing-cloud-applications-with-aws-cdk/

### CDKベースライン（BLEA）

https://aws.amazon.com/jp/blogs/news/announcing-baseline-environment-on-aws/

### AWS CDK Conference Japan 2023資料

https://www.docswell.com/s/integrated1453/5GXL7N-AWS-CDK-Conference-Japan-2023#p1

https://speakerdeck.com/mhrtech/cdkconjp2023

https://speakerdeck.com/tmokmss/answering-cdk-faqs
【Laravel】#2 ララベルのディレクトリ構成


# していること
ララベルで実装されたECサイトのリーディングをしていきたいと思います。
使用するオープンソースのURLはこちらです。
https://github.com/jsdecena/laracom

# バージョンの確認
Laravelのバージョンを確認します。
```
root@3471397e79b3:/var/www# php artisan -v
Laravel Framework 8.75.0
```

# ディレクトリ構成
Laravel　8.75のようです。ディレクトリの構成は以下のようになっています。慣れないうちは目当てのファイルを探すのに手一杯ですので、なんとなく覚えておきましょう。
```bash
Laravel 8.75
├── app
│ ├── Console
│ ├── Exceptions
│ ├── Http
│ │ ├── Controllers
│ │ │ └── *コントローラーはここ
│ │ ├── Middleware
│ │ └── Requests
│ └── *モデルはここ
├── bootstrap
├── config
├── database
├── public
├── resources
│ └── views
│ └── *ビューはここ
├── routes
│ └── web.php
├── storage
├── tests
└── vendor
└── laravel
└── framework
└── src
└── Illuminate


```

|ディレクトリ|内容|
|---|---|
|app|アプリケーションのコードやロジックが格納されるディレクトリ|
|└── Console|	コンソールコマンドに関連するクラスが格納されるディレクトリ|
|└── Exceptions|	例外処理に関連するクラスが格納されるディレクトリ
|└── Http|	HTTPリクエストやレスポンスに関連するクラスが格納されるディレクトリ
|└── Controllers|	コントローラークラスが格納されるディレクトリ
|└── Middleware|	ミドルウェアクラスが格納されるディレクトリ
|└── Requests|	リクエストクラスが格納されるディレクトリ
|└── モデルはここ|	モデルクラスが格納されるディレクトリ
|bootstrap|	アプリケーションの起動に関連するファイルが格納されるディレクトリ
|config|	アプリケーションの設定ファイルが格納されるディレクトリ
|database|	データベース関連のファイルやマイグレーション、シードファイルが格納されるディレクトリ
|public|	パブリックアクセス可能なファイルが格納されるディレクトリ
|resources|	テンプレートやビューファイルが格納されるディレクトリ
|└── view|	ビューファイルが格納されるディレクトリ
|routes|	ルートファイルが格納されるディレクトリ
|└── web.php|	Webルートの定義が記述されるファイル
|storage|	アプリケーションのストレージ関連のファイルが格納されるディレクトリ
|tests|	テスト関連のファイルが格納されるディレクトリ
|vendor|	Composerパッケージが格納されるディレクトリ
|└── laravel|	Laravelフレームワークが格納されるディレクトリ
|└── framework|	Laravelフレームワークのコアコンポーネントが格納されるディレクトリ
|└── src|	Laravelフレームワークのコアコンポーネントが格納されるディレクトリ
|└── src	ilminate|フレームワークのコアコンポーネントのソースコードが格納されるディレクトリ
mpmathのnsumを使って無限数列の和を高精度で求める

pythonで使えるmpmathモジュールのnsum関数を使ってみたので紹介します。

**【例題】次の無限数列の和を小数点以下14桁まで求めよ**


```math
1.  \quad \sum_{n=0}^{\infty}\frac{1}{n!} \\
2.  \quad \sum_{n=1}^{\infty}\frac{1}{n^2}
```


### 単純ループプログラム

$1.$に関しては以下のように定義通りに$n=25$までループさせると正解（=eの値）が求まります。

```python
from sympy import factorial, E
print(f"{sum([float(1/factorial(n)) for n in range(25)]) :.14f}")
print(E.n())
#2.71828182845905
#2.71828182845905

```
しかし$2.$に関しては$10^6$までループさせても正解($=\zeta(2) $)にはなりません。

```python 
from sympy import zeta
print(f"{sum([float(1/n**2) for n in range(1,10**6)]) :.14f}")
print(f"{zeta(2).n():.14f}")
# 1.64493306684777
# 1.64493406684823
```

### mpmathのnsum
そこでmpmathのnsumの出番にとなります。この関数の詳細は、以下のリンクにありますが、ポイントは**mp.dps**で求める値の精度を指定できることです。


> [mpmath: Sums, products, limits and extrapolation](https://mpmath.org/doc/current/calculus/sums_limits.html)

今回は**mp.dps=100**として100桁まで求めてみました。例題の$1.$も$2.$も正確に求まっているのが分かると思います。

```python 
from mpmath import *
from sympy import N, E, zeta
prec = 100
mp.dps = prec
print(nsum(lambda n: 1/fac(n), [0, inf]))
print(N(E,prec))
print(nsum(lambda n: 1/n**2, [1, inf]))
print(N(zeta(2),prec))
#2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427
#2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427
#1.644934066848226436472415166646025189218949901206798437735558229370007470403200873833628900619758705
#1.644934066848226436472415166646025189218949901206798437735558229370007470403200873833628900619758705

```

（開発環境：Google Colab）

この考え方はProject Euler Problem 722: Slowly Converging Seriesを解くのに役に立ちます。
【Gradle】gradleから値を埋め込んだファイルを生成し、ソースとして扱う【Kotlin】

# やりたいこと
ビルド時に`gradle`から値を埋め込んだ`Kotlin`ファイルを生成し、それをソースとして扱います。

実際に生成したかったのは、以下のようなファイルです。
文字列で設定されている`"2.15.2-beta0"`などの値は`gradle`から埋め込んだものです。

```kotlin:KogeraVersion.kt
package io.github.projectmapk.jackson.module.kogera

import com.fasterxml.jackson.core.Version
import com.fasterxml.jackson.core.util.VersionUtil

public val kogeraVersion: Version = VersionUtil.parseVersion("2.15.2-beta0", "io.github.projectmapk", "jackson-module-kogera")
```

# やり方
これから紹介する方法は以下の方法を参考に独自の改修を加えたものです。
自分はこの辺りに詳しくないため、より良いやり方などご存じでしたらコメント頂けると嬉しいです。

https://stackoverflow.com/a/74771876/13709749

成果物（`build.gradle.kts`）は以下の通りです。
移行の解説は、この成果物からの抜粋をベースに行います。

https://github.com/ProjectMapK/jackson-module-kogera/blob/a147c49482f53d42104cb3f9ae6b968808d35437/build.gradle.kts

## 1. ファイルを生成するタスクを定義する
まず、以下のようにしてファイルを生成するタスクを定義します。

```kotlin:build.gradle.kts（抜粋）
val generatedSrcPath = "$buildDir/generated/kotlin"

/* 略 */

tasks {
    // Task to generate version file
    val generateKogeraVersion by registering(Copy::class) {
        val packageStr = "$groupStr.jackson.module.kogera"

        from(
            resources.text.fromString(
                """
package $packageStr

import com.fasterxml.jackson.core.Version
import com.fasterxml.jackson.core.util.VersionUtil

public val kogeraVersion: Version = VersionUtil.parseVersion("$version", "$groupStr", "${rootProject.name}")

                """.trimIndent()
            )
        ) {
            rename { "KogeraVersion.kt" }
        }

        into(file("$generatedSrcPath/${packageStr.replace(".", "/")}"))
    }

    /* 略 */
}
```

`generateKogeraVersion`としている部分はタスクの名前で、ここには任意の名前を付けられます。

`resources.text.fromString`の中身は生成結果となる文字列です。
ここで`gradle`タスクのスコープに定義された値を読み取り、埋め込んでいます。

`rename`に設定した値がファイル名、`into`に設定した値が生成先ディレクトリとなります。
生成先ディレクトリに関しては、基本的に`build/generated`の下とすることがお勧めです。
サンプルコードでは`build/generated/kotlin`の下としていますが、既存で生成を行っている場合そのディレクトリに合わせるのも手です。

ここまで設定したタスクを実行することで、ファイル生成を行えます。

## 2. 生成したファイルをソースに加える
1で生成したファイルは、そのままでは`Kotlin`上でソースとして扱われません（既にソースとして認識されているディレクトリへ生成した場合を除く）。
そこで、以下のようにしてソースに加える必要が有ります。

```kotlin:build.gradle.kts（抜粋）
val generatedSrcPath = "$buildDir/generated/kotlin"

/* 略 */

kotlin {
    /* 略 */

    sourceSets["main"].apply {
        kotlin.srcDir(generatedSrcPath)
    }
}
```

## 3. ビルドに連動して自動生成する
以下のように、1で定義したタスクを`compileKotlin`の依存に追加することで、ビルドの前に自動実行することが出来ます。

```kotlin:build.gradle.kts（抜粋）
tasks {
    /* 略 */

    compileKotlin {
        dependsOn.add(generateKogeraVersion)

        /* 略 */
    }

    /* 略 */
}
```
モダール画面が開かなかった時に行ったこと(Bootstrap使用時)

## 起こったこと
HTML・CSS(Bootstrap5)・JavaScriptの学習のためサイト模写をしていたが、ボタンクリックによりモーダル画面を開く処理をJavaScriptで実装したところ、うまくmodalが開かなかった。
例えば、以下のようなコード


```
const modal = document.querySelector('.js-modal');
const modalButton = document.querySelector('.js-modal-button');

modalButton.addEventListener('click', () => {
      modal.classList.add('modal-open');
});

```

上記は、HTMLでクラス属性に"js-modal"と"js-modal-button"が設定されている要素を取得し、ボタンがクリックされた後に前者の要素のクラス属性に"modal-open"を付与するもの。
"modal-open"にはmodalを表示するためのCSSを記述していたが、これがうまくいかなかった。
## 原因
HTMLで読み込んでいたBootstrapのCSSファイルにmodalを装飾するコードがあり、今回自分が作成したCSSやJavascriptのコードと競合してしまったためかと思われる。

```
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
```
## 対策
自分で書いたmodal部分のCSSやJavaScriptのコードの"js-modal"や"js-modal-button"を"js-smodal"や"js-smodal-button"等別の名称に変更し、競合しないようにした。

```
const smodal = document.querySelector('.js-smodal');
const smodalButton = document.querySelector('.js-smodal-button');

smodalButton.addEventListener('click', () => {
  smodal.classList.add('smodal-open');
});

```

これでボタンクリック後にmodal画面が開くようになった。
【QAポエム】「どう対応しても文句は出る」という言葉のインシデント性

:::note info
本記事はとあるソーシャルゲーム開発会社でQA(品質管理)を務める筆者が、
業務を通して考えたこと・感じたことを書き留めるポエム記事です。
あくまでも一事例として捉えていただければ幸いです。
また、守秘のために一部フェイクも含みます。
:::

#### ソーシャルゲームに限った話ではないですが、あらゆるサービスにおいて「全ての顧客の納得」を得ることは大変に難しいことです。

QAという仕事の上でも、「その現象が発生しなくなったら解決」という単純な結末にできない不具合に多々直面いたします。

よくある例としては、
**・本番環境でとある不具合が発生した。**
**・これによりユーザー間で不平等がもたらされた。**
などがあります。
今回はそういった場面をきっかけに私が考えたことを書きたいと思います。

:::note info
ここで言う「**不平等**」とは、プレイ時間、ゲーム内通貨の獲得量など、ゲーム内における何らかの恩恵が全てのユーザーに均等に提供できなかった場合を指します。
※ゲーム外に及ぶ課金などの決済不具合は今回の件とは考え方が異なるため一旦省きます
:::

例えば、PCブラウザ・スマホアプリなど複数のプラットフォームで遊ぶことのできるゲームが「**特定の環境だけ進行できない**」といった不具合に直面すると、ユーザーに提供されるプレイ時間に**差**が生まれます。
この場合、この進行不具合を解消しても不具合発生中に生じたプレイ時間の差は埋まりません。

ソーシャルゲームにおいて「**プレイ時間の差**」は「**ゲーム内の通貨やアイテムの獲得量**」に直結する部分が大半のため、大なり小なりこの手の不平等は発生した時点でユーザー間の心証はよくありません。よってサービス運用者は不具合修正という事態の収束とは別に、ユーザーに向けた何らかの対応を行い「**手打ち**」を目指す必要があります。
しかしここで難しいのが「**個別対応**」が行えるかどうかです。

ゲーム内で発生した不平等を解消するためには、運営側でその生じた差を計測し、マイナス分に何らかのアイテムを補填することで「**お許しいただく**」といった対応が一般的です。

ユーザー視点で見れば
**１．友人たちとラーメンを食べに行くと自分の分だけ煮玉子が切れていた。**
**２．チャーシュー1枚追加で許してと店主に頼まれたため受け入れた。**
みたいな話ですね。

上記のようにひと目で状況の全貌が観測できる場面だと良いのですが、
ソーシャルゲームの運用では不具合の種類によってはその不具合の発生したユーザー全員を漏れなく洗い出すことが非常に困難な状況も多々あります。
（厳密にはそれだけのコストが支払えないという場合も…）

そのような状況において、運営側は「**全体**」に向けた一括の対応を行うことで解決をはかるという選択を取らざるを得ません。ネットスラングでは「詫び石」などと呼ばれることも多い、ゲーム内通貨の配布での手打ちを目指します。

しかしながら、これは「**差**」を解消する対応ではない点を忘れてはなりません。

上記に書いたラーメンの例に照らすと、自分以外の友人たち全員にもチャーシュー1枚追加が行われるような状況ですから、それを受け入れられないユーザーももちろん現れます。

そのため、運営内ではどのような形の全体対応がもっとも多くの納得が得られるかという点を思案します。昨今ではこの手の対応１つで炎上なども起こり得ますから、状況によっては時間をかけた議論も必要です。

※とある有名なタイトルでは、ユーザーにとって差が気にならなくなるレベルの多量のアイテムの補填を行ったことで話題になったこともありました。これはプラスの方向での炎上とも言えます。ただしその出来事以降、他社で何らかの不具合が起きるとこれを見習うように催促さてしまう事例も多くなったことがSNS上でもよく観測されます。

この手の決断には**正解がありません。**
手打ちのためのお詫びには「**適正量**」があることは確かですが、相手取るのはユーザーの「**感情**」です。
議論に議論を尽くした後に決断を下し、対応して様子を見る他ありません。
そんな時、現場では「**どう対応しても文句は出る**」という言葉で決断を後押しすることもがあります。

#### さて、前置きが長くなりましたが、
私はこの「**どう対応しても文句は出る**」という言葉にひとつのインシデントを感じています。
（これは感覚的なものなので、一種の行き過ぎた考えでもあります。）

:::note info
**インシデント**：重大な事件・事故に発展する可能性を持つ行動や要素
:::

当たり前なのですが、ゲーム会社で働いているとゲームの運営チームも人間なのだということを日々実感します。ソーシャルゲームという媒体を通しておりますが、ユーザーというお客様へ人の手でサービスを提供するという形は他の仕事と大きく変わるものでもありません。

客商売、サービス業という分野で近年暴走してしまっている言葉に「**お客様は神様です**」という言葉があります。元々はサービスの提供者側がその心構えを言語化したワードではありますが、主語を広くとれる言葉でもあったことから言葉の使用者が顧客に移り、トラブルに発展している事例が見られます。
私はこういった「**言葉のインシデント性**」というのがこれからの時代にはより強くなっていくのではないかと個人的に考えています。

ゲームの運営チームも人間ですから、個々それぞれに心構えがあり、そして大きな決断にはストレスも感じます。決断は心身に疲労を蓄積させるものですから、社会におけるチーム運用においては個人個人のメンタルケアも重要視されます。
前置きに挙げた不具合に伴うユーザー対応などは実に大きな決断です。自社のタイトルのブランディングにも影響する一姿勢を個人で決めるわけですから、責任もまた重大です。
そんな時、「どう対応しても文句は出る」という言葉は、「**どう対応しても文句は出るものですよ、気負いすぎないほうが良いですよ**」とチームが決断者を**支える言葉**として機能してほしいと私は考えています。
なぜなら、もしこの言葉を決断者本人が乱用し始めた時、決断そのものの背景に考慮の不足や諦めの感情が強く現れ、そのゲームの運用品質は大きく下がる可能性が高いからです。
これが私の思うインシデント性です。

サービス業がネットワークを介しゲームがソーシャルになった今、
QAが考える「品質」とは、「製品の品質」だけでなく「不平等を生じさせるインシデントの抑制」も当然意識していかなければならないものとして存在します。

しかしながら事業としての形態がどれだけデジタルに進歩しても内部で動くのは人間ですから、私としては一歩引いて**会社人としてチームの運用にもインシデント性を見出していく**ことが肝要ではないかと近頃は襟を正す日々であったりします。
VuetifyのComboboxでフォーカスが当たったときに部分一致でフィルタする裏技

## 前提
- Vue2
- Vuetify2
- JavaScript

## やりたいこと
こんな感じのcomboboxがあったとします
```vue
<template>
  <v-combobox v-model="select" :items="items" />
</template>

<script>
export default {
  data: () => ({
    select: "",
    items: ["test1", "test2", "foo", "bar"],
  }),
};
</script>
```
`test` と入力すると部分一致でフィルタがかかり、 `test1` と `test2` が表示されます。
![75dd6fbf891c4a9c13cbc1ed23e9cbc1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265114/253669c0-58f0-ccf9-7d65-476e78c35d46.gif)

しかし、 `test` と入力されている状態でフォーカスが外れた後に再びフォーカスが当たると、フィルタがかかっていない状態になります。
![981955756d98236e86923f31590f82fd.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265114/cddfa463-ec86-25ea-0661-d3cbeb92ce4b.gif)

「フォーカスが当たったときにも部分一致でフィルタしてほしい！」と思ったのに公式にはそのオプションがありません。
ということで、裏技的な感じになりますが、自作してみました。

## 方法
```vue
<template>
  <v-combobox
    v-model="select"
    :items="items"
    :search-input.sync="searchInput"
    @focus="forceFilter"
  />
</template>

<script>
export default {
  data: () => ({
    select: "",
    items: ["test1", "test2", "foo", "bar"],
    searchInput: "",
  }),

  methods: {
    forceFilter() {
      this.select += " ";
      setTimeout(() => {
        this.searchInput = this.searchInput.replace(/\s+/g, "");
      }, 10);
    },
  },
};
</script>
```
comboboxにフォーカスしたときに入力値の末尾にスペースを挿入し、10ms後に末尾のスペースを削除しています。
これによって、フォーカスした際にフィルタがかかります。

※ `setTimeout` して遅延実行しているのは、遅延を挟まないとうまくスペース削除が動作しないからです。ここは適宜調整してください。

これで無事、フォーカスが当たったときにフィルタがかかるようになりました。
![b2f97ce86df0b79d3919de6a8f2c95d6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265114/08d3d57e-f4ac-a281-bfc6-2b62a502ae34.gif)

## 参考
[コンボボックス・コンポーネント — Vuetify](https://v2.vuetifyjs.com/ja/components/combobox)
2つのTBLの差分チェック

# pandasで2つのTBLの差分チェック


AとBの２つの差分を調べる

```
#uniqueかどうかを重複排除して調べる
def is_uni(df):
    return len(df) == len(len.drop_duplicates())
```

## データ作成

```
#ここは適当にしてます
番号と年月をカラムにもつ
df_a = df_a[df_a["年月"] > "202306"]

df_b = df_b[df_b["年月"] > "202307"]

# aの中にbがあるか調査。重複しているものをdf_exに
df_ex = df_a[df_a["番号"].isin(df_b["番号"])]

```

## ソート

```
# aとexを比較する前にソート
df_a = df_a.sort_values(["番号","年月"]).reset_index(drop=True)
df_ex = df_ex.sort_values(["番号","年月"]).reset_index(drop=True)

```

# 差分チェック
```
df_a.equals(df_ex)
→Trueなら完全一致
→Falseなら不一致

```

#不一致ならどこが差分なのかチェックする
```
df_compare = df_a.compare(df_ex)

df_compareを表示で重複している差分をチェックできる
カラムに絞る場合は
df_compare = df_a["番号"].compare(df_ex["番号"])

# ちなみに、index番号で一行表示したい場合は
df[100:101]
などのスライス表記で表示可能
```
脳汁大好きShaderGraphさんでこってりマテリアルを作成しよう

# 概要

脳汁が出そうなShaderGraphの作例紹介です
レシピなのでCookPadを参考にして書いています

作例紹介なので各種ノードの細かい解説は省きます

# 材料

- Unity2021.3.18f1 : 1人前
- UniversalRP 12.1.10 : 1人前
- ShaderGraph 12.1.10 : 1人前　

# 対象の料理人

- Unityでパチンコを打ちたい
- Unityでスロットを打ちたい
- なんでもいいから脳汁を出したい
- ユニコーンで5万発出したい
- 実質勝ちにしたい

# 基本の調理工程

## :one: 脳汁の素を作成します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/a1049dfb-7d26-0a63-8ead-9b5714830212.png)

- Gradientノードを虹色にしましょう
- ここのグラデーションパターンは何個か作っておくと使いまわせるので気に入ったものができたら `New` を押して保存しておくと吉

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/8c19fc27-c9ba-468a-e855-ddd597f6c57d.png)

温かみのある手作業で色付けしたものがこちらになります

## :two: 作成した脳汁の素をトレーに移して時をかけさせます

![2023-06-11-20.26.23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/4367efd1-2d3f-6434-fe2e-bf9406d48760.gif)

SampleGradientノードを使ってグラデーションから色を取り出せるようにします
0~1の範囲でグラデーションをとることができるのでTimeノードをFractionノードに繋いで0~1を繰り返す値をTimeに繋いで色が変わるようにしています

## :three: 配膳します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/f802df05-3996-6178-b8f8-dc6ca8170d9a.png)

BaseColorにこの色を出力すれば基本的な脳汁シェーダーは完成です

## :tada: 完成

このShaderGraphを元にマテリアルを作成して割り当てたものがこちら

![2023-06-11-20.44.05.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/f41af04c-8010-7c00-c760-cb1157e67e6c.gif)

これを適当に並べてポールっぽくしてみたのがこちら

![2023-06-11-20.49.20.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/b42555bc-1b54-2643-da2b-6d2aa2c4d99d.gif)

あとはお好みでBloomを適量でさらにキマります

![2023-06-11-20.53.46.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/894d9a32-ebe3-3964-e844-66a6006342c0.gif)

# 独自レシピのすすめ

## 色が流れて変わるようにしてオーガニックな脳汁の分泌を促します

![2023-06-11-20.59.16.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/465c2b86-bc16-a7c1-c199-110dad3f229b.gif)

ScreenPositionノードの値を取り出して足します

![2023-06-11-21.01.33.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/95b40fbd-28d8-575b-0f25-18fe2d7e84f2.gif)

## 点滅させて脳汁にコントラストを持たせます

![2023-06-11-21.07.07.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/22e6ef38-c548-8e27-d808-a17f4ed4d9e7.gif)

Noiseノードの値を取り出して掛けます

![2023-06-11-21.09.44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/95e2d065-2879-4ccf-4a73-b8ad1254361c.gif)

# 愛は最後にひとつまみ

SampleGradientノードのTimeにどんな値を入れるかで表現が変わるということがなんとなく伝わったと思います
ここでどんな絵柄を出せるのかによっていろんな脳汁を分泌させることができるのでどんどん改造して脳を楽しませてください
よいShaderGraphライフを！

# おまけ

変な絵柄の作例（2Dのやつで作ったので使い勝手が変わります）

## 円状に広がる

![veqxr-1itxz.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/147fa3cf-de19-bebe-290f-2fb06272edb3.gif)

## よくわからん形に広がる

![nkhx5-ttzyz.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/613274/b8ae78fa-9ca3-af91-114d-2210f4349ed5.gif)
小ネタ kubernetes クラスタに手っ取り早く負荷をかける


# これは？

簡易的なk8sクラスタの負荷テストを行うため、stress-ngコマンドを実行するpodのデプロイメントを作成する備忘録

podのimageには[containerstack/cpustress](https://hub.docker.com/r/containerstack/cpustress/tags)
を使わせてもらった。

k8クラスタで負荷テスト・リソースを食いつぶした時の挙動、高負荷時でもUPSが耐えられるか？を確認したかった。

## デプロイメントの作成

`resources:`, `args:`はお好みで

~~~
$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cpustress
spec:
  selector:
    matchLabels:
      app: cpustress
  replicas: 1
  template:
    metadata:
      labels:
        app: cpustress
    spec:
      containers:
      - name: cpustress
        image: containerstack/cpustress
        args: [ "--cpu", "4", "--timeout", "72h", "--metrics-brief" ]
        resources: {}
EOF
~~~

## スケール

お好みで

    kubectl  scale  deployment cpustress --replicas 100
【Python初学者向け】continueとbreakの違い

本記事ではPythonで使うcontinueとbreakの違いについて解説をします。

そもそも用途が違うので、違いを比べられてもと思う方もいるかもしれませんが、Twitterでこの辺の理解が曖昧だという声をいただいたので記事を書くことにしました。

初学者の方への参考になれば幸いです。

## そもそもどこで使うのか？
ループ処理（for文、while文）内で使用する。

## どんな時に使うのか？

continueは値がxの時はループに戻ってねという感じ

breakは値がxになったらループを抜けてねという感じ

実際にコードを見たほうがわかりやすいと思うので、以下にコードを書いていきます。

まずはcontinueから

## continue
簡単なfor文を使ってcontinueについて確認をします。

~~~py
>>> for i in range(1, 6):
...     if i == 3:
...         continue
...     print(i, end = ",")
...
1,2,4,5,
~~~

i == 3 のときにcontinueが実行されますが、**continueは残りの処理をスキップして
次の繰り返し処理に移ります。**

よって、print(i, end = ",")は実行されないため、出力結果は 1,2,4,5,となります。

while文についても同様です。
~~~py
>>> num = 0
>>> while num < 5:
...     num += 1
...     if num == 3:
...         continue
...     print(num, end = ",")
...
1,2,4,5,
~~~


続いてbreakについてみていきます。

## break
こちらも同じように簡単なfor文を使ってbreakについて確認をします。
~~~py
>>> for i in range(1, 6):
...     if i == 3:
...         break
...     print(i, end = ",")
...
1,2,
~~~

i == 3 のときにbreakが実行されますが、**breakは文を囲うもっとも内側のforまたはwhileのループから抜ける**ため、i == 4 以降は実行されません。よって出力結果は 1,2,となります。


while文についても同様です。
~~~py
>>> num = 0
>>> while num < 5:
...     num += 1
...     if num ==3:
...         break
...     print(num, end = ",")
...
1,2,
~~~

## else節がある場合のcontinueとbreak
ループ文はelse節を持つことができます。**for文では反復させる要素を使い切ったとき、while文では条件が偽になったときに実行されます。**

結論から言うと、**continueはelse節が実行されますが、breakは実行されません。**

### for文のelse節
for文のelse節は以下のような実行結果となります。
~~~py
>>> for i in range(1, 6):
...     print(i, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,3,4,5,
else節が実行されました
~~~
#### continue有り
~~~py
>>> for i in range(1, 6):
...     if i == 3:
...         continue
...     print(i, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,4,5,
else節が実行されました
~~~
#### break有り
~~~py
>>> for i in range(1, 6):
...     if i == 3:
...         break
...     print(i, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,
~~~
breakは途中でループ処理を抜けるため、else節が実行されません。
### while文のelse節
while文のelse節は以下のような実行結果となります。
~~~py
>>> num = 0
>>> while num < 5:
...     num += 1
...     print(num, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,3,4,5,
else節が実行されました
~~~

#### continue有り
~~~py
>>> num = 0
>>> while num < 5:
...     num += 1
...     if num == 3:
...         continue
...     print(num, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,4,5,
else節が実行されました
~~~

#### break有り
~~~py
>>> num = 0
>>> while num < 5:
...     num += 1
...     if num == 3:
...         break
...     print(num, end = ",")
... else:
...     print("\nelse節が実行されました")
...
1,2,
~~~
こちらも同様にelse節は実行されません。

## おわりに
continueとbreakの違いについて解説をしました。

Pythonチュートリアル（公式ドキュメント）を使ってPythonの基礎文法を解説しています。
よろしければご活用ください。

https://www.youtube.com/@arika-python/videos

**あわせて読みたい**
資格を取得すると文法基礎を理解していることが証明できます。

https://qiita.com/python_academia/items/eac2bbf831caa610866f


## 出典
公式ドキュメント https://docs.python.org/ja/3.9/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
npm auditのwarningの定義について調べてみた

こんにちは。@Esperna です。

# 背景

npm auditしたときに
```
12 vulnerabilities (6 moderate, 4 high, 2 critical)
```
みたいな出力が出ます。上記の moderate,high,criticalの定義がものすごく気になってます。
[About audit reports](https://docs.npmjs.com/about-audit-reports)を見ると

| 重要度 | 推奨される対応           |
| ------ | ---------------------- |
| Critical   | 直ちに対処してください |
| High     | できるだけ早急に対処してください |
| Moderate   | 時間が許す限り対処してください |
| Low     | お任せします、必要に応じて対処してください |

という記載があり、少なくともCriticalとHighは対処すべきとは思うのですが、
何を以て重要度を判断してるのかが分からず、それを分かってないことは危険ではないか。
例えばModerateの脆弱性が致命傷になることだってケースによってはあるはず。
あるいはそうならないケースもあるのではないかと思って、もう少し詳しく調べてみることにしました。

# npm auditをよく見てみる

以下はとあるリポジトリでnpm auditを実行した時の結果の一部です。

```
% npm audit            
# npm audit report

ajv  <6.12.3
Severity: moderate
Prototype Pollution in Ajv - https://github.com/advisories/GHSA-v88g-cgmw-v5xw
fix available via `npm audit fix`
node_modules/table/node_modules/ajv
  table  3.7.10 - 4.0.2
  Depends on vulnerable versions of ajv
  node_modules/table
```
Prototype Pollutionという単語が出ているので調べてみると、
2018年の記事ですが[Node.jsにおけるプロトタイプ汚染攻撃とは何か](https://jovi0608.hatenablog.com/entry/2018/10/19/083725)が目につきました。
オブジェクトリテラルの__proto__が Object.prototype と同一であることを利用して、
他のオブジェクトのプロパティを書き換えてしまうことができると。
moderateになってますが、普通に恐ろしいなと思いました。

それはさておき、重要なのが以下のGitHubのリンクで、
https://github.com/advisories/GHSA-v88g-cgmw-v5xw
を見てみると、CVE-2020-15366というIDがあります

CVEはCommon Vulnerabilities and Exposuresの略で、IPAによると
個別製品中の脆弱性を対象として、米国政府の支援を受けた非営利団体のMITRE社が採番している識別子だそうで、脆弱性検査ツールや脆弱性対策情報提供サービスの多くがCVEを利用しているそうです(*1)。

また、上記リンク先には

```
CVSS base metrics
Attack vector Network
Attack complexity High
Privileges required None
User interactionNone
Scope Unchanged
Confidentiality Low
Integrity Low
Availability Low
```
という記述がありますが、こちらは[Base metrics](https://www.ipa.go.jp/security/vuln/scap/cvss.html)というものがあり、

* Au ：攻撃前の認証要否 (Authentication):脆弱性を攻撃するために対象システムの認証が必要であるかどうか
* C ：機密性への影響 （情報漏えいの可能性、 Confidentiality Impact ）:脆弱性を攻撃された際に、対象システム内の機密情報が漏えいする可能性
* I ：完全性への影響 （情報改ざんの可能性、 Integrity Impact ）:脆弱性を攻撃された際に、対象システム内の情報が改ざんされる可能性
* A ：可用性への影響 （業務停止の可能性、 Availability Impact ）

などの観点があるようです。Base metricsの計算式は正直よく分かりませんでした。


# 所感

* 結局、npm auditのwarningの定義（というか計算式）はよく分からなかった
* moderate, high, criticalだけでなくCVEの内容まで見たほうが良さそう
* 下記はmoderateではなくhighではなかろうか

```
ajv  <6.12.3
Severity: moderate
Prototype Pollution in Ajv - https://github.com/advisories/GHSA-v88g-cgmw-v5xw
```

# 参考

https://www.ipa.go.jp/security/vuln/scap/cve.html

https://www.ipa.go.jp/security/vuln/scap/cvss.html
[SwiftUI] 子Viewのサイズを取得するChildGeometryReaderを作る

GeometryReaderでは親のViewのサイズを取得することができます。
ですが、子のViewのサイズは取得することができないので、ChildGeometryReaderというものを作っていきたいと思います。

# 実装
```Swift
struct ChildGeometryReader<Content: View>: View {
    @Binding var size: CGSize

    let content: () -> Content

    var body: some View {
        ZStack {
            content()
                .background(
                    GeometryReader { proxy in
                        Color.clear
                            .preference(key: SizePreferenceKey.self, value: proxy.size)
                    }
                )
        }
        .onPreferenceChange(SizePreferenceKey.self) { preferences in
            self.size = preferences
        }
    }
}

struct SizePreferenceKey: PreferenceKey {
    typealias Value = CGSize
    static var defaultValue: Value = .zero

    static func reduce(value _: inout Value, nextValue: () -> Value) {
        _ = nextValue()
    }
}
```
PreferenceKeyを用いて子から親のViewにSizeを通知しています。
`onPreferenceChanges`でSizeの変更を受け取っていて、そのクロージャ内でBindingの値を書き換えることで実現しています。

## 使用方法
```Swift
struct HogeView: View {
    @State var size: CGSize = .zer

    var body: some View {
        ChildGeometryReader(size: $size) {
            FugaView()
        }
    }
}
```
このようにして子のViewのSizeを取得することができました！

# 参考
https://stackoverflow.com/questions/56573373/swiftui-get-size-of-child
情報セキュリティマネジメント試験 受験レポート

## はじめに
CBT方式で受けれるので応用情報の勉強ついでに受けて合格しました。
その受験レポートです。

## 筆者スペック

* エンジニア4年目
* 持っている資格
    * AWS資格 SAA
* 応用情報の勉強中
    * 去年春1次試験落ち(57点)、秋0次試験落ち(起床できず)
* 1年目に基本情報の勉強はした
    * 過去問で豪かくて取れるレベル

## 行った対策
1か月ほど以下の対策を行った。
* 毎日通勤電車の中で過去問道場を行う
    * 最終的に200問ほど実施し正答率は68%だった。
* [予想問題集](https://www.amazon.co.jp/%E4%BB%A4%E5%92%8C05%E5%B9%B4-%E6%83%85%E5%A0%B1%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%9E%E3%83%8D%E3%82%B8%E3%83%A1%E3%83%B3%E3%83%88-%E3%83%91%E3%83%BC%E3%83%95%E3%82%A7%E3%82%AF%E3%83%88%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E4%BA%88%E6%83%B3%E5%95%8F%E9%A1%8C%E9%9B%86-%E6%83%85%E5%A0%B1%E5%87%A6%E7%90%86%E6%8A%80%E8%A1%93%E8%80%85%E8%A9%A6%E9%A8%93-%E5%BA%84%E5%8F%B8/dp/4297132281)を購入し一通り解く

## 結果

合否結果はまだですが685/1000で合格点に達しました。
(600/1000で合格)

## 分野別感想

### 科目A

* 技術系だけでなくマネジメント系や僅かだがストラテジ系も出題される。
    * 筆者はこの分野が苦手なのでその点が苦しめられた
* 予想問題集と過去問道場がかぶっている部分が多かった
    * 科目Aに関しては過去問道場だけで問題ないと感じた

### 科目B

* 応用情報の過去問をこなしている自分には問題文も短くかなり優しく感じた。
    * 過去問も解けていて本番でもこっちのほうが手ごたえがあった。
* 情報セキュリティマネジメントがIPA試験初めての人は難しいのかなと思った。

## 最後に

そこまで時間をかけて取得したわけではないので話すことも少ないです。
この程度の勉強量でも取得できるのだという自身の成長を感じました。
今年こそは応用情報受かるように次はそちらの勉強を頑張ります。
軽量版Windows11のTiny11をためす

軽量版とのことなのでためしてみました。

:::note info
**結論**
特定用途にかぎって使えば軽量の恩恵を得られると思われます。
:::

# Tiny11について
**2GBメモリでも動作可能な軽量化したWindows 11「tiny11」を試してみた**
https://atmarkit.itmedia.co.jp/ait/articles/2303/06/news027.html

# マニュアル
こちらのサイトどおりに行います。

**Tiny11 をインストールして日本語化**
https://pmp-style.hatenablog.com/entry/Tiny11-install

# ダウンロード
こちらにログインしてダウンロードします。b2が最新版のようです。（画面はGoogle翻訳）
https://archive.org/details/tiny-11_202302
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/e8bb456b-6daa-d022-044e-ba73421fc86b.png)

# インストール
ISOからインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/3d6325ed-cdd4-b275-6305-88341692f1b0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/a3ddd40a-1481-91ec-ce69-04c219f281ab.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/280d468a-cdf5-003f-1f87-ae61a605be44.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/b7c88fbf-db51-824b-f1c8-4c31c1360696.png)
最初からオフラインインストールが有効化されていたので便利でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/59eef81b-d373-3adb-3da9-1086ca43e0b9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/b13c91b0-5cef-b79a-e0b3-20ac5c4ee542.png)

# リソース消費量
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/6812cdef-59be-52fe-247e-33729a56a153.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/2ea625eb-c4bf-afa3-a29d-04c43419db8c.png)


1.4～2GB利用しており、ノーマルWin11とそれほど変わりない印象でした。

# WinUpdate & 日本語化
ネット経由で行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/3e869634-eb55-4de4-9d6b-d3d3b9d838bc.png)
ディスクは17GBの使用でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/30e2b310-cb61-2826-76d5-0107363099c9.png)

# まとめ
- インストールや起動が確かに早いので、コンテナ的に単一用途のポータブルなVMを運用したい場合は良いと思います。
- 一部のUIやソフトによっては文字化けがされています。
- 一般的な使い方であれば標準の日本語版ISOがよいと思います。

# 参考にさせていただいたサイト

**Tiny11 をインストールして日本語化**
https://pmp-style.hatenablog.com/entry/Tiny11-install

**2GBメモリでも動作可能な軽量化したWindows 11「tiny11」を試してみた**
https://atmarkit.itmedia.co.jp/ait/articles/2303/06/news027.html
【Rails】ブラウザを閉じてもログインセッションを維持させる

Rails のデフォルトではログインセッションが Cookie で行われるため、ブラウザを閉じるとセッションが終了してしまう。`Active Record`セッションストアを利用してデータベースでセッションを管理することで、ブラウザを閉じてもセッション維持することができるようになる。

## 環境
Ruby 3.1
Rails7
MySQL 8.0
devise（認証ライブラリ）

## Gemfile更新・インストール
```gemfile:Gemfile
gem 'activerecord-session_store'
```

```shell
gem 'activerecord-session_store'
```


## セッションストアの変更
config\initializers\session_store.rb 作成
key: 他者に推測されないような文字列

```rb:session_store.rb
Rails.application.config.session_store :active_record_store, key: 'app_name_sessionxxx', expire_after: 1.week
```
上記の場合、セッションは最後にアクセスされた時点から1週間有効となる。
例）1/1にID/パスワードでログインしたあと、1/5に保存されたセッションを使用して再度アクセスした場合、`セッションの有効期限は更新されます。`つまり、最後のアクセス時刻が1/5に更新され、この時点から1週間後である1/12が有効期限となる。切れたあとは再度ログインが必要になります。

## マイグレーションファイルの作成と実行
```shell
$ rails generate active_record:session_migration

$ rails db:migrate
```

### サーバー（コンテナ）を再起動
現在の年・月・日・時・分・秒を別々で取得する方法

# swift 年・月・日・時・分・秒を別々に取得する
たまに別々で取得したい時があるので、忘れない為に

```
let year = Calendar.current.component(.year, from: Date())
let month = Calendar.current.component(.month, from: Date())
let day = Calendar.current.component(.day, from: Date())
let hour = Calendar.current.component(.hour, from: Date())
let minute = Calendar.current.component(.minute, from: Date())
let second = Calendar.current.component(.second, from: Date())

print(year) // 2023
print(month) // 6
print(day) // 11
print(hour) // 20
print(minute) // 38
print(second) // 7
```

参考までに:relaxed:
新規アプリケーションを立ち上げよう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/218149d2-4e21-28c5-4c82-be344b529401.png)
１：アプリの雛形を作成しよう
①cd ~/projects
②rails _6.0.0_ new pictweet -d mysqlコマンドを実行する。
③cd pictweetコマンド実行する。