OmniAICreator/Qiita-1.07M huggingface dataset:
githubのレポジトリにpushしてもvercelの自動デプロイが走らなかった

### 起こったこと
この度、業務でvercelを利用することになりました。これまでは、個人的に作っていたプロジェクトをvercelにデプロイする程度だったので無料のHobbyプランしか触ってこなかったのですが、今回は複数人で管理するため、Proプランでチームを作成してもらって、そこに参加することになりました。

さて、チームに参加して少し開発を進めてみて気づいたことがありました。自分以外がコミットをプッシュしたりブランチを発行したりした場合は、自動でその差分のデプロイがvercelで行われるのですが、自分の場合はそれが行われていませんでした。これでは不便なので、原因を探しました。

### 原因と対処
gitに設定されているメールアドレスがvercelのアカウントに設定されているメールアドレスと違う、ということが原因でした。(数ヶ月前にアドレスを変更していたのだが、その後戻し忘れていた)

そのため、
```
git config --global user.email [正しいメールアドレス]
```
を実行して正しいメールアドレスを設定したら、自動でデプロイが行われるようになりました。

### 参考
https://vercel.com/guides/why-aren-t-commits-triggering-deployments-on-vercel#correctly-configured-metadata
hasura graphqlで、「該当するレコードがテーブルになければレコードを追加する、あればレコードを更新する」がしたかった

表題の通りです。結論としては、`upsert mutation`を使います。

### 前提
例えば、ブラウザ上で不特定の人数にアンケートを行う場合のテーブルの構造やクエリについて考えてみます。
テーブルには「回答のid(answerId)、回答(answer)、その解答をしたユーザー(userId)、その回答に対する質問番号(questionId)」の4つのカラムがあればよいです。

例) 回答テーブル
| answerId | answer | userId | questionId |
| :---: | :---: | :---: | :---: |
| 1 | ① | ユーザーA | 質問1 |
| 2 | ② | ユーザーB | 質問1 |
| 3 | ② | ユーザーA | 質問2 | 
| 4 | ⑤ | ユーザーC | 質問3 |

さて、ユーザーが回答する際には、「まだ回答していない質問に答える(=DBに新しく回答を追加する)」ときと、「すでに回答したものを修正する(=DBに既に存在するレコードを更新する)」ときの2パターンがあります。クエリ的には、前者の場合は`insert mutation`で、後者の場合は`update mutation`となります。

`insert`と`update`をいちいち切り替えるのは面倒だなと思いながらHasuraのドキュメントを読んでいたら、`upsert mutation`というものがありました。

https://hasura.io/docs/latest/mutations/postgres/upsert/

詳しいqueryの記述例などは上記のリンクからドキュメントを読んでもらえればと思います。

ざっくりと説明しておくと、
`insert`用のクエリに`on_conflict`を追加します。そして`constraint`には「レコードが既にある」とみなすために参照するカラムがどれであるかを指定します。
レコードを追加する際に`constraint`に指定されたカラムの値を参照し、同じ値を持っているレコードがあればそのレコードを更新する。なければ新しいレコードとして追加する、といった具合です。

```graph
  insert_answers(
    objects: { answerId: $answerId, answer: $answer, userId: $userId, questionId: $questionId }
    on_conflict: { constraint: answers_userId_questionId_key, update_columns: answer }
  ) 
```

ちなみに、`constraint`に指定するのはカラム名ではなく、ユニークキーとして設定されているカラムのキー名?です。

例えば以下の場合は、`constraint: answerId`ではなく、`constraint: answers_pkey`と指定します。どんな名前が振られているかは、テーブルのModifyから確認することができます。
![スクリーンショット 2023-03-30 14.51.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645471/a2311dd2-a831-097a-b3c0-5496190df853.png)

先ほど例として挙げた回答テーブルでは、ユニークなキーは回答idとなっています。しかし、回答idはレコードがテーブルに追加されたときに生成されるものであるため、`constraint`に指定することはできません。
そのため、今回はユニークなキーとして、ユーザーidと質問idの複合キーを使いました。

テーブルのModifyのUnique Keysで、Add a new unique keysを選択し、ユーザーidと質問idを追加することで、ユニークな複合キーとしてキー名が生成されます。

![スクリーンショット 2023-03-30 14.56.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645471/33ee227b-66e3-34af-e46c-b4081619b5f4.png)

つまり今回の場合は、`constraint: answers_userId_questionId_key`としてクエリを実行すればよいです。
SalesForceMarketingCloud SDKの通知をタップした時、正しく機能しない。

# SFMC SDKの通知をタップした時、正しく機能しない。

SFMCSDKを使用したPush通知機能で、
通知が複数溜まると、正しい遷移先に遷移されないという事象が発生した。


## 原因

NotificationManagerのchannelの指定がdefaultになっていなかった。

 

## 対策

notification managerのchannel指定の分岐をなくし、defaultChannelをcreateする。
もし指定が必要なら、指定のチャンネルのStringに設定する。


なのでこれを ↓ 

```
        NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
          if (TextUtils.isEmpty(notificationMessage.url)) {
            NotificationManager.createDefaultNotificationChannel(context)
          } else {
            "UrlNotification"
          }
```

こうする　↓

```
        NotificationManager.NotificationChannelIdProvider { context, notificationMessage ->
            NotificationManager.createDefaultNotificationChannel(context)
          }
```


## 参考


https://salesforce-marketingcloud.github.io/MarketingCloudSDK-Android/notifications/customize-notifications.html


## 備考

通知をタップした時の挙動は、
when　以下の　指定をいじくればいい

公式実装の場合、
url.isNullOrEmpty() の場合は、アプリ(MainActivityに遷移)し、 
そうでない場合は、ブラウザでurlを表示するようになっている。

もし、特定のurlの時、このActivityを表示したい。
みたいなことをする場合は、
intentを編集して遷移先Activityを指定する。
取得したurlを遷移先でも使用したいなら、
intentPutExtraで遷移先まで持っていく。

        NotificationManager.NotificationLaunchIntentProvider { context, notificationMessage ->
          val requestCode = Random().nextInt()
          val url = notificationMessage.url
          when {
            url.isNullOrEmpty() ->
              PendingIntent.getActivity(
                context,
                requestCode,
                Intent(context, MainActivity::class.java),
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
              )
            else ->
              PendingIntent.getActivity(
                context,
                requestCode,
                Intent(Intent.ACTION_VIEW, Uri.parse(url)),
                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
              )
          }
        },
ChatGPT を使って、Rust言語を勉強します。エラー処理 篇

#### 質問1: Rust言語のエラー処理を教えてください。

#### 回答1:

---

Rust言語では、エラー処理にResultとOptionという2つの列挙型を使用することが一般的です。Resultは成功した値と失敗した値の2つの可能性を持ち、Optionは値が存在するかどうかの2つの可能性を持ちます。それぞれの列挙型には、エラーが発生した場合に関する情報が含まれます。

Result型は、次のように定義されます。

```rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}
```

Tは成功した場合の型、Eはエラーの型です。

Option型は、次のように定義されます。

```rust
enum Option<T> {
    Some(T),
    None,
}
```

Tは値の型です。

これらの列挙型を使用して、エラーを適切に処理することができます。例えば、Result型を使用してファイルを開く場合は、次のようになります。

```rust
use std::fs::File;

fn main() -> Result<(), std::io::Error> {
    let file = File::open("file.txt")?;
    Ok(())
}
```

この例では、File::openメソッドがResult型を返すため、?演算子を使用してエラーが発生した場合にはmain関数からErrが返されます。成功した場合は、Ok(())が返されます。

また、Rustではpanic!というマクロを使用して、実行時エラーを発生させることもできます。ただし、panic!は通常、致命的なエラーの場合にのみ使用されます。

---

#### 質問2: Rust言語のエラー処理の使い方を教えてください。

#### 回答2:

---

Rust言語のエラー処理には、Result型とpanic!マクロがあります。

Result型は、関数の戻り値として使用され、成功した場合はOkの値を、エラーが発生した場合はErrの値を返します。OkまたはErrの値は、それぞれジェネリック型Tとエラー型Eを保持します。

例えば、以下のような関数を考えてみます。

```rust
fn divide(x: i32, y: i32) -> Result<i32, String> {
    if y == 0 {
        return Err("Division by zero".to_string());
    }
    Ok(x / y)
}
```

この関数は、2つの整数xとyを受け取り、yが0でない場合はxをyで割った結果をOkの値として返し、yが0の場合はエラー文字列を含むErrの値を返します。

関数を呼び出す場合、match文やif let文を使ってResult型の戻り値を処理することができます。

```rust
let result = divide(10, 2);
match result {
    Ok(value) => println!("Result: {}", value),
    Err(error) => println!("Error: {}", error),
}
```

この場合、resultはOk(5)の値を持っているので、Okの分岐が実行され、"Result: 5"が出力されます。

一方、yが0の場合は以下のようになります。

```rust
let result = divide(10, 0);
match result {
    Ok(value) => println!("Result: {}", value),
    Err(error) => println!("Error: {}", error),
}
```

この場合、resultはErr("Division by zero".to_string())の値を持っているので、Errの分岐が実行され、"Error: Division by zero"が出力されます。

panic!マクロは、ランタイムエラーが発生した場合に使用されます。例えば、unwrapメソッドを呼び出した場合、Option型がNoneの場合にパニックが発生します。

```rust
let value: Option<i32> = None;
let unwrapped = value.unwrap();
```

この場合、valueはNoneを保持しているため、unwrappメソッドを呼び出したときにパニックが発生します。

これを回避するには、match文やif let文を使ってOption型を処理する必要があります。

```rust
let value: Option<i32> = None;
match value {
    Some(unwrapped) => println!("Unwrapped value: {}", unwrapped),
    None => println!("Error: value
```

---

#### 質問3: Rust言語のエラー処理のサンプルを教えてください。

#### 回答3:

---

以下は、std::fs::read_to_string関数を使用してファイルを読み取るRustプログラムの例で、エラー処理が含まれています。

```rust
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let file_contents = fs::read_to_string("file.txt")?;
    println!("{}", file_contents);
    Ok(())
}
```

このプログラムは、file.txtというファイルを開き、その内容を標準出力に表示します。しかし、ファイルが存在しない場合や、アクセス権限がない場合など、読み取りに失敗する可能性があります。そのため、read_to_string関数が返す結果型であるResultを使用して、エラー処理を行っています。

Result型は、成功時にはOk列挙子に値を格納し、失敗時にはErr列挙子にエラーの種類を格納します。?演算子は、Okの場合には中の値を取り出し、Errの場合にはそのまま返して、関数の呼び出し元でエラーを処理することを可能にします。

このプログラムでは、main関数の戻り値型をResult<(), Box<dyn std::error::Error>>としています。これは、Okの場合には()（ユニット型）を返し、Errの場合にはBox<dyn std::error::Error>型のエラーを返すことを意味しています。Box<dyn std::error::Error>型は、任意のエラーを表すことができ、main関数が返す値は、呼び出し元のプログラムにエラーを伝えるために使用されます。

---
ArduinoIDE2.0.xで同じスケッチがタブに出現する件

初カキコ…ども…

# 概要
win10 インストール版ArduinoIDE 2.0.3 , 2.0.4で発生した
写真のように、まったく同じスケッチがタブに出てくる。
![ArduinoIDE1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126772/3097251d-8bfa-3f76-802a-c8d927cebb02.jpeg)

このままでも使えるけど,うっとうしいので消し方をメモ書き程度にまとめる．

# 結論
1. 追加で空のinoファイルを作成する

2. "Ctrl+""Shift"+"P"　でコマンドパレットを開く
![ArduinoIDE2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126772/1604c4ef-f635-85aa-f155-d8a1d4a2dd51.png)
3. "View: Reset Workbench Layout"を実行

4. 1で追加したinoファイルを削除(任意)

以上.

### 参考URL
1.https://forum.arduino.cc/t/ide-2-0-3-double-tabs/1066585/4
2.https://github.com/arduino/arduino-ide/issues/1791
ファイルの文字コードをMacのターミナルで確認する＆変換する

先日Shift_JIS形式でhtmlファイルを作成する案件があり、
納品する前に今一度文字コードを確認しておきたい…と思っていろいろ調べたことメモです。

# 文字コードを確認する
文字コードを確認するにはfileコマンドにmimeオプションをつけて実行すればOKです。
```
$ file --mime ファイル名
```
ここで`unknown-8bit`と出てきたのでドキッとしてしまいましたが、`unknown-8bit`は`Shift_JIS`のことだそうでホッと一安心。

# 文字コードを変換する
文字コードを変換するにはnkfコマンドを使います。
```
//未インストールの場合、Homebrewでnkfをインストール
$ brew install nkf

//ファイルをShift_JISに変換（上書き保存）する
$ nkf -s --overwrite ファイル名

//ファイルをUTF-8に変換（上書き保存）する
$ nkf -w --overwrite ファイル名
```

# いろいろなcharset

上記のコマンドをテストしようとして適当にテキストファイルを作成したところ、コマンドはもちろんエディターの設定をいろいろ変えて保存しても`file --mime`での出力結果がずっと`us-ascii`になってしまい、大いにハマりました。
原因はマルチバイト文字を含まないファイルだったからでした。（本文がtestのみとかだった）

```
$ file --mime 空のファイル.txt
結果： inode/x-empty; charset=binary

$ file --mime マルチバイトなしファイル.txt
結果： text/plain; charset=us-ascii

$ file --mime utf-8のファイル.txt
結果： text/plain; charset=utf-8

$ file --mime Shift_JISのファイル.txt
結果： text/plain; charset=unknown-8bit
```
まめると↑のような感じです。
これで困るケースはあまりなさそうですが念のため頭に入れておきたいと思います :bulb: 

# 参考記事

https://qiita.com/unsoluble_sugar/items/ca8fc4458abbd9c10c97

https://qiita.com/heeroo_ymsw/items/c6e15d5f9246b4e842eb

https://sachips.byeto.jp/mac/cmd_nkf.html

ありがとうございます:pray::sparkles:
UIAutomationって、デカいと見つけられない？

VBAからUIAutomation使ってとあるツールをリモコンしているのだが、データをたくさん作ったり読み込むとだんだん重くなっていくとともに、
FindFirstが、対象物があるはずなのにいつまでもオブジェクトを拾わず帰ってこない。
なんで？

と一日悩んて寝たら、

はっ、もしやたくさん読み込んだってことは、Windowを持ち過ぎなのか？

と思い立つ。
データファイルとペアになったWindowを閉じまくったら調子良くなった。
[Biopython]scaffoldをcontigに分割する


# 動機
これを
```scaffolds.fa
>scaffold1
ACTGTGCATNNNNNNACGCTGCANnnNNCTGCAnnnCTGCAnnNNNNCTGCA
>scaffold2
ACGACGACGCGATAGAGnnnnnnAGACGAGAGNNNnnACGACGACG
```
こうしたいことがある。
```contigs.fa
>scaffold1_001
ACTGTGCAT
>scaffold1_002
ACGCTGCA
>scaffold1_003
CTGCA
>scaffold1_004
CTGCA
>scaffold1_005
CTGCA
>scaffold2_001
ACGACGACGCGATAGAG
>scaffold2_002
AGACGAGAG
>scaffold2_003
ACGACGACG
```
[Python2](https://www.biostars.org/p/211400/#214457)や[Perl](https://www.seqanswers.com/forum/bioinformatics/bioinformatics-aa/11014-help-scaffold-2-contigs?t=12993)、[SeqKit](https://www.biostars.org/p/211400/#214453)を使った方法がネットにあったが、Python3で動作するものは見当たらなかった。[Python2の実装](https://www.biostars.org/p/211400/#214457)をベースに書きなおした。元コードは最初のコンティグをアウトプットしないバグ(?)があったのでそれも修正。


# scaffold2contigs.py
https://github.com/satoshikawato/bio_small_scripts/blob/main/scaffold2contigs.py
## Usage
```
$ ./scaffold2contigs.py
usage: scaffold2contigs.py [-h] --input INPUT [--output OUTPUT] [-d DIGIT]

Split scaffolds into contigs

optional arguments:
  -h, --help            show this help message and exit
  --input INPUT, -i INPUT, --in INPUT
                        Input FASTA file
  --output OUTPUT, -o OUTPUT, --out OUTPUT
                        output FASTA file (default: stdout)
  -d DIGIT, --digit DIGIT
                        number of digits for zero-padding (default:3)
```
# References
https://www.biostars.org/p/211400/

https://www.seqanswers.com/forum/bioinformatics/bioinformatics-aa/11014-help-scaffold-2-contigs?t=12993
パッケージ管理ツールとは

# 個人的リマインド用
参考：せお丸のプログラマー養成講座様
https://www.youtube.com/watch?v=GCa3nMseZnQ&t=28s

## パッケージ管理ツールとは
パッケージとは、どこかの誰かが公開してくれている便利なツール
例：kaminariというパッケージを使えば、ページネーションが簡単に作れる

### パッケージ管理ツール
**このパッケージの導入や管理を行うツール**
言語ごとにいろいろある
php → Composer
python → Pip
ruby → Gem
node.js → npm/yarn
## 詳細をcomposerを使って確認
パッケージをインストールする時はGitHubにインストール方法がのっているので、ターミナルで導入

→導入すると「composer.json」「composer.lock」「vendor」という3つのファイルができる

1.composer.jsonには、どのパッケージをどのバージョンでインストールしたかがjson形式で記されている
2.vendorにはインストールしたパッケージの実態や、依存関係にあるものが入っている
3.composer.lockでは依存関係についてまとめている

## 依存関係とは
パッケージも別のパッケージを使って開発されている(どのバージョンかも決まっている)

**ここでパッケージ管理ツールの役割1**
パッケージの依存関係を自動的に解決する

バージョンについてだが、~1.1で1.1.0以上、2.0.0未満みたいなのとか、^1.1.5で1.1.5以上、2.0.0未満とかが決まっている。よくわからん。
ちなみにバージョンの数字が3つとかあるのを、セマンティックバージョニングとかいうらしい

## セマンティックバージョニング
バージョン番号はこういう風につけろっていうルールみたいなもの
例：1.2.3(左からメジャー、マイナー、パッチ)

**バージョン種類/バージョンが上がるタイミング**
**メジャー** /API互換性がなくなった場合
**マイナー** /API互換性を保ったまま機能を追加した場合
**パッチ** /API互換性を保ったままバグを修正した場合

API互換性
→まず、クラスやメソッドの呼び出し方のルールをAPIという。それを変更する時をAPI互換性がなくなったという。メジャー番号が繰り上がっていたら、これまで動いていたものが動かなくなる可能性あり

動かなくなるようにしないためには、**バージョンを固定する**
例："monolog/monolog": "^2.0"みたいな

バージョンアップ自体はいいことだから、定期的にバージョンアップはしよう

## バージョンアップ
1. Updateコマンド(composer update)を実行
→全てのパッケージが最新になるのではなく、各パッケージが可能な限り最新になる感じ
2. テスト
3. コミット

### composer install
composer.lockに書かれているパッケージをvendor/にダウンロードする

## ちなみに注意点
**vendorはgitにコミットしちゃダメ←.gitignoreに入れよう**
→composer installでダウンロードできるから、わざわざgitリポジトリでバージョン管理しなくていい

composer installどこで使うの？
→Aさんがリモートにpush。それをBがpullするが、もちろんvendorファイルはない。そこで、composer install。

## バージョン用語
**バージョン用語 /意味**
dev/deta / 実験版
stable / 安定版
deprevated / 非推奨。今後廃止されるなど

## RubyのGemだけ特殊な件について
gem installはjsonやlockを作ってくれない

**そこでjsonとかを作ってくれるbundlerをinstallしてbundle installをする**

##　パッケージ管理ツールOS版
Mac → brew(HomeBrew)
Linuc → yum(cent)/ rpm/ apt-get(ubuntu)
アウトプットマインドを身に着けた！

## はじめに
最近、日課になりつつあるQiitaを徘徊してたら
アウトプットの重要性を説く記事を見つけました。
勝手ながら、記事を共有させていただきます。↓

[日々息をするようにアウトプットしている人は何を意識しているのか？](https://qiita.com/satomihoya/items/876dea7dd7a4c90c140a)
[エンジニアは全員技術記事を書くことを習慣化した方がいいぞ](https://qiita.com/HiromuMasuda0228/items/a71dea7ef4d77a30b118)

今日一日で、アウトプットに対するモチベーションがかなりupした。
本記事では、アウトプットに対する私なりの考えの結論を共有させていただく。

## アウトプットの重要性は頭では分かっていたが
「時間がもったいない」
「頭で理解できてるから、まぁいっか」と思っていた。
愚か者！

## 説明ができないことに気づいた
最近、私は誰かに物事を説明することがかなり苦手だということに気付いた。

これまでの人生、聞き手に回ってたことが多かった。
だから自分一人が喋り続けるという行為に抵抗があることに気づいた。。。。

## 実はよくわかっていなかった
いざ、インプットした（つもりの）知識を発揮しようとしても、言語化できないことが多い。
それってつまり、理解できてないんじゃない？？

> 1日で記事を書き上げられるだろうと思ってたものが、結局1ヶ月くらいかかってしまう。一度書き始めたら、とても多くのことが曖昧でよく分かっていないと気づく。ほどなくして20個ほどタブを開いていることに気づく。

この言葉ほんと共感しかなくて、
「聞いただけ」「読んだだけ」だと
結局自分の中で咀嚼できてないから、理解が曖昧。

## 言語化することに意味がある
言葉としてまとめることで、誰にでも共有できる。もちろん、未来の私にも！

## アウトプットで、トークが上手になる
もちろん、言語化してるわけだから、それを口に出すだけだし。
「人に伝わる」話し方ができるようになる。

## アウトプットの仕方は人それぞれ
なら自信をもって、アウトプットする。
拙い言葉でもいいや。
後で読んで、それで理解できたらベスト！！

## 「簡単」が好き
「箇条書き」とか、「一言で表す」って簡単で良い。
自分のアウトプットスタイルってこれかな。

## アウトプットを意識してインプットする
[日々息をするようにアウトプットしている人は何を意識しているのか？](https://qiita.com/satomihoya/items/876dea7dd7a4c90c140a)で、山田さんは、
> 勉強会と本はアウトプットを意識してインプットしているかも。
とおっしゃっていた。

この考え方、すごく参考にしたいと思った。

## おわりに
最初は言語化することにかなりの時間を消費することになりそう。
それを理解しながらも「やろう！」って思えたんなら、
自分の成長に必要なことって判断したからだと思う。

とにかくまずは、三日坊主にならないようにしながら
Qiita記事を毎日書いていこうと思う。

本記事は、その導入として。
閲覧ありがとうございました。
【SharePointOnline】リストの複数列を対象にして順に並べ替えたいとき

---
# 1. はじめに

こんにちわ。エーティーエルシステムズ加藤です。
SharePointリストで、並べ替えをしたい。

という願いは容易に叶うのですが、

複数列で並べ替えたいんだ！！！というときのやり方がわかりません・・・:sob:ちーん

調べてみたので、投稿します。

みなさまのお役に立てれば幸いです。

<!-- 自己紹介や発端や概要を記載 -->

**※免責事項(必ず、ご一読ください)**
　　本記事の情報により生じた、いかなる損害や損失についても、当社は一切の責任を負いかねます。
　　また、誤情報が入り込んだり、情報が古くなったりすることもありますので、必ずしも正確性を保証するものではありませんのでご了承ください。

# 2. 概要
・M365アカウント
・PowerAutomateなどの他のアプリは使わない前提


サンプルデータ：
　【健康チェック】リスト
　　・登録日
　　・体温
　　・症状
　　・備考
　　・名前

![0_並べ替え前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/d8c2a81a-fc36-dbf3-c76f-5a5367c4c7cb.png)

では、早速並べ替えていきます。


# 3. 並べ替え

**１）症状順→体温順で並べ替えたいとき（グループ化で並び替える）**

　①症状でグループ化

![2-1_並べ替え前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/c1b0c8ba-11e4-0262-fe68-874f70a9bec7.png)


　②体温で並べ替え

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/e97083a7-06ad-10e2-8f0e-d67a7d8744d2.png)


　③完成：症状順→体温順で並び替えられています。

※でも、症状の降順が選べない。体温の降順は選べる。

![3_並べ替え2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/30831250-f827-8ddc-d602-a1a5640a2331.png)


**２）体温順→登録日順で並べ替えたいとき（現在のビューの編集を使って並べ替える）**

①右上の[すべてのアイテム]から[現在のビューの編集]を選択します。

![4_編集から並べ替え.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/5e9ba139-b383-214e-f2e6-f316472db8f9.png)


②並べ替えから

[最優先する列]で[体温]
[2番目に優先する列]で[登録日]を選択する

![スクリーンショット 2023-03-31 143703.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/577e32b8-bc6e-b3d4-629d-edb23869211b.png)



③右上の[OK]を押して更新する。

![5_編集から並べ替え2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/98c34fff-84b3-7cf5-cd5d-e5c4c0a9cf1b.png)


④完成：体温順→登録日順で並び替えられています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2326369/62dea4ca-ae63-eeea-80dc-1d5729337530.png)


# 4. まとめ

知っていればどってことない複数列の並べ替えですが、
いざどうやるのかって調べるとなかなか出てこないので、メモってみました＾＾

１）グループ化と２）ビューの編集の合わせ技などもできるのかなど、よろしければ試してみてください。


# 5. 最後に

　最後まで読んでいただき、ありがとうございました！
　参考になりましたら、💖「いいね」していただけると励みになります＾＾

　弊社はfacebookもしております！

[![f_logo_RGB-Blue_64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1661584/81cb9432-5fc7-7dfd-0155-15a602114fa2.png)](https://www.facebook.com/atlsystems)
PowerVC でVMデプロイ、VM起動、停止、再起動のスケジューリングが可能

#### はじめに

PowerVC で VM 停止、起動、再起動、デプロイがスケジュール可能です。機能のお知らせで記事を記載します。


参考：[PowerVC API: Scheduled tasks](https://www.ibm.com/docs/en/powervc/2.0.2?topic=apis-scheduled-tasks)

- Schedule stop VMs
- Schedule start VMs
- Schedule restart VMs
- Schedule deploy VMs


参考：[PowerVC 2.0.2 - Working with scheduled tasks](https://community.ibm.com/community/user/power/blogs/poovannan-e1/2021/12/15/scheduledtasks)



スケジューリングは 2.0.2 の新機能ですがスケジューリングの他、PowerVC 2.1 の新機能情報を含めて 2023年3月中盤に下記のPowerVC v2 導入・運用ガイドを追加更新しました。


https://qiita.com/c_u/items/41e87f5d0e0417c74395




---
#### 環境

PowerVC 2.0.2.1  (ppc64le, S824)

---
#### VMデプロイのスケジューリング

"Schedule for later"  を押すとSchedule date と time が画面に出てきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517421/3ebec771-a15a-a7ec-3359-44ebe3bd0e90.png" width=70%>





設定入力後 ”Deploy VM” を押します。デプロイは開始されず、タスクとして登録されます。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517421/2960d5ec-9321-dc1d-5bfb-91759084b0ea.png" width=80%>




---
#### VM起動のスケジューリング

VM 起動では StartVM の画面で "Schedule for later" を押すと Start date, Select a time が出力します。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517421/3ada6c35-1aee-6973-2b24-913038792e08.png)


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517421/d3188017-f13e-2566-1dea-a28eec4f8806.png" width=70%>


左レーン Task - Scheduled task に対象のタスクがリストされます。

![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517421/856a5cc5-a715-7f4d-7d2e-75fb5eb5aef8.png)

---
#### おわりに


IBM Cloud Power Systems Virtual Server でもいつかスケジューリング機能が出てくるかも? と、PowerVS の新機能を見ています。

https://cloud.ibm.com/docs/power-iaas?topic=power-iaas-release-notes





以上です。
Akamai EdgeWorkersでCDNのサーバー上にBFF(Backends for Frontends)を構築する

## マイクロサービス化とクライアントアプリ
「APIのマイクロサービス化が進む中、クライアントアプリの実装がどんどん複雑で面倒になってきている。アプリはBFFのみと通信し、BFFがバックエンドのAPIとのやり取りを担うアーキテクチャに変更したい。」
最近はこのようなケースが増えてきているのではないでしょうか。
BFFの構築方法は多くの検討の余地があると思いますが、ここではEdgeWorkersを使ってエッジサーバー上にBFFを構築する例を考えます。EdgeWorkersで構築することでBFFのスケーリングを気にする必要がなくなります。

## EdgeWorkersとは
EdgeWorkersでは、エッジで駆動するリクエスト/レスポンスのイベントハンドラを提供し、 JavaScript を実行できます。クライアントの近くのEdgeサーバーで処理を行うためレイテンシが小さく、世界中に数十万台のサーバーが配置され、自動で分散駆動するためスケーリングを気にしなくて良いというメリットがあります。

参考: [EdgeWorkers:Akamaiのエッジサーバー上でJavascriptで書いたコードが実行可能に](https://www.akamai.com/ja/blog/developers/edgeworker)

## サンプル
ここでは、既存のAPIをcallした結果をアグリゲートするという内容で実装します。既存リソースを無駄にすることなくBFFを構築できます。
```javascript
import { logger } from 'log'
import { createResponse } from 'create-response'
import { httpRequest } from 'http-request'

export async function responseProvider(request) {
    // 3つのAPIをcallする
    const data = await Promise.all([
        callAPI1(request),
        callAPI2(request),
        callAPI3(request)
    ])
    const result = {
        api_1: data[0],
        api_2: data[1],
        api_3: data[2]
    }

    // アグリゲートして返却
    const options = {}
    options.status = 200
    options.headers = {}
    return createResponse(JSON.stringify({
        status: "OK",
        data: result
    }), options)
}
```
## EdgeWorkersを呼び出すよう設定
CDN側の配信設定からEdgeWorkersを呼び出します。
![CDNの設定からEdgeWorkersを呼び出す](https://camo.qiitausercontent.com/66f499af2236748bb7624f4afc89cac95d256c0d/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f323931313532352f33393065643438632d376435332d363430392d326565632d3564623164353064326332342e706e67)

## おわりに
今回のサンプルは非常に簡単なものですが、たとえばEdgeWorkersに組み込まれているcryptoモジュールを利用したJWTの検証処理を追加などするとより実用的になります。アーキテクチャを再検討する機会がありましたらぜひEdge Computingも選択肢に含めてみてはいかがでしょうか。
jtagアダプタいろいろ

雪豹のurjtag(0.10)で試してみました。

自作FT232Hモジュール - 使えます

![写真（2023-03-31 14.12）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/b19c99bd-98ca-55da-e501-acde7c8724f5.jpeg)

```
jtag> cable ft2232
Connected to libftd2xx driver.
```

USB-Blasterもどき - 使えます

![写真（2023-03-31 14.10）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/abe4a7d0-af68-f575-f48e-68806ee90416.jpeg)

```
jtag> cable UsbBlaster
Connected to libftd2xx driver.
```

USB-Blasterコピー品 - 使えません

![写真（2023-03-31 14.11） #2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/0d6af714-b95d-1295-dfb8-03c1f246fa0c.jpeg)

```
jtag> cable UsbBlaster
Couldn't connect to suitable USB device.
Error: Cable connection failed!
```

USB Probeでは見えてるしVID,PIDも同じようなのですが。。。とりあずまにあってるので、デバッグはしません。
【Swift】UIActivityItemをカスタムする

# 環境
macOS Monterey バージョン 12.6.3
Xcode バージョン 14.2

# やりたいこと
共有ボタン（Action Button）が押された時に表示されるアイコン、テキストをカスタムしたい
<img width=40% alt="IMG_2623.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333791/7f3f3b31-2a21-edfd-fd77-316b391f521e.png">

# コード
iOS13以降で利用可能なLPLinkMetadataを使って共有画面の内容をカスタムします。

内容を書き換えやすいよう、ActivityItemのカスタムクラスを作成します。
``` swift: CustomShareItem.swift
class CustomShareItem: NSObject, UIActivityItemSource {
    var shareURL: URL
    var shareText: String
    var shareImage: UIImage
    
    init(shareURL: URL, shareText: String, shareImage: UIImage) {
        self.shareURL = shareURL
        self.shareText = shareText
        self.shareImage = shareImage
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return ""
    }
    
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        return nil
    }
    
    func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
        let metaData = LPLinkMetadata()
        metaData.originalURL = shareURL
        metaData.url = metaData.originalURL
        metaData.title = shareText
        metaData.imageProvider = NSItemProvider(object: shareImage)
        return metaData
    }
}
```

``` swift: ViewController.swift
    func share() {
        let url = URL(string: "https://www.mlb.com/") ?? nil
        let text = "Sample Text"
        let image = UIImage(named: "sample") ?? nil

        let shareItem = CustomShareItem(shareURL: url!, shareText: text, shareImage: image!)
        let activityViewController = UIActivityViewController(activityItems: [shareItem, self], applicationActivities: nil)
        present(activityViewController, animated: true, completion: nil)
    }
```

# 参考

https://developer.apple.com/documentation/uikit/uiactivityitemsource

https://developer.apple.com/documentation/linkpresentation/lplinkmetadata

少し読み解くのが難しいですが、こちらの記事も参考にしています。

https://qiita.com/ezura/items/6036c6e100599b601482
「--help」が使えるシェルスクリプトを作る方法

## はじめに
Linuxを使っていると､ちょっと作業をシェルスクリプトで自動化したくなることがあると思います｡
そんな時に､他のUNIXコマンド同様､`--help`や`-h`で使い方が表示できるようになると便利｡

下のテンプレートをシェルスクリプトの先頭に貼り付ければ､`$ ./foo.sh --help`で使い方がターミナルに出力されます｡


## テンプレート
```bash:日本語バージョン
function usage() {
cat <<EOF
使い方: コマンド [<オプション>...] 

-----コマンドの説明部分--------

    オプション:
      -h|--help		ヘルプを表示

EOF
}
while [ -n "${1:-}" ]; do
    case "$1" in
        -h|--help) usage; exit 0;;
        -*) echo -e 'エラー: 無効なオプションです。「-h」を使用してください。'; exit 1;;
        "") break;;
    esac
    shift
done
```

- EOFとは､"**End of File**"（ファイルの終わり）を表す用語であり､ファイルの終わりに達したことを示す特別なシグナル｡
  - ファイルを読み込む際には、EOFに達すると読み込みが停止し、処理が終了する｡
  - `cat << EOF`とすることでそれまでに読み込んだ文字列を全て表示する｡
- `-n $1`とすることで､引数が空白でない限り`case`構文を実行するようになる｡
  - `${1:-}`はデフォルトの指定であり､`-`は空文字を意味する

以下は英語バージョン｡
```bash:英語バージョン
function usage() {
cat <<EOF
Usage: command [<options>...] 

-----コマンドの説明部分--------

    Options:
      -h|--help		show help 

EOF
}
while [ -n "${1:-}" ]; do
    case "$1" in
        -h|--help) usage; exit 0;;
        -*) echo -e 'error: Invalid option. Use "--help"'; exit 1;;
        "") break;;
    esac
    shift
done
```


## 使ってみる

今､`test.sh`というシェルスクリプトで上記のコードを記述しているとする｡
```bash:ヘルプを表示
$ ./test.sh --help
使い方: コマンド [<オプション>...] 

-----コマンドの説明部分--------

    オプション:
      -h|--help		ヘルプを表示

```

空白は`break`で無視するようになっているので､オプションまで間隔を空けてもちゃんと実行される｡
```bash:空白を空けても大丈夫
$ ./test.sh           --help
使い方: コマンド [<オプション>...] 

-----コマンドの説明部分--------

    オプション:
      -h|--help		ヘルプを表示

```

`-`を伴う引数は､設定しているもの以外はエラーが出る｡
```bash:「-」を伴うオプションは､設定したもの以外だとエラーが出るようにしている
$ ./test.sh -x
エラー: 無効なオプションです。「-h」を使用してください。
```

### 新しいオプションを作る
`-e`もしくは`--echo`で「hello world」と出力されるようにする｡
```bash:「-e」と「--echo」を設定する
while [ -n "${1:-}" ]; do
    case "$1" in
        -h|--help) usage; exit 0;;
        -e|--echo) echo "hello world"; exit 0;;
        -*) echo -e 'エラー: 無効なオプションです。「-h」を使用してください｡'; exit 1;;
        "") break;;
    esac
    shift
done
```
試してみる｡
```bash:「-e」もしくは「--echo」で「hello world」
$ ./test.sh -e
hello world

$ ./test.sh --echo
hello world
```

### 引数を作る
ただし､上記の設定のままだと､`echo "$1"`のように直接引数を渡すと､`未割り当ての変数です`と言われる｡
```bash:test.sh
echo "$1"
```
```bash:引数を渡したのに「未割り当て」と言われる
$ ./test.sh テスト
./test.sh: 行 25: $1: 未割り当ての変数です
```

これは「-オプション」に合致しない引数に対する処理が書かれていないため｡
`*) test="$1";;`を追加することで､処理を正常に動作させることが出来るようになる｡
```bash:test.sh
while [ -n "${1:-}" ]; do
    case "$1" in
        -h|--help) usage; exit 0;;
        -e|--echo) echo "hello world"; exit 0;;
        -*) echo -e 'エラー: 無効なオプションです。「-h」を使用してください。'; exit 1;;
        "") break;;
        *) test="$1";;
    esac
    shift
done

echo $test
```
```bash:今度はちゃんと動作した
$ ./test.sh テスト
テスト
```

## まとめ

このように､Usage関数とwhile構文を使うことで､好きなオプションをシェルスクリプトに設定できるようになりました｡

また､このヘルプの書き方は､**引数の扱い**に関して注意してください｡
【jQuery UI】sortable() の handle オプションにinputタグを指定した際に動作しない事象の解決法

# 結論
`sortable()`の`cancel`オプションに`input`や`button`タグが指定されており、初期設定で動作しないようになっている。
その為、以下のような記述が必要。
```javascript
$("hoge").sortable({ 
    handle: 'button',
    cancel: '' // 空文字を指定
})
```


# 参考
https://js.studio-kingdom.com/jqueryui/interactions/sortable

> `cancel`
> 型：Selector 初期値：":input,button"
> マッチした要素の並び替え処理が開始されることをキャンセルします。

`handle`オプションより`cancel`オプションの指定の方が優先される模様。
単純だがかなりハマってしまったので備忘録として。
フロントエンド開発におけるCSSフレームワークの比較

# 概要
今回はcssフレームワークの比較を行いたいと思います。
フレームワークを使用することでコードの整理や開発時間の短縮につながります！
以下によく知られているCSSフレームワークを比較した結果をまとめてみました！
# Bootstrap
Bootstrapは、もっとも有名なCSSフレームワークの一つです。Twitterによって開発され、現在はオープンソースで利用可能です。Bootstrapには、レスポンシブデザインのためのグリッドシステム、テンプレート、フォーム、ボタン、ナビゲーションなどのコンポーネントが含まれています。Bootstrapは、ブラウザの互換性が高く、多くのプラグインやツールが利用可能です。また、多くの開発者が使っており、質問に答えてくれるコミュニティもあります。ただ多くの利用者がいるためデザインが似通るのが難点です。

# Foundation
Foundationは、Bootstrapに匹敵する人気の高いCSSフレームワークです。ZURBによって開発され、オープンソースで利用可能です。Foundationには、グリッドシステム、UIキット、フォーム、ナビゲーション、ボタンなどのコンポーネントが含まれています。Bootstrapと比較すると、より柔軟で、よりカスタマイズ可能なレイアウトを提供することができます。また、Foundationは、Sassのようなプリプロセッサを使用することができます。

# Materialize
Materializeは、GoogleのMaterial Designガイドラインに基づいて作成されたCSSフレームワークです。Materializeは、グリッドシステム、テンプレート、フォーム、ボタン、ナビゲーション、アイコンなどのコンポーネントが含まれています。BootstrapやFoundationと比較して、より美しいUIを提供することができます。また、Materializeは、JavaScriptフレームワークであるVue.jsやReactとの統合が容易であることが特徴です。

# Bulma
Bulmaは、CSSフレームワークの中でも比較的新しいフレームワークです。Flexboxを使用しており、簡潔で使いやすいスタイルを提供しています。また、カスタマイズ性が高く、必要なコンポーネントだけを取り入れることができます。

# Tailwind CSS
Tailwind CSSは、クラス名を用いたスタイリング方法を採用したCSSフレームワークです。コンポーネントやスタイルの設定は、クラス名を追加するだけで実現できます。独自のテーマやアセットを容易にカスタマイズできるため、カスタマイズ性に優れています。

## まとめ
以上が代表的なフレームワークになります。
上手に使って効率のいい開発を行いたいですね。
ChatGPT を使って、Rust言語を勉強します。ライフタイム 篇

#### 質問1: Rust言語のライフタイムを教えてください。

#### 回答1:

---
Rust言語におけるライフタイム（lifetime）とは、変数や参照が存在できる期間を表します。Rustの借用規則は、コンパイラにライフタイムの正しい使用を保証するよう指示します。

Rust言語におけるライフタイムは、借用された参照の有効期間に関連しているため、主に借用規則において重要です。ライフタイムは、'aのようにアルファベットを使用して表記されます。以下は、&参照演算子で使用されるライフタイムパラメーターの例です。

```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let s1 = String::from("Rust");
    let s2 = "programming";
    let result = longest(s1.as_str(), s2);
    println!("The longest string is {}", result);
}
```

この例では、longest関数は、2つの文字列スライス x と y を取り、より長い文字列を返します。'aはライフタイムパラメーターであり、xとyのライフタイムが同じであることを示しています。つまり、xとyのライフタイムは、longest関数の返り値である&'a strのライフタイムと同じである必要があります。

---

#### 質問2: Rust言語のライフタイムの使い方を教えてください。

#### 回答2:

---
Rust言語のライフタイムは、メモリの所有権と借用を管理するために使われます。ライフタイムは、値が有効な期間を表します。所有者が値を所有している場合、その値のライフタイムは所有者のライフタイムによって制御されます。値が借用されている場合、そのライフタイムは借用元のライフタイムによって制御されます。

ライフタイムは、コンパイラに対して、値を参照する変数が有効な間、その値が有効であることを示します。Rustのライフタイムシステムは、変数のスコープとライフタイムを一致させることにより、安全で効率的なコードを生成します。

ライフタイムを使用するためには、ジェネリック関数や構造体に対してライフタイム引数を指定する必要があります。ライフタイム引数は、シングルクォートで囲まれた識別子で表されます。関数の引数や構造体のフィールドにライフタイムが必要な場合、その引数やフィールドにもライフタイムを指定する必要があります。

以下は、ライフタイムを指定したジェネリック関数の例です。

```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
```

この関数は、2つの文字列スライスを受け取り、長さの大きい方の文字列スライスを返します。関数の宣言の前に、'aというライフタイムが定義されています。このライフタイムは、xとyが有効である間、返される参照が有効であることを示します。関数内では、if文を使ってxとyの長さを比較し、長さの大きい方を返します。そのため、xとyのライフタイムが一致する必要があります。

このように、Rustのライフタイムシステムを使うことで、安全かつ効率的なコードを書くことができます。

---

#### 質問3: Rust言語のライフタイムのサンプルを教えてください。

#### 回答3:

---
以下は、Rust言語でのライフタイムのサンプルコードです。この例では、関数内で参照されるString型のスライスを返す関数を定義しています。このスライスは、元の文字列の一部を指すため、そのライフタイムは元の文字列よりも短くなければなりません。ライフタイム引数を使用して、コンパイラにこれを伝えます。

```rust
fn get_slice(s: &str, start: usize, end: usize) -> &str {
    &s[start..end]
}

fn main() {
    let s = String::from("hello, world!");
    let slice = get_slice(&s, 0, 5);
    println!("{}", slice);  // output: hello
}
```

この例では、関数get_sliceに文字列sと、スタートインデックスとエンドインデックスが渡されます。この関数は、スライスを返すため、元の文字列sの一部を指すことになります。しかし、このスライスは元の文字列よりも短いライフタイムでなければなりません。したがって、関数get_sliceの引数に&str型のライフタイム引数を追加し、スライスが元の文字列よりも短いライフタイムを持つことを明示します。

---
BLAST結果をBEDフォーマットに変換する


ゲノムに対してタンパク質あるいは核酸配列をBLASTで貼り付け、その結果をBEDフォーマットに変換したい。

# blast2bed.py
https://github.com/satoshikawato/bio_small_scripts/blob/main/blast2bed.py
## Usage
```sh
$ ./blast2bed.py
usage: blast2bed.py [-h] -i INPUT [-o OUTPUT] [-s SCORE] [-e EVALUE]

convert BLASTN/BLASTX/TBLASTX output into BED format

optional arguments:
  -h, --help            show this help message and exit
  -i INPUT, --input INPUT
                        tab-separated blast output (required) with "-outfmt [6|7]"
  -o OUTPUT, --output OUTPUT
                        output BED format file (default: stdout)
  -s SCORE, --score SCORE
                        score (default: 0)
  -e EVALUE, --evalue EVALUE
                        E-value threshold (default: 1e-30)
```

```sh
tblastx -query WSSV.fa -subject CoBV.fa -outfmt 6 -out WSSV.CoBV.tblastx.out
```


```WSSV.CoBV.tblastx.out
NC_003225.3     LC741431.1      70.242  289     86      0       163798  164664  210172  211038  0.0     443
NC_003225.3     LC741431.1      59.459  259     105     0       254436  255212  9995    9219    0.0     348
NC_003225.3     LC741431.1      66.341  205     69      0       227273  227887  165779  166393  0.0     347
NC_003225.3     LC741431.1      65.753  219     75      0       16089   15433   85251   85907   0.0     343
NC_003225.3     LC741431.1      60.889  225     88      0       305641  306315  52386   51712   0.0     328
NC_003225.3     LC741431.1      63.636  209     76      0       304672  305298  53370   52744   0.0     315
NC_003225.3     LC741431.1      75.723  173     42      0       176995  177513  221910  222428  0.0     313
NC_003225.3     LC741431.1      59.193  223     91      0       302092  302760  55860   55192   0.0     303
NC_003225.3     LC741431.1      57.282  206     88      0       116435  115818  137829  138446  0.0     279
...
```
```sh
./blast2bed.py -i WSSV.CoBV.tblastx.out -o WSSV.CoBV.bed -e 1e-10
```

```WSSV.CoBV.bed
NC_003225.3     163798  164664  +       0       LC741431.1
NC_003225.3     254436  255212  +       0       LC741431.1
NC_003225.3     227273  227887  +       0       LC741431.1
NC_003225.3     16089   15433   -       0       LC741431.1
NC_003225.3     305641  306315  +       0       LC741431.1
NC_003225.3     304672  305298  +       0       LC741431.1
NC_003225.3     176995  177513  +       0       LC741431.1
NC_003225.3     302092  302760  +       0       LC741431.1
NC_003225.3     116435  115818  -       0       LC741431.1
NC_003225.3     164633  163806  -       0       LC741431.1
NC_003225.3     20141   19605   -       0       LC741431.1
NC_003225.3     303439  303864  +       0       LC741431.1
NC_003225.3     21670   22155   +       0       LC741431.1
NC_003225.3     10802   11305   +       0       LC741431.1
...
```
![igv_snapshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689564/7fb1e25a-2c7c-ce95-ed66-fc90a38b9954.png)


似たスクリプトが複数存在する。先人たちの苦労が偲ばれた。

https://github.com/milesroberts-123/convert-blast-to-bed

https://github.com/mojaveazure/BLAST_to_BED

https://github.com/nterhoeven/blast2bed
【Android】Navigationでよくある画面遷移時のクラッシュと対策

# Navigationによる遷移の問題
ボタンタップ時の遷移にnavController.navigate()を使用していると、時々こんなクラッシュが発生する
```
Fatal Exception: java.lang.IllegalArgumentException: Navigation action/destination a_to_b cannot be found from the current destination Destination
```

高速タップをした際などに既にdestinationでは遷移したことになっているのに遷移しようとしてクラッシュしている
（手元で再現したい人はnavController.navigate()を2回連続で呼んでみると簡単に再現する）

# 解決案
遷移前に今の画面を確認するような処理を入れることで解消する
```kotlin
if (navController.currentDestination?.id == 遷移元画面のID) {
    navController.navigate(遷移アクション)
}
```

毎回確認処理を入れるとコード量が無駄に増えていくので、以下のような拡張関数で処理をまとめる

```kotlin
fun NavController.isCurrentDestination(
    /** 対象の宛先 */
    @IdRes resId: Int
): Boolean {
    return currentDestination?.id == resId
}
```

あとは遷移時にはこの拡張関数を使うようにすれば解決する。

# 発展
今回は現在画面のチェックまでしか拡張関数化していないが、チェックから遷移までを拡張関数化すればよりコード量の削減と効率化に繋がりそう。
【レビューで指摘されがちシリーズ】メソッド名は「何をするかより、何が起きるかを表現」しよう

レビューで指摘された内容をそのまま放流するシリーズ。

ある Form のクラスに「詳細情報をセットして表示する」メソッドを作成しました。そのメソッドに対して `SetDetails` のようなメソッド名をつけました。このメソッド名に対してコメントをもらいました。

> メソッド名は違う気がします。
ダイアログの振る舞いを表すと考えて `ShowDetails` とかでいいのではないでしょうか？
何をするかより、何が起きるかを表現できると、ソースコードが読みやすくなりそうです。

私はこの指摘を受けるまでメソッド名に違和感を持っていませんでした。自分で実装すると、ついつい実装に寄り過ぎた命名になってしまう癖があるようです。でもコメントのように何が起きるのかを表現すれば、実装に寄り過ぎた命名になりにくいと思いました。

この指摘は有用な情報だなと思い社内展開したところ下記のようなコメントももらいました。

私と同時期に入社した社員からは

> これすごくあるあるですね…
どういう処理をしているのかをメソッド名に書いていて、「処理についてはメソッドの内容読めば分かるよね？」って指摘をもらったこと結構あります

とコメントもらいました。良かった。仲間がいた（笑）

ベテラン勢からは

> トップダウン的にメソッド書くと、そうなりにくい気がしないでもない。そういう意味ではTDDとかでも効果はあるかも

とコメントもらいました。なるほどです。上流から考えれば内部実装に寄ることはなく、そのメソッドにしてほしい内容を抽象化して命名できそうな気がします。

そして実際にレビューでコメントをくれた方からは、

> よく関数名を変えた後に、その関数の呼び出し箇所を見直します。
かっこよく呼び出せているか考えます。
TDDと似た感じだと思います。

とコメントをもらいました。呼び出し側からの気持ちになって考えると確かにうまくいきそうな気がします。「かっこよく呼び出せているか」という判定をしているあたりが天才味を感じさせるコメントです。

命名は難しいですが、ちょっとした意識の切り替えで命名センスが上がりそうだと感じた指摘でした。
【自分メモ】Beanstalk のバンドルログの解説

# はじめに
Beanstalkはログメニューから、環境のログをダウンロードすることができます。

![スクリーンショット 2023-03-31 0.38.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38099/e9799ad5-9587-8293-1995-61267707c35c.png)

ダウンロードすると、「BundleLogs-XXXXXXXXXXX.zip」というファイルができますが、解凍すると色々な種類のログファイルが入っています。

![スクリーンショット 2023-03-31 11.44.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38099/0b3712be-30ca-7327-3a59-6e87b3b8496f.png)


AWSの色々なサービスを触っている人は、ピンとくるものがありますが、初心者には「Beanstalkで動いているプログラムのログがどれか分からない」、「そもそも何のログか分からない」という事があるかと思います。 
   
今回、ログについて調べたりAWSに問い合わせたりした内容を、自分用にメモにまとめました。

# 解説

### 🔹 cfn-◯◯.log
  - CloudFormation ヘルパースクリプトが出力しているログ。
  - 参照：[CloudFormation ヘルパースクリプトリファレンス](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html)

### 🔹 cloud-init.log 、 cloud-init-output.log
  - EC2の構成管理ツールのcloud-initの実行ログ

### 🔹 cron
  - cron のログファイル。
  - Elastic Beanstalk の環境には、デフォルトでは cron ジョブは設定されていないが、設定した場合には、このファイルに実行ログ等が記録される。
  - 参照：[Elastic Beanstalk 環境にある Amazon EC2 インスタンスに cron ジョブを作成する方法を教えてください。](https://repost.aws/ja/knowledge-center/cron-job-elastic-beanstalk)

### 🔹 eb-◯◯.log
  - Elastic Beanstalk 環境の Amazon EC2 インスタンスからの各種ログ。
  - 例えば、「eb-cfn-init.log」にはElastic Beanstalkとして設定されたインスタンスのユーザーデータの実行時のログが記録されている。
  - 参照：[Elastic Beanstalk 環境の Amazon EC2 インスタンスからのログの表示](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/using-features.logging.html)

### 🔹 「healthd」フォルダ
  - Elastic Beanstalkのヘルスエージェントのログが記録されている。
  - 参照：[拡張状態ヘルスレポートおよびモニタリング](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/health-enhanced.html)

### 🔹 message
  - **恐らくこれが一番見たいファイルになると思います。**
  - 後述のweb.stdout.logと同様に、デプロイしたアプリケーションの標準出力のログの内容に加えて、rsyslogによって管理されるその他のコンポーネントのログ(kernelや、systemd等)が記録されている。
  - 後述のweb.stdout.logとの使い分けとしては、**トラブルシューティング等で、複数のコンポーネントのログを単一ファイルにて時系列順に確認したい場合に有効。**

### 🔹 「nginx」フォルダ
  - Elastic Beanstalk にて使用している nginx のアクセスログ、エラーログが格納されている。
  - 参照：[リバースプロキシを設定する](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/java-se-nginx.html)

### 🔹 「rotated」フォルダ
  - 後述の、「web.stdout.log」の過去ログ。

### 🔹 web.stdout.log
  - Node.jsの場合ですが、ElasticBeanstalkにデプロイされたアプリケーションの、標準出力のログ。
  - 以下はAWSに問い合わせて教えてもらった内容。
    - Node.jsのアプリケーションは、デフォルトではsystemdにより起動されており、設定ファイルは/etc/systemd/system/web.serviceとなっております。
    - 当該ファイルのStandardOutputとStandardErrorにはsyslogが指定されており、/etc/rsyslog.d/web.confにより、Node.jsのアプリケーションの標準出力のログは/var/log/web.stdout.logに出力されております。
Github Actions + AWS CLIでCDしたECSタスクのIPアドレスをWAF IPsetに登録する

# やりたいこと

開発環境なのでwafを設定している。
ただし一部spaなのでデプロイしたIPでは許可してやりたい

# 結論

```bash
CLUSTER="XXX_CLUSTER_NAME"
SERVICE="XXX_SERVICE_NAME"
IPSET="XXX_IPSET_NAME"

IPS=$(aws ecs list-tasks --cluster $CLUSTER --service $SERVICE --query 'taskArns' --output text | \
           xargs aws ecs describe-tasks --cluster $CLUSTER --query 'tasks[*].attachments[0].details[?name==`networkInterfaceId`].value' --output text --tasks | \
           xargs aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text --network-interface-ids | \
           awk '{print $1"/32"}')
IP_SET=$(aws wafv2 list-ip-sets --scope CLOUDFRONT --region=us-east-1 --query "IPSets[?Name=='$IPSET'][].{Id:Id,LockToken:LockToken}" --output json)
IP_SET_ID=$(echo "${IP_SET}" | jq -r '.[0].Id')
LOCK_TOKEN=$(echo "${IP_SET}" | jq -r '.[0].LockToken')
aws wafv2 update-ip-set --name $IPSET --scope CLOUDFRONT --region=us-east-1 --id "$IP_SET_ID" --addresses "$IPS" --lock-token "$LOCK_TOKEN"
```


解説

分けて考える。

IPSの箇所は以下の4つのコマンドに分けられる。

```bash
# ①
aws ecs list-tasks --cluster $CLUSTER --service $SERVICE --query 'taskArns' --output text
# ②
xargs aws ecs describe-tasks --cluster $CLUSTER --query 'tasks[*].attachments[0].details[?name==`networkInterfaceId`].value' --output text --tasks
# ③
xargs aws ec2 describe-network-interfaces --query 'NetworkInterfaces[*].Association.PublicIp' --output text --network-interface-ids
# ④
awk '{print $1"/32"}'
```

それぞれ解説すると
①: ECSクラスターとサービスからタスクARNの一覧を取得する
②: タスクARNからネットワークインターフェースIDの一覧を取得する
③: ネットワークインターフェースIDからパブリックIPアドレスの一覧を取得する
④: 取得したパブリックIPアドレスに"/32"を追加し、CIDR形式に変換する

そうして最終的に出力される④の結果をIPSの変数に入れている。

以下のコマンドについて解説を行います。

```bash
# ⑤
IP_SET=$(aws wafv2 list-ip-sets --scope CLOUDFRONT --region=us-east-1 --query "IPSets[?Name=='$IPSET'][].{Id:Id,LockToken:LockToken}" --output json)
# ⑥
IP_SET_ID=$(echo "${IP_SET}" | jq -r '.[0].Id')
# ⑦
LOCK_TOKEN=$(echo "${IP_SET}" | jq -r '.[0].LockToken')
# ⑧
aws wafv2 update-ip-set --name $IPSET --scope CLOUDFRONT --region=us-east-1 --id "$IP_SET_ID" --addresses "$IPS" --lock-token "$LOCK_TOKEN"
```

⑤: AWS WAF v2 の list-ip-sets コマンドを使って、指定した IP セット名 (IPSET) を持つ IP セットの情報を取得します。スコープは CLOUDFRONT、リージョンは us-east-1 に設定されています。結果は JSON 形式で取得され、Id と LockToken のみが抽出されます。

⑥: 取得した IP_SET の JSON から、jq コマンドを使って IP セットの ID を抽出します。

⑦: 同様に、取得した IP_SET の JSON から、jq コマンドを使って IP セットの LockToken を抽出します。

⑧: aws wafv2 update-ip-set コマンドを使って、指定した IP セット名 (IPSET)、スコープ (CLOUDFRONT)、リージョン (us-east-1)、IP セット ID (IP_SET_ID)、IP アドレス (IPS)、およびロックトークン (LOCK_TOKEN) を使用して IP セットを更新します。

これらのコマンドを実行することで、AWS WAF の IP セットに ECS タスクの IP アドレスが登録され、開発環境でのアクセス制限が適切に行われるようになります。
Composeで1dp以下を設定したいときに使える機能

# 初めに
今回は、Compose実装で1dp以下を指定したい時に使えるものを書いていこうと思います
### 本文
DP指定時に下記を設定することで1dp以下を綺麗に指定することができます
```kotlin
Dp.Hairline,
```
### 最後に
備忘録ように残してありますがどなたかのお役に立てれば幸いです
【ReactHooks】useCallbackについて

## useCallbackとは
パフォーマンス最適化を行うためのフックです。**メモ化されたコールバック関数を返します**。依存関係の変更がない場合にキャッシュされた関数を返すため、余分な再レンダリングを防げます。

通常、Reactコンポーネントが再レンダリングされるたびに、コンポーネント内で定義されたすべての関数が再定義されますが、これはパフォーマンス上の問題を引き起こす可能性があります。なので、`useCallback`関数を使用し(コールバック関数をメモ化して)不必要な再レンダリングを防止するようです。

※メモ化とは、関数の呼び出しで同じ入力が再発生した時にキャッシュした結果を返すことです。キャッシュされた結果を再計算せずに返すことができるので、2回目以降の同じ入力の関数呼び出しに対するコストが削減されます。

## 使い方
2つの引数が必要です。1つ目の引数は、**メモ化したいコールバック関数**。2つ目の引数は、**依存する値の配列**です。依存する値が変更された場合にのみ、新しいコールバック関数が作成されます。

#### 基本型
```javascript
useCallback(callbackFunction, [deps]);
```

#### 例
```javascript
import React, { useCallback, useState } from 'react';

function App() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
```
`useCallback`を使用して **`increment`関数をメモ化**。`count`の値が変更された場合にのみ新しい関数が作成されます。1クリックに1ずつ増加するボタンです。

なお、`useCallback`を使用しない例です。
```javascript
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}
```
ボタンがクリックされるたびに`handleClick`関数が再定義されます。

再レンダーのコストについて、以下を参考にさせていただきました。

https://qiita.com/seira/items/8a170cc950241a8fdb23#%E5%86%8D%E3%83%AC%E3%83%B3%E3%83%80%E3%83%BC%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B3%E3%82%B9%E3%83%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E6%A4%9C%E8%A8%BC%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B

すべてのコンポーネントが再レンダーされる場合の負荷が想像できますね...
以上です。
Lambdaの意外な罠？勝手にリトライされる問題に遭遇

AWS Lambdaの関数でタイムアウトエラーが発生し、意図しないリトライが行われる問題に遭遇しました。
CloudWatchで確認してみると、関数が3回実行されていました。

リトライするような設定は特にしていなかったので、この現象は「クラウド破産」という文脈でちょっと怖い事態。

## 調べる

調べてみると、以下が見つかりました。

> Lambda manages the function's asynchronous event queue and attempts to retry on errors. If the function returns an error, Lambda attempts to run it two more times, with a one-minute wait between the first two attempts, and two minutes between the second and third attempts. Function errors include errors returned by the function's code and errors returned by the function's runtime, such as timeouts.

https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html?icmpid=docs_lambda_help


要するに、
Lambdaは関数の非同期イベントキューを管理し、エラーが発生した場合にリトライを試みます。
ここで関数がエラーを返すと、1回目と2回目の試行の間に1分間、2回目と3回目の試行の間に2分間待機して、**2回追加で実行を試みます**。
この関数エラーには、関数のコード自体から返されるエラーと、**タイムアウト**などのランタイムから返されるエラーが含まれます。

つまり、初回と併せて、3回実行されることになります。

### 非同期イベント？

この非同期イベントには
- Amazon Simple Storage Service (Amazon S3)
- Amazon Simple Notification Service (Amazon SNS) 
- AWS EventBridge

などがあります。
今回は、まさしくこのEvent BridgeをトリガーにしてLambdaを実行していました。


## 結論

**「非同期イベントでLambdaを実行する場合、明示的に設定していなくても自動でリトライされる」**
CSPアカウントを条件付きアクセスでブロックする方法

# はじめに
またまたCSP関連の話になります。Azureの顧客テナントに対するCSPパートナーのアカウント（以下CSPアカウント）のアクセスを制限機能がGAしましたので、その詳細を確認しました。

# CSPアカウントによる顧客テナントへのアクセス
CSPから払い出されたAzure環境には「外部プリンシパル」という特殊なオブジェクトが存在し、その中ではCSPアカウントに対する代理管理者権限が割り当てられています。具体的には、サブスクリプションスコープで『所有者』、AzureADロールとして『グローバル管理者』といった超強力な権限が割り当たっています。
もちろんCSPアカウントを使って顧客テナントで定常的な操作を行うことはありませんが、例えば、__MSサポートへの問い合わせ__ や __節約プランの購入__ など、一部の特殊な操作についてはCSPアカウントでないとできないことがありますので、そういった場合にCSPアカウントで顧客テナントにアクセスすることがあります。
なお、CSPアカウントは、通常のAzureポータルからのアクセスはできず、パートナーセンターというCSP管理ポータルを経由して顧客テナントにアクセスします。

## CSPアカウントのアクセスを制限したい
顧客のポリシーによっては、強権限であるCSPアカウントのアクセスは抑止すべき、といったケースがあります。CSPアカウントのアクセスを抑止することで一部運用が制限される可能性がありますが、それよりポリシー順守を優先したいと仰る企業様は一定数いらっしゃいます。その要求を満たすためには、これまではAzureADの __条件付きアクセスポリシー__ で、「すべてのユーザー」もしくは「すべてのゲストと外部ユーザー」に対するアクセス制限をかける必要がありました。というのも、CSPアカウントは特殊なアカウントになるため、CSPアカウントそのものをピンポイントで指定することができなかったためです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/2df4a401-16b6-5db2-3ba0-71341b0d8d28.png)

条件付きアクセスポリシーを丁寧に設定すれば実現はできますが、逆にテナントにアクセスさせたいユーザーは全て「対象外」の方に設定しないといけないので、設定があべこべになり運用が煩雑になってしまうというデメリットがありました。

## 条件付きアクセスの対象を細かく設定できるようになった
この設定は2022年からプレビューとして公開されていましたがこの度GAしました。ポリシー定義の「割当」→「対象」→「ユーザーとグループの選択」→「ゲストまたは外部ユーザー」から対象項目を細かく選択できるようになりました。CSPアカウントを対象としたい場合は __「サービスプロバイダーユーザー」__ が該当します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/21af1f0b-1e95-07bf-593b-a5be3693c706.png)

## 実機確認
早速動作検証してみました。
まず、顧客テナント側の条件付きアクセスポリシーで「サービスプロバイダーユーザー」のアクセスをブロックするポリシーを作成し有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/08184061-8769-9feb-f504-aa25424d4748.png)

ポリシーの適用されるまで数分待ってから、今度はCSPアカウントにて、パートナーセンターから顧客テナントにアクセスしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/5d97d23b-59ef-346a-d742-063e7c2a778a.png)

CSPアカウントでAzureのサインイン画面を突破すると、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/6846beec-0f02-01c1-1694-eb0046713f0f.png)

ちゃんとブロックされました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199489/f1ad181b-e715-d791-4ed0-9a19198994c9.png)

## 留意点
機能自体は問題なく使えることを確認しましたが、先述した通り、CSPアカウントのアクセスをブロックすることでCSP側で操作すべき作業ができなくなります。その作業内容を網羅的に上げるのは難しいのですが、その影響を受けて、顧客テナント側で余計な調整・対応が必要になったり、スピード感が出なくなったりする可能性があります。本設定を入れる際には、まずCSP管理側と相談していただき、CSP管理側にも認識してもらうことと、それにより発生するリスクの対処法を考えておくことをお勧めします。
 
# おわりに
3月は期末のバタバタで、ブログ自体は軽め、かつ、あまり一般的な話ではありませんでしたが、みなさんの参考になれば幸いです。なお、今回のネタ元はMicrosoftサポートチームの下記ブログです。Microsoftサポートのブログですが、中の人が実際対応された様々な問い合わせの中で”これは公開した方がよい”とされたものが記事になっていますので、現場目線の内容が多く大変参考になります！こちらも是非ご参考ください。

https://jpazureid.github.io/blog/azure-active-directory/new-capolicy-for-csp-account/

それではまた！
TOMLファイル形式のVersionCatalogをMavenLocalに公開する

## はじめに
やろうと思った時にやり方を忘れていたのでメモ。

## 公開する側
### build.gradle(.kts)の設定
rootのbuild.gradle(.kts)に、
- 公開するために必要なプラグイン
- 公開したいTOMLのパスの設定
- 公開に関する設定

を追加する。

```kotlin:build.gradle.kts
plugins {
    id("version-catalog")
    id("maven-publish")
}

catalog {
    versionCatalog {
        from(files("gradle/libs.versions.toml"))
    }
}

publishing{
    publications{
        create<MavenPublication>("catalog"){
            //このfromの設定により、versionCatalogとして公開できる
            from(components["versionCatalog"])
            groupId = "com.sample"
            artifactId = "catalog-publishing-sample"
            version = "1.0.0"

        }
    }
}
```


### MavenLocalに公開する
`./gradlew PublishToMavenLocal`
を叩くと、metadata,pom,module,tomlなどが生成される　

## 読み込む側
### settings.gradle(.kts)の設定
読み込みたいプロジェクトのsettings.gradle(.kts)に
- MavenLocal()の追加(dependencyResolutionManagementの中だけで◎)
- MavenLovalに公開されてるgroup,artifact,versionを指定してのVersionCatalogの追加

を行う

```kotlin:settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        mavenLocal()  //追加
    }
    versionCatalogs {
        create("mavenLocalLibs") {
            from("com.sample:catalog-publishing-sample:1.0.0")
        }
    }
}

```

### 読み込んだcatalogを使う

読み込み時につけた名前で使える。
```groovy:build.gradle(:app)
implementation mavenLocalLibs.activity.compose
```


# おしまい
参考
https://docs.gradle.org/current/userguide/publishing_maven.html
https://docs.gradle.org/current/userguide/platforms.html#sec:version-catalog-plugin
ChatGPTをCondaのPythonで



![SS 192.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/a41cd967-f549-8677-1efa-2e99bf69518d.png) ノーコードの前にとりあえず慣れた環境で、、、

## やること
conda環境でchatGPTのAPIをインストールして使います。
自分用メモ。

## 環境
- conda環境（M1 mac, miniforge, python 3.9.5）

## 参考

https://qiita.com/ekusiadadus/items/3304c4832106b244a60f

https://qiita.com/taxinumber1729/items/d0b09b506eef76ebd918

https://qiita.com/N4RU/items/0dc2317cc28193162661

わかりやすくてすばらしい記事をありがとうございます。

## ステップ１ ： 無料登録する

https://openai.com/blog/chatgpt

はじめてChatGPTを使う場合や↑から[Try ChatGPT]を押して[Sign up]でメアドを登録する。
指示に従うとChatGPTをブラウザで使えるようになる。

## ステップ２ ： APIキーを得る
APIは無料で少し使えるらしい。
https://platform.openai.com/account/api-keys

↑のリンクに入る。
[+ Create new secret key] をクリックしてAPIキーを生成
ポップアップ画面の文字列をどこかにコピペで保存しておく（再表示不可なので注意）

## ステップ３ ： conda環境でライブラリを導入する

ターミナルでconda環境をアクティベートする。
以下のコマンドでライブラリを導入する
```
conda install openai
```

## ステップ４ ： 試す

https://qiita.com/N4RU/items/0dc2317cc28193162661
↑ の「実装」の項目のコードを実行します。

上記の記事の対話形式バージョンを実行し、
User: の後に会話文を入力します。すると、下記のような結果になります。
![SS 328.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565043/6334610c-09f2-6c93-2177-88b8747d6f3f.png)


## ステップ５ ：有料にアップグレードする

有料にアップグレードする場合は、さきほどのChatGPTのブラウザのどこかでアップデートできたはず。

## おわりに
あとでもうすこし意味のある記事にするつもりですがとりいそぎ。