OmniAICreator/Qiita-1.07M huggingface dataset:
二択を繰り返すことで新しい本と出逢えるwebアプリを作った

# 作ったwebアプリについて

左右に表れる２つの本から1つを選ぶことを繰り返すことで, 新しい本に出逢えるwebアプリを作りました.
宣伝です.

https://horinator.pages.dev/

↑web版

https://play.google.com/store/apps/details?id=dev.pages.horinator.twa&pli=1

↑androidアプリ版

# 概要

本を買う際に, amazonなどのECサイトで買うことも多いですが, ときどき本屋にも行きたくなります.
......と言うのは, ECサイトの本と触れる箇所は, 検索(ほしい本)･レコメンド(類似本)･ランキング(人気本)がほとんどで, 本屋で得られる｢本棚を眺め, なんとなく出逢った本を手に取る｣という出逢いの経験が得らないからです.
なので, web上で類似本でも人気本でもない新しい本と出逢えるwebアプリを作りました.

２つの本の中から｢どちらが手に取りたいか｣を何回か選ぶことで, ユーザーの趣味嗜好を深掘りし, オススメの本をピックアップするサービスです.
名前の由来は｢深掘り｣+｢akinatorっぽい｣ということでhorinatorにしました(知人につけてもらった).

# アルゴリズムについて

このwebアプリは
* 本を二択から選択
* オススメの本紹介

の2ステップで構成されてます.
それぞれについて説明していきます.

## 本を二択から選択 - 階層クラスタリング

まずはじめに, 本の利用傾向の近さから埋め込み表現を作ります.
(ここのところは, すでに色々と解説があると思うので省略します)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950973/7c55f992-6e37-0039-55a8-e94da8ef8d08.png)

次に, 各書籍間で階層クラスタリングを行います.
ユーザーの二択は, 階層クラスタリングでできた木構造の, 片側を選ぶか, もう片側を選ぶかで表現されます.
そして選択が終わると, 木構造の深さを一つ進めることで, ユーザーの興味を深掘りしていきます.

一般的な階層クラスタリング手法は, クラスタリングをボトムアップで行います.
しかし今回の場合では
 
* 全部を完全にクラスタリングするには計算量が多すぎること
* 木構造の深い階層の情報は不要なこと

から階層クラスタリングをトップダウンで行いました.
手法は, クラスタ数2のK-meansを繰り返し行うことで実装しました.
(｢[統計的学習の基礎](https://www.amazon.co.jp/dp/432012362X)｣いわくそれがよく使われる手法のようです)

## オススメの本紹介 - spectral relaxation

｢オススメの本紹介｣では選択された本のクラスターを一覧表示させています.
ここで大事になってくるのが, ｢本の並び方をどうするか｣という問題です.

｢本の人気順でソートする｣というのも一つの手だとは思いますが, ｢人気本でもない新しい本と出逢う｣というコンセプトに反します.
なので今回は本のバリエーションが極力豊かになるようにソートしました.

ただ, これを愚直に計算するとなると, かなりの計算量が必要になるため, spectral relaxationを用いることで近似解を計算しました.
具体的な手法に関してはかなり込み入った話になるので｢[Novelty and Diversity in Top-N Recommendation -- Analysis and Evaluation](https://dl.acm.org/doi/10.1145/1944339.1944341)｣を読んでいただければ幸いです.
(いつか別途解説の記事を書くかも)

# 実装について

実装は, [Flet](https://flet.dev/)というpython×flutterなライブラリで実装しました.
pythonに慣れているかたなら, 簡単にそれなりの見栄えのものができるのでオススメです.
筆者はpythonぐらいしかできる言語がないのでこれを選択. 
結果としてまあよかったかなと思っています.
webアプリ以外にもマルチプラットフォームでつくれるので, ちょっとしたツールならば, これで作るのが良さそうです.

# 今後改良したいこと

* 事前に｢小説｣｢新書･実用書｣｢マンガ｣などのジャンルフィルターを設けたい
* マンガやライトノベルは冊数が多いため, 一つにまとめて学習したい
    * ただ, 結構表記揺れがあるのでどうしようか......
* 今は, ｢本を二択から選択｣段階と｢オススメの本紹介｣段階の切り替えを冊数で行っているが, これをよりラインナップに即した形で決定したい
    * X-meansを応用すればいけるかも
ChatGPTを使って読書の幅が広がった話

## ミノ駆動本を読んでいて
　[ITエンジニア本大賞2023](https://www.shoeisha.co.jp/campaign/award/result)でも技術書部門で大賞を取ったみなさんご存知の[ミノ駆動本](https://gihyo.jp/book/2022/978-4-297-12783-1)。私も買ってJavaのコードをPythonに変換しながら写経していたのですが、私はJavaは全く経験がなかったのでJava→Pythonの変換で学習コストがかかって、とても良い本なのにだんだんこの本で勉強するのが億劫になってしまいました。

　そこで最近ChatGPTで遊んでいる時に、ふと積本たちが視界に入り「ChatGPTを使えばこの問題解決するんじゃね？」、と思いChatGPTにJava→Pythonの変換をさせてみることにしました。

## ChatGPTでJava→Pythonの変換
　それでは試しにミノ駆動本のJavaのサンプルコードをChatGPTでPythonに変換させてみましょう。サンプルコードは5章のMagicPoint.javaを使用、ChatGPTには今流行りの深津式プロンプトで入力します。

ChatGPT入力

```
以下の成約条件のもとJavaコードをPythonに変換して下さい。
# 制約条件
・元のコードと同じようにコメントも入れること
・型ヒントを使うこと
・変更の内容を説明すること

# Javaコード
import java.util.ArrayList;
import java.util.List;

/** 魔法力 */
class MagicPoint {
  private int currentAmount;
  private int originalMaxAmount;
  private final List<Integer> maxIncrements;

  // 省略
  MagicPoint() {
    originalMaxAmount = 100;
    maxIncrements = new ArrayList<>();
  }

  /** @return 現在の魔法力残量 */
  int current() {
    return currentAmount;
  }

  /** @return 魔法力の最大量 */
  int max() {
    int amount = originalMaxAmount;
    for (int each : maxIncrements) {
      amount += each;
    }
    return amount;
  }

  /**
   * 魔法力を回復する
   * @param recoveryAmount 回復量
   */
  void recover(final int recoveryAmount) {
    currentAmount = Math.min(currentAmount + recoveryAmount, max());
  }

  /**
   * 魔法力を消費する
   * @param consumeAmount 消費量
   */
  void consume(final int consumeAmount) {
    // 省略
  }
}
```

ChatGPT出力

```
以下は、上記のJavaコードをPythonに変換したものです。

from typing import List

class MagicPoint:
    def __init__(self):
        self.current_amount = 0
        self.original_max_amount = 100
        self.max_increments = []

    def current(self) -> int:
        """現在の魔法力残量を返す"""
        return self.current_amount

    def max(self) -> int:
        """魔法力の最大量を返す"""
        return self.original_max_amount + sum(self.max_increments)

    def recover(self, recovery_amount: int) -> None:
        """魔法力を回復する"""
        self.current_amount = min(self.current_amount + recovery_amount, self.max())

    def consume(self, consume_amount: int) -> None:
        """魔法力を消費する"""
        # 省略
        pass

このコードは、Javaで定義された魔法力を表すクラスをPythonに変換したものです。変換され
たコードは、Javaのコードと構造が似ていますが、いくつかの点で異なります。

Pythonでは、インスタンス変数の宣言時に値を割り当てる必要はありません。変数は初期化され、
必要に応じて値を代入することができます。従って、Pythonのコンストラクタでは、Javaで行わ
れた値の割り当てが省略されます。

Javaのリストは、Pythonのリストに変換されますが、Pythonではリストを宣言する際にデータ型
を指定する必要はありません。ただし、Pythonでは型ヒントとして使用されるため、Pythonコード
ではリストに対しても型ヒントが提供されています。

Javaではvoidメソッドを使用しているため、PythonコードでもNoneを返すことになります。
```

　いい感じに変換できています。よく見ると変数名がJavaで使われるキャメル型からPythonで使われるスネーク型に変換されていたり、元のコードではMaxメソッドでfor文を使って計算しているところをPythonではsumを使っていたり、言語の特徴に合わせて変換してくれています。

　これならミノ駆動本以外の特定のプログラミングで書かれたデザインパターンや開発手法の技術書でも自分の得意な言語に変換できるので、読書の幅が広がりますね。

## 最後に

　ChatGPTに触り始めてから4ヶ月くらい経つのですが、なぜこういった使い方になかなか気が付かなかったんだろうと思っています。そういった事柄はまだ色んなところに転がっているのかもしれませんね。

　この記事が誰かの勉強の手助けになれば幸いです。
CouchDB をさわってみたメモ

作成：2023年4月11日
CouchDB を触ってみたくなったので、触ったメモを残します。

# Docker で CouchDB を導入とテスト

Docker で CouchDB を立てて、DB やデータの追加を色々試してみます。
```:console
# 起動
% docker run -it -p 5984:5984 --name my-couchdb -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password apache/couchdb:3.3.1

# 試しにDB追加
% curl -X PUT http://admin:password@127.0.0.1:5984/_users
{"ok":true}
% curl -X PUT http://admin:password@127.0.0.1:5984/_replicator
{"ok":true}
% curl -X PUT http://admin:password@127.0.0.1:5984/_global_changes
{"ok":true}
% curl -X GET http://admin:password@127.0.0.1:5984/_all_dbs       
["_global_changes","_replicator","_users"]

# 試しにデータ追加してデータ確認
% curl -X PUT http://admin:password@127.0.0.1:5984/test_db
{"ok":true}
% curl -X PUT http://admin:password@127.0.0.1:5984/test_db/test_doc -d '{"name":"hoge"}'
{"ok":true,"id":"test_doc","rev":"1-8efae2575443e0b3ff301aee512fc284"}
% curl http://admin:password@127.0.0.1:5984/test_db/test_doc
{"_id":"test_doc","_rev":"1-8efae2575443e0b3ff301aee512fc284","name":"hoge"}
% curl -X GET http://admin:password@127.0.0.1:5984/_all_dbs
["_global_changes","_replicator","_users","test_db"]

# 試しにDB削除
% curl -X DELETE http://admin:password@127.0.0.1:5984/_replicator
{"ok":true}
% curl -X GET http://admin:password@127.0.0.1:5984/_all_dbs        
["_global_changes","_users","test_db"]
```

# 参考
・[公式Github](https://github.com/apache/couchdb-docker)
　　CouchDB Docker image の使い方
・[2.1. Single Node Setup](https://docs.couchdb.org/en/stable/setup/index.html#setup)
・[公式Tutorial](https://docs.couchdb.org/en/stable/intro/tour.html)
・[CouchDBの構築 on Ubuntu](https://varlal.com/www/home/archives/1745)
　　Docker で CouchDB 導入の記事
・[Golang で CouchDB のデータを作成 (Create)](https://qiita.com/ekzemplaro/items/c06b32e71eb62229dcf1)

## 検索
・[CouchDB のきほんチートシート curl 編](https://qiita.com/usagi/items/ffe7b2cde9f2f8b1b7f4)
　　view を使った絞り込み検索
・[Cloudant(CouchDB) の MapReduce ビュー](https://dotnsf.blog.jp/archives/1077038369.html)
・[1.1. API Basics](https://docs.couchdb.org/en/stable/api/basics.html#api-basics)
・[1.3.6. /db/_find](https://docs.couchdb.org/en/stable/api/database/find.html)
ChatGPTを活用したAWS環境のコスト削減

## 1. はじめに

話題のChatGPTを活用してAWS環境のコスト削減を実現した事例について紹介します。

## 2. 背景

対象のAWS環境は社内教育目的で利用されており、リソースが放置されることが課題でした。
月額利用料は約120万円で、EC2とRDSが大部分を占めています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/b1278b66-1f1e-3168-f18e-78d0c30739c3.png)

## 3. ChatGPTとは

ChatGPTはOpenAI社が開発した自然言語モデルで、自然な会話を通じて情報検索が可能です。
例として、言語も指定せずにChatGPTに質問してみた結果、Pythonのコードと、その説明まで丁寧に回答してくれます。

![image (67).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/ff34e423-24fe-e637-ff4a-10901c5fd7a1.png)

## 4. まずは雑に聞く
どのような回答が返ってくるか検証してみました。
![Inkedimage (63)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/7f0e4aea-1bde-b5a9-f73a-271169798ba9.jpeg)

ごくごく当たり前の回答が返ってきました。
これでは運用方法としては物足りないため、自分を考えることとしました。

## 5. 考案した構成図

EC2とRDSを自動停止させる運用構成を考案しました。
Lambdaを使ってリソースにタグを付与し、Systems Managerで自動停止させます。
さらに、利用者がリソースの起動時間を簡単に変更できる機能も実現しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/7736c6b1-6533-bc33-eea6-e5af53372012.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/87583afd-4333-69db-4655-7ee4397b622f.png)

## 6. ChatGPTを活用した実装

ChatGPTはLambdaやSystems Managerのコード作成に活用し、構築コストを大幅に削減しました。

![image (64).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/5a792e19-825d-eee6-14ce-24ea4270d6db.png)

## 7. コスト削減効果

本構成により、EC2とRDSの月額利用料を約50%削減できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2988434/6335cbf6-400f-93df-d4ac-ee29c89de18e.png)

## 8. まとめ

・ChatGPTを活用して月額利用料と構築コストを削減！
・ChatGPTは万能ではなく、人間とAIとで考える部分を分けることが重要。
　→GPT-4等のこれからのAIの進化に期待

## 参考文献
・タグを指定するだけでEC2インスタンスを自動起動・停止する
https://qiita.com/uzresk/items/504d52bf1042c1067dde
・タグを指定するだけでRDSを自動起動・停止する
https://qiita.com/uzresk/items/5094ad47fc56805c42e5
【基礎知識】Python用ライブラリsqlite3について

## はじめに

 #### 本記事について
　本記事は、SQlite3を初めて利用する方向けの基本的な内容です。公式ドキュメントのチュートリアルを軸に構成しています。

 #### SQLiteについて
SQLiteはサーバーを必要とせず、データベースを扱うことができるC言語ライブラリです。
   軽量なデータベースであるため、プロトタイプの作成や、小規模のWebサイト・アプリケーションでの使用に適しています。

  ##### **sqlite3はPythonの標準ライブラリ**であり、**SQLiteをPythonから扱うためのモジュール**です。
  sqlite3は、SQLite 3.7.15 以降が必要です。



## SQLite3の利用方法

①モジュールを使用して SQLite データベースを作成
tutorial.dbが既に存在する場合は、データベースへの接続を確立します。（※その場合、ファイルパスを指定します）

```import sqlite3
# データベースに接続します
con = sqlite3.connect("tutorial.db")
```
返されるConnectionオブジェクトは、con ディスク上のデータベースへの接続を表します。

②データベース カーソルを作成

```# カーソルを作成する
cur = con.cursor()
```

:::note info
 **データベースカーソル**
　データベース内のデータを操作するために使用されるオブジェクト
:::

③テーブルを作成

:::note
カーソルオブジェクトのexecute()メソッドで、テーブル作成のSQL文を実行します
:::

```
cur.execute("CREATE TABLE movie(title, year, score)")
```

>* テーブル
>    * movie

>* 列(columns)
>   * タイトル(title)
>    * リリース年(year)
>    * レビュー・スコア(score)

⑤データを追加

```
cur.execute("""
    INSERT INTO movie VALUES
        ('Monty Python and the Holy Grail', 1975, 8.2),
        ('And Now for Something Completely Different', 1971, 7.5)
""")
```

⑥トランザクションをコミット

```
con.commit()
```

:::note info
SQLiteでは、デフォルトでautocommitが有効になっているため、通常この工程は必要ありません。
しかしプログラムが大規模になる場合は、明示的にコミットすることが推奨されるようです。
:::

⑦接続を閉じる

```
con.close()
```



## SQLの基本的な操作

|SQL文 |	CRUD |	機能 |
:-----------|------------:|:------------:|
|INSERT文 |	Create | データ追加 |
|SELECT文 |	Read |	データ取得 |
|UPDATE文 |	Update |	データ更新 |
|DELETE文 |	Delete |	データ削除 |

> 引用:[【MySQL入門③】データベースの基本操作CRUDを解説！](https://snome.jp/database/db-intro-3/)


## SQLiteの基本的な操作方法


```
# カーソルを作成する
cur = con.cursor()

# データ検索
cur.execute('SELECT * FROM items')

# 取得したデータはカーソルの中に入る
for row in cur:
    print(row)

# データ更新
cur.execute('UPDATE items SET price = 260 WHERE id = "3"')
# データ削除
cur.execute('DELETE FROM items WHERE id = "2"')
```



## 備考
 #### PythonでSQL文を実行する他の方法

PythonでSQL文を実行する方法としてsqlite3は、Pythonのコード内で直接SQLを実行できるため、最も簡単な方法だそうです。

しかし、アプリケーションによってはより高度な操作が必要な場合もあります。
その時の選択肢として別のライブラリも簡単に調べました。

以下のライブラリは、基本的なSQL文の書き方はすべて同じです。

+ **SQLAlchemy**：SQLAlchemyは、Pythonで書かれたSQLツールキットで、SQL文をPythonのコードとして書くことができます。

+ **PyMySQL**：PyMySQLは、Pythonで書かれたMySQLライブラリです。PythonからMySQLに直接アクセスすることができます。

+ **psycopg2**：psycopg2は、Pythonで書かれたPostgreSQLライブラリで、PostgreSQLにアクセスするために使用されます。



## まとめ

今回私は、ローカルで使用するPythonアプリケーションにSQlite3を利用するため、SQLiteについて基本的な操作方法を調べました。はじめはSQliteとSQlite3の違いに混乱しましたが、調べることで「**SQLiteをPythonから扱うためのモジュール**」がSQlite3だと認識することができました。
SQLiteは軽量で扱いやすいデータベースとして様々なアプリケーションに利用されているそうです。本記事がSQLiteを初めて利用する方にとって役立つことを願っています！

閲覧ありがとうございました。
 
#### 参考文献

[【MySQL入門③】データベースの基本操作CRUDを解説！](https://snome.jp/database/db-intro-3/)
[公式ドキュメント](https://docs.python.org/ja/3/library/sqlite3.html)
[PyMOTW](https://ja.pymotw.com/2/sqlite3/)
macportsのインストールからgradsを使うまで

gradsってそんな簡単に使えると！？（謎の方言
という話をランチタイムにしたので、僕がMacでgradsを使えるようにする手順を整理します。
僕はmacportsというパッケージ管理システムを使っています。
直接入れてもいいし、他のパッケージ管理システム（homebrewとか）を使っても構いません。
同じようにする必要は無いです！

# macportsのインストール
1. 公式サイト[The MacPorts](https://www.macports.org/index.php)にアクセスする。
1. タブの`Installing MacPorts`をクリックする。
1. いろいろやり方は書いていますが、Quickstartで困ったことはないので、これに従い、Command Line Developer Toolをインストールする。
    ``` terminal
    $ xcode-select --install
    ```
    このような画面が表示されるので
    ![](https://techracho.bpsinc.jp/wp-content/uploads/2021/03/202103-xcode-select-dialog.png)
    「インストール」をクリックする。

    ※結構時間がかかります。ご注意を。

1. [Installing MacPorts](https://www.macports.org/install.php)に戻って、「2.Install MacPorts for your version of the Mac operating system:」の、自分のMacのOSに対応したものをダウンロードする。ちなみに、2023年4月11日現在、Ventura用をダウンロードすると、「MacPorts-2.8.1-13-Ventura.pkg」 になる。
1. これをポチポチと進めていって下さい。
    ![スクリーンショット 2023-04-11 18.13.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252703/5009189c-f635-2e4b-39f0-26442ae3400e.png)
    インストールされたら完了。
1. 使っているシェルのプロファイル（デフォルトはzsh）にMacportsのパスが通っていれば、問題なし。
    ```terminal
    $ less ~/.zprofile
    ```

    ```~/.zprofile
    # MacPorts Installer addition on 20＊＊-**-**_at_**:**:**: adding an appropriate PATH variable for use with MacPorts.
    export PATH="/opt/local/bin:/opt/local/sbin:$PATH"
    # Finished adapting your PATH environment variable for use with MacPorts.
     ```

ここまででmacportsのインストールが完了。
つまり、`/opt/local/`にmacportsを介してインストールしたものが行くことになる（特にPATHを指定しなければ）
つまりつまり、Linuxで言うところのroot権限、管理者権限が無いとmacportsを使ってアプリケーションをインストールできない。
（そもそもその権限がないとmacports自体がインストール出来ないのだけど）

# gradsのインストール
1. macportsでgradsをインストールするコマンドを入力する。
   ```terminal
    $ sudo port install grads
   ```
1. 色々と必要なもの...netcdfとか...も一緒にインストールしてくれるので、待つ。
1. インストールが終わったら、gradsのPATHを通す。
    ```terminal
    $ vi ~/.zprofile
    ```

    ```.zprofile
    export GAUDPT=/opt/local/share/grads/udpt
    export GADDIR=/opt/local/share/grads
    ```
    ※おそらく、GASCRPなんかも実質必要なのだろうけど、取り急ぎこのページではここまで。
1. gradsが動くことを確認する。
    ```terminal
    $ grads

    Grid Analysis and Display System (GrADS) Version 2.2.1
    Copyright (C) 1988-2018 by George Mason University
    GrADS comes with ABSOLUTELY NO WARRANTY
    See file COPYRIGHT for more information

    Config: v2.2.1 little-endian readline grib2 netcdf hdf4-sds hdf5 opendap-grids geotiff shapefile
    Issue 'q config' and 'q gxconfig' commands for more detailed configuration information
    Landscape mode? ('n' for portrait):  
    GX Package Initialization: Size = 11 8.5 
    ga-> 
    ```
以上。gradsを使うまででした。

macportsの使い方には全く触れていないので（例えばOSをアップデートしたら、macportsも入れ直す必要がとか、gradsのバージョンを変えたいとか）、躓いた時は躓いた時。
そのあたりもいつか整理できればと思います。
Storage::path() と storage_path の使い分け

たまに混同してハマるので覚え書き。
書くまでもないことかもしれませんが。

# storage_path()
ヘルパ関数。
`storage`配下のパスを返す。

https://readouble.com/laravel/6.x/ja/helpers.html#method-storage-path

# Storage::path()
Storageクラスのメソッド。
挙動は`config/filesystems.php`の設定に基づくが、インストール時点の設定では`storage/app`配下のパスを返す。

# どう使い分ければいいか
Storageクラスのメソッドを使った処理をする過程で使うなら、Storage::path()が適切。

例えばこんな風に。
ただStorage::files()はがディレクトリを返さないようになっているので、あんまり意味はないが。
```php
foreach (Storage::files('data_sources') as $_file) {
    if (is_file(Storage::path($_file))) {
        // ...
    }
}
```

storage_path()はapp以外のディレクトリにもアクセスするときに使う。
2    Slack アプリを作成してincoming Webhook URLを設定する

## 概要

- Slackにてアプリを作成してincoming Webhookを設定する方法をまとめる。

## 前提

下記の内容が完了していること。

https://qiita.com/miriwo/items/f0ae86e4cafbaa57e37e

## 方法

1. 下記にアクセスする。
    1. [https://api.slack.com/apps](https://api.slack.com/apps)
1. 対象のアプリ名をクリックする。

1. サイドバーで「Incomming Webhooks」をクリックする。

    ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/9c45045d-6f11-3a16-fe8f-9c2845d6aa47.png)

1. スライダーを「On」にする。

    ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3398cf4c-ced0-819a-847d-3728059000b3.png)

1. 「Add New Webhook to Workspace」をクリックしてWebhook URLを作成する。

    ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/cb9e04a3-0ee9-a6f3-1b2a-96f42a9a8b34.png)

1. アプリを追加するチャンネルを選択し、「許可する」をクリックする。

    ![send_message_が_miriwo-notice_Slack_ワークスペースにアクセスする権限をリクエストしています___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/7031ef6a-15fe-9378-8280-3c1d551612e9.png)

1. ターミナルからサンプルメッセージを送る事ができる。「Copy」をクリックしてターミナルに貼り付け、実行する。

    ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/86d4bdd4-c0fb-6dbc-9f89-f5c6d627bca2.png)

1. 下記の様に「Hello world!」が出ればここまでの作業に問題はない。

    ![06_send_lambda_-_miriwo-notice_-_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3a0d3ea4-76e7-517d-2c8d-8fad7d756dc1.png)

1. あとは下記の「Copy」をクリックしてWebhook URLをコピーして各々使いたいものに使えば良い。

    ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/82e92f61-aeb7-a144-a1b5-4c9deae18fa2.png)

# 付録

- 作成したアプリの「Incoming Webhooks」のページまで遷移する方法を簡単にまとめる。
    1. 「Slack API」と検索し[https://api.slack.com/](https://api.slack.com/)にアクセスする。
    1. 「Your apps」をクリックする。
    
        ![Customize_your_workspace___Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/e8759fa0-3ff6-4f96-e113-c4fc63fb467c.png)

    1. 「sign in to your Slack account」をクリックしログインする。

        ![Slack_API__Applications___Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/c88b7c19-2aaa-5c04-e80f-6b0021a5e3dd.png)

    1. 同じブラウザの別タブで[https://api.slack.com/](https://api.slack.com/)にアクセスし、同じ様に「Your apps」をクリックする。
    1. アプリ一覧に並んでいるアプリ名をクリックする。

        ![Slack_API__Applications___Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/bc2e2a75-4c94-7658-5de6-b740282de786.png)

    1. サイドバーで「Incomming Webhooks」をクリックする。

        ![Slack_API__Applications___miriwo-notice_Slack.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/9c45045d-6f11-3a16-fe8f-9c2845d6aa47.png)

    1. 以上でいつでもWebhook URLを見ることができる。
JAZZ Sakura

ちょっと前まではNYCのWBGOをきいていたのですが、日本のJAZZのストリームを見つけて最近はずっとこれをきいています。

http://kathy.torontocast.com:3330/stream

タグに日本語が入る事があるのですが、mpg123では--utf8オプションを付けると正常に表示できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/5cd6b348-eae2-db5c-ed86-558ca681876c.png)

自画自賛ですがAtherosのpcmドライバー最高です。

渋谷毅さんの持ってない音源が流れてとっても嬉しいです。

以前はコミュニティーFMもmp3のストリーミングがあったのですが、廃止されてmpg123ではきけなくなりました。
sourcetreeでクローンに失敗する

gitのコマンドでは成功するがsourcetreeでクローンしようとすると失敗してハマったのでめも。

キーチェーン(mac)からsourcetreeのアカウントを削除しても解消しなかった。

環境設定から高度な設定を開き、アカウントを削除すると解消する。
sourcetreeを日本語にしていると環境設定から高度な設定が開けないバグがあるため、一度 English 等にする必要がある。
Spring Bootとlogback-accessでTomcatのアクセスログを出力

Spring BootでTomcatのアクセスログを出力するには```server.tomcat.accesslog.*```プロパティを使用するが、これで不足する場合には[logback-access](https://logback.qos.ch/access.html)を使用する。設定は[logback-access-spring-boot-starter](https://github.com/akkinoc/logback-access-spring-boot-starter)の依存性追加するのが簡単で、ここではこのstarterを使わずに同等の設定をする方法をメモしておく。内部的にはstarterと同等の事をやっている。


まず```logback-access```の依存性を追加する。

```groovy
plugins {
  id 'java'
  id 'org.springframework.boot' version '3.0.5'
  id 'io.spring.dependency-management' version '1.1.0'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
  mavenCentral()
}

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-web'
  testImplementation 'org.springframework.boot:spring-boot-starter-test'

  implementation 'ch.qos.logback:logback-access'
}

tasks.named('test') {
  useJUnitPlatform()
}
```

以下のようにlogback-accessの設定を追加する。アクセスログ用のlogback設定ファイルは後述で、デフォルトのファイル名は```conf/logback-access.xml```なので下記コメント行のように変更できる。

```java
import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import ch.qos.logback.access.tomcat.LogbackValve;

@Configuration
public class LogbackAccessConfig {

  @Bean
  public WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> webServerFactoryCustomizer() {
    return (factory) -> {
      LogbackValve v = new LogbackValve();
      // v.setFilename(new ClassPathResource("logback-access.xml").getPath());
      factory.addEngineValves(v);
    };
  }
}
```

<code>src/main/resources/conf/logback-access.xml</code>にアクセスログ用の設定ファイルを配置する。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <Pattern>combined</Pattern>
      <Pattern>[ACCESS] %h %l %u %r %t{yyyy-MM-dd HH:mm:ss.SSS} %s %b %D ms</Pattern>
    </encoder>
  </appender>

  <appender-ref ref="STDOUT" />
</configuration>
```

こうすると以下のようなアクセスログが表示される。

```
[ACCESS] 0:0:0:0:0:0:0:1 - - GET /hoge HTTP/1.1 2023-04-11 18:23:05.002 404 275 24 ms
```
ChatGPT が不正解と不正解の 6 つの理由

ChatGPT から最良の結果を得るには、ユーザーは、具体的で、詳細で、有益で、状況に応じたプロンプトを提供する必要があります。
ChatGPT は、多くの人々の仕事を支援しています。 ただし、OpenAI チャットボットが常に正確で役立つ回答を提供するとは限りません。 それはユーザーの「プロンプト」スキルに大きく依存します。 ChatGPT が誤って応答した理由をいくつか調べて修正してみましょう。

プロンプトが短すぎる

ChatGPT は単なるチャットボットであるため、プロンプトも簡潔にする必要があると考えるかもしれません。 ただし、ChatGPT が必要な回答を提供するのに十分な情報は提供されません。 何が欲しいのか、AIに知らせたくないのかを詳細に説明し、応答を作成する必要があります。

不特定のリマインダー

ChatGPT のような大規模言語モデル (LLM) は、膨大な量の知識とデータを保有しているため、質問が広すぎる場合、ユーザーが望むものの正確な「出力」を提供することは困難です。

漠然としたリマインダー

人間の言語は複雑で、時にはあいまいです。 ここでのあいまいさは、どのような解釈も理にかなっていることを意味すると理解されています。 質問をするときに論理的な問題が発生している可能性があります。おそらく、回答が多すぎる質問をしているために、ChatGPT はあなたが探している回答を認識していません。

たとえば、「鶏肉を最高に調理する方法」と尋ねられた場合、ChatGPT には「最高」の目標を達成するための多くの方法があります。 しかし、「健康のために鶏肉をどのように調理するか」と聞かれると、質問の範囲が狭まります。 「糖尿病患者のために鶏肉を調理する最良の方法」は、さらに良い思い出です.

コンテキストの欠如

ChatGPT は非常に状況に敏感です。 したがって、最良の結果を得るには、プロンプトにコンテキストを追加する必要があります。 ChatGPTに記事の編集を依頼すると、これがはっきりとわかります。 ブログの執筆は、本や学術論文の執筆とは大きく異なります。 ChatGPT にテキストを YouTube 動画のスクリプトに変換するよう依頼すると、出力もテレビ番組のスクリプトとは異なります。

したがって、ChatGPTが取得したいものを理解できるように、特定のキーワードで取得したいものを記述する必要があります.

限られた情報

ChatGPT の弱点は、チャットボットのトレーニング データが 2021 年 9 月以前のものしかないことです。 そのため、このチャットボットは搭載されていないという認識が多いです。 さらに、ChatGPT は情報を「偽造」して、非論理的で不正確で信頼できない回答を提供する可能性があるため、確認する必要があります。

片道交換

ChatGPT の強力な機能の 1 つは、チャット履歴全体を記憶し、フォローアップ プロンプトを解釈するためのコンテキストとして使用することです。 したがって、「応答の生成」ボタンをクリックしてプロンプトへの応答を取得する代わりに、スレッドの内容に基づいて新しいリマインダーを作成できます。

人間と同じように、時間をかけて ChatGPT と双方向の会話を行うと、ニーズに合わせて出力をすばやく微調整できます。

この記事を書くのに gpt チャットを使用しています https://nhacap4.org/nha-bang-go-toi-uu-chi-phi-voi-cong-nang-dinh-chop/
ラインブログ画像ダウンロード

base_url　にダウンロードしたいラインブログのIDを入れていただければ、
1000ページ目までダウンロードできるようになっています。

Python3.9.4の環境で動きました。
Beautifulsoup、lxml他、「見つかりません」と言われたものはググってインストールしてください。
わからないことはtwitterまで。可能な限り答えます。

    import re
    import requests
    import os, time
    import urllib.parse
    from bs4 import BeautifulSoup
    
    save_dir = './pics'
    base_url = "https://lineblog.me/ダウンロードしたいブログのID/?p={0}"
    
    def download_all():
    
        # 保存フォルダがなければ作る
        if not os.path.exists(save_dir):
            os.mkdir(save_dir)
        print(save_dir)
        # 1～1000番までのページを連番で記事一覧を表示
        for id in range(1,1000):
            download_file(id)
            #1秒待ちます。これがないと迷惑をかけてしまいます。
            time.sleep(1)
    
    # id番目の記事一覧から画像をダウンロードする
    def download_file(id):
    
        # 名前は1000番まで
        url = base_url.format(id%1000, id)
        print(url)
        response = requests.get(url)
        soup = BeautifulSoup(response.content,'lxml')
        images = soup.find_all('a', attrs={"href": re.compile(r"^https://obs.line-scdn.net/*")})
        # 画像のないページがきたら終了
        if len(images) < 1:
            print("finished")
            sys.exit(0)

        i = 1
        for target in images:
            picurl = target.get('href')
            response = requests.get(picurl)
            image = response.content
            with open(str('./pics/')+"img_"+str(id).zfill(4)+str(i).zfill(3)+str('.jpeg'), 'wb') as f: 
                f.write(image)
            i += 1
    
    # メイン処理
    if __name__ == '__main__':
        download_all()
        print("1000ページまで終了")
junit等のテスト自動化の範囲は？　全てのソースカバレッジ100％は不要

junit等のテスト自動化で、どこまで担保を取るかはどの現場でも議論になると思います。

現場でとても良いルール付けに巡り合えたので紹介したいと思います。



# 投稿の目的
無駄なテストコード作成を削減し、テストコードのメンテコスト削減

# 前提

今回はサーバーサイドの話です。
![テスト説明.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317883/52c8e7b0-fe3c-0637-b987-5695390ad8fa.png)

　・コントローラ層　：サービス層からの値を画面表示値に編集したりする責務
　・サービス層　　　：各機能処理の入り口
　・ドメイン層　　　：ビジネスロジックの責務
　　　　　　　　　　　DTO、VOの詰め替えもここで行います。
　・データアクセス層：DBアクセスの責務

# 担保範囲、ルール付け

#### コントローラ層
基本的にテストコード不要です。
なぜならコントローラは画面に関わるところですので、画面に関わるテストで担保が取れるので、junitなどのサーバサイドで担保を取るのは不要です。

複雑な処理がある場合は、その処理のみ切り出してそこだけ担保取るのが良いです。私の場合は別メソッドに切り出してその箇所のカバレッジのみ100％にしています。

#### サービス層
ここのテストを**一番厚くすべき**です。サービス層以下の結合テストをする勢いで担保を取ると良いです。但し、以下の担保は不要です。
　・業務に起こりえない箇所の網羅。
　・メソッドの呼び出し回数。
　　担保理由が不明だからです。戻ってきたアウトプットの検証で十分です。

#### ドメイン層
基本的に不要です。
サービス層での担保でカバーできるからです。

複雑なビジネスロジックがある場合は、メソッドに切り出してそのメソッドだけ担保を取ります。
例えば、金額の計算ロジック等は担保を取っておくと良いです。

#### データアクセス層
ここも厚くする箇所です。指定した条件で期待した登録、更新、参照できることを細かく担保しましょう。SQLの外部結合、内部結合条件も細かく網羅しましょう。
上記の細かいところはサービス層のテストでも良いですが、サービスのテストコードが大量になるのを防ぐため、データアクセス層のテストに任せられるところはお任せしましょう。

#### 全体的
業務で起きないことは担保不要です。業務で起きないことを色々考慮し、増えた分岐は**処理されないコード**です。作成したコード量に応じてバグが増える可能性はありますし、テストコードも増えてしまいます。

# 苦い経験①
私はカバレッジ100％を目指し過剰な品質担保で、1週間ぐらいは工数を無駄に使ってしまったことがあります…ぐぬぬ


# 苦い経験②
この方法を考えた同じチームメンバーの方は、カバレッジ100%が必須だった現場で追加開発の度に多数のjunit修正が必要となりました。
その結果、保守性が悪くお金の折り合いがつかなくなりプロジェクトが無くなったそうです…

# まとめ
今回はサーバサイドを例に挙げていますが、seleniumなどの**その他テスト自動化も同じ**だと思います。
考慮すべきは「**理由のある担保**」と「**メンテのしやすさ**」です。
Databricksにおけるインターネットからのデータのダウンロード

[Download data from the internet \| Databricks on AWS](https://docs.databricks.com/files/download-internet-files.html) [2023/1/25時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

公開URLからデータをお使いのクラスタードライバーにアタッチされているボリュームストレージにダウンロードするためのDatabricksノートブックを活用することができます。クラウドオブジェクトストレージからデータにアクセスしている場合には、Apache Sparkを用いて[直接データにアクセス](https://docs.databricks.com/storage/index.html#configure-object)することでより良い結果を得ることができます。

Databricksクラスターは汎用計算資源を提供するので、Apache Sparkコマンドに加えて任意のコードを実行することができます。任意のコードはDBFSルートではなく、クラスターのルートディレクトリに実行されるので、ダウンロードしたデータをApache Sparkで読み込む前に新たな場所にデータを移動しなくてはなりません。

:::note
**注意**
いくつかのワークスペースでは、公衆インターネットへのアクセスを許可していない場合があります。ネットワークアクセスの拡張が必要かどうかはワークスペース管理者にお問い合わせください。
:::

# Bash、Python、Scalaによるファイルのダウンロード

Databricksでは、インターネットからデータをダウンロードするネイティブツールは提供していませんが、サポートされる言語で利用できるオープンソースツールを活用することができます。以下の例では、いくつかのファイルをダウンロードするためにBash、Python、Scalaを使用しています。

```bash:Bash
%sh curl https://data.cityofnewyork.us/api/views/kk4q-3rt2/rows.csv --output /tmp/curl-subway.csv
```

```py:Python
import urllib
urllib.request.urlretrieve("https://data.cityofnewyork.us/api/views/kk4q-3rt2/rows.csv", "/tmp/python-subway.csv")
```

```scala:Scala
import java.net.URL
import java.io.File
import org.apache.commons.io.FileUtils

FileUtils.copyURLToFile(new URL("https://data.cityofnewyork.us/api/views/kk4q-3rt2/rows.csv"), new File("/tmp/scala-subway.csv"))
```

これらのファイルはドライバーにアタッチされたボリュームストレージにダウンロードされるので、以下のサンプルのようにこれらのファイルを参照するには`%sh`を使います。

```sh:Bash
%sh ls /tmp/
```

以下のサンプルのように、ダウンロードしたファイルのコンテンツをプレビューするためにBashコマンドを使うことができます。

```bash:Bash
%sh head /tmp/curl-subway.csv
```

# dbutilsによるデータの移動

Apache Sparkを用いてデータにアクセスするには、カレントロケーションからデータを移動します。このデータのカレントロケーションは、ドライバーからのみ参照できる揮発的なボリュームストレージです。Databricksは並列にファイルソースからデータをロードするので、ファイルは計算資源のすべてのノードから参照できなくてはなりません。Databricksでは幅広い[外部データソース](https://qiita.com/taka_yayoi/items/09348912a92a24441c40)をサポートしていますが、ファイルベースのデータアクセスは通常クラウドオブジェクトストレージへのアクセスを前提としています。

[Databricksユーティリティ](https://qiita.com/taka_yayoi/items/3717623187859809515d)(`dbutils`)を用いることで、ドライバーにアタッチされたボリュームストレージから、アクセスを設定した外部オブジェクトストレージを含むDBFSからアクセスできる他のロケーションにファイルを移動することができます。以下の例では、ワークスペース構築時に設定されたクラウドオブジェクトストレージのロケーションである[DBFSルート](https://qiita.com/taka_yayoi/items/406b639a9dcd43dbcc54)に移動しています。

```py:Python
dbutils.fs.mv("file:/tmp/curl-subway.csv", "dbfs:/tmp/subway.csv")
```

# ダウンロードしたデータの読み込み

クラウドオブジェクトストレージにデータを移動すると、いつもと同じようにデータを読み込むことができるようになります。以下のコードでは、DBFSルートに移動されたCSVデータを読み込んでいます。

```py:Python
df = spark.read.format("csv").option("header", True).load("/tmp/subway.csv")
display(df)
```



### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
Julia: ChatGPT API の使い方

こちらのプログラムを　Julia を使って書きました。
[Curl: ChatGPT API の使い方](https://qiita.com/ekzemplaro/items/6660260e287a9ee9ecf4)

## プログラム

```bash:julia_fuji.sh
export OPENAI_API_KEY="sk-****************'
./julia_fuji.jl
```

```jl:julia_fuji.jl
#! /usr/bin/julia
#
#	julia_fuji.jl
#
#						Apr/11/2023
# --------------------------------------------------------------------
# import Pkg; Pkg.add("HTTP")
# import Pkg; Pkg.add("JSON")
using HTTP
using JSON
# --------------------------------------------------------------------
println(stderr,"*** 開始 ***")

# APIキーを設定
openai_api_key = ENV["OPENAI_API_KEY"]

# ヘッダーを設定
headers = [
    "Content-Type" => "application/json",
    "Authorization" => "Bearer $openai_api_key"
]

# API呼び出しのパラメーターを設定
params = Dict(
    "model" => "gpt-3.5-turbo",
    "messages" => [
        Dict(
            "role" => "user",
            "content" => "富士山の高さは"
        )
    ]
)

# API呼び出しを実行
response = HTTP.post(
    "https://api.openai.com/v1/chat/completions",
    headers,
    JSON.json(params)
)

# レスポンスをJSON形式にパース
json_response = JSON.parse(String(response.body))

# 結果を表示
# println(json_response)
println(json_response["choices"])
data = json_response["choices"]
content = data[1]["message"]["content"]
println(content)

println(stderr,"*** 終了 ***")
# --------------------------------------------------------------------
```

## 実行結果

```text
$ ./julia_fuji.sh 
*** 開始 ***
Any[Dict{String, Any}("finish_reason" => "stop", "message" => Dict{String, Any}("role" => "assistant", "content" => "3776メートルです。"), "index" => 0)]
3776メートルです。
*** 終了 ***
```

## 確認したバージョン

```text
$ julia --version
julia version 1.8.5
```
OracleのSQL文でwhere句の 条件にrownumを入れると、パフォーマンスは下がりますか？

OracleのSQL文でwhere句の条件にrownumを入れると、パフォーマンスに悪影響を与える可能性があります。

rownumは、Oracleで特別な意味を持つ擬似列であり、結果セットの行番号を示します。rownumは、該当する条件を満たす行のみを取得する場合に使用されます。ただし、rownumは、where句の条件で他の演算や関数と組み合わせて使用すると、パフォーマンスに悪影響を与えることがあります。

厳密に言えば、where句とrownumを組み合わせると、Oracleのクエリーの最適化が失敗する可能性があります。これは、Oracleがクエリーの実行計画を作成する際に、where句の条件を最上位の条件として評価するためです。つまり、rownumを使用して最初のn行のみを取得しようとする場合、Oracleはテーブル全体をスキャンして、rownumが指定したn行に到達するまで、結果セットを作成し続けます。

このような場合、インデックスが効果的に使用されず、クエリーにかかる時間が長くなります。特に、大きなテーブルに対してrownumを使用する場合は、パフォーマンスの問題が顕著に現れます。

したがって、where句の条件にrownumを入れる場合は、クエリーの最適化方法に注意する必要があります。代替手段として、インデックスを使用したり、一時的なテーブルにデータを抽出したりすることができます。

SQLのwhere句でrownumを使用する場合、最適な構文は、rownumを条件に含めたサブクエリを使用することです。具体的には、以下のようになります。

例：
```
SELECT * FROM (
  SELECT * FROM mytable WHERE <条件> ORDER BY <列名> ASC
) WHERE rownum <= <取得する行数>;
```

このクエリーは、mytableテーブルから条件に一致する行を抽出し、指定した列で昇順にソートした結果セットから、rownumが指定した数以下の行を取得します。

このように、rownumを条件に含めたサブクエリを使用することで、where句の条件にrownumを組み合わせる場合に起こる問題を回避できます。ただし、サブクエリーを使用した場合でも、rownumは結果セットの行番号を示すため、取得する行数が制限されてしまいます。
SQL文のヒント句も実行計画でも同じインデックスを使用しているのですが、ASHレポートで確認すると違うインデックスを使用しているという結果が出ていますが、なぜかわからないのです

ASHレポートで実行計画が異なるインデックスを使用していると報告されている場合、いくつかの可能性が考えられます。

実行計画のキャッシュ
実行計画は、SQLの実行中に決定されます。同じSQLを複数回実行する場合、Oracleはキャッシュされた実行計画を再利用することができます。この場合、異なるインデックスを使用する実行計画を得ることがあります。

ヒントの無視
ヒントは、SQLの実行計画を制御するために使用されます。しかし、Oracleは最適な実行計画を決定するために自動的に最適な方法を選択するため、ヒントが無視される場合があります。この場合、SQL文のヒント句があっても、実際には別の実行計画が使用されることがあります。

統計情報の古さ
実行計画を決定する際に、Oracleは表の統計情報を使用します。表が変更された場合、統計情報が古くなっている可能性があります。この場合、異なるインデックスを使用している実行計画を得ることがあります。

以上のような理由により、SQL文のヒント句と実行計画が異なるインデックスを使用する場合があることがあります。詳細を確認するために、ヒントが正しく適用されているかどうか、統計情報が最新であるかどうかなど、SQLの詳細を確認することをお勧めします。
Salesforce認定アソシエイト試験の公式練習問題解説してみた-04

こんにちは
本橋孝昭です

Salesforce認定アソシエイト試験の公式練習問題があるのはご存じでしょうか。
Trailheadにフラッシュカードとして何度も解ける練習問題がございます。

今回は
認定資格試験の準備: Salesforce 認定アソシエイト＞
Salesforce エコシステムについて学ぶ


の問題４問目を解説します

今回の内容は動画でも公開しています

https://www.youtube.com/watch?v=Au_E-050Xfg&list=PLz8HSF-SrLeGA3OcX5wuL6b9Tfdm0xf6W&index=4

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

https://trailhead.salesforce.com/ja/content/learn/modules/cert-prep-salesforce-certified-associate/learn-about-the-salesforce-ecosystem?trailmix_creator_id=strailhead&trailmix_slug=prepare-for-your-salesforce-certified-associate-credential

![０４Salesforce.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/954e6908-8d93-ae53-4358-6ba8ca86c532.png)

正解はBです
ソリューションアーキテクトは
Salesforceの疑問について解決する仕事です

Aの開発者について

開発者は間違いです
開発者はコードを使用してSalesforce　Platform上に自動化を構築します

![構築A.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/62d0bff4-ab72-4110-c8ad-7b452445afdc.png)


システム管理者のCについても間違いです
![構築CC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/5b3347fc-8b9f-6723-f2ce-a53ddd87d8a8.png)






## 他の認定アソシエイトの勉強方法について

他の勉強方法についてはこちらをご確認ください

https://qiita.com/Takaa/items/d35c9955fe8eb889d76b
Ruby 文字列を「改行して」繰り返す

## はじめに
Ruby学習の備忘録です。


## 文字列を繰り返す

Rubyにて「文字列を繰り返してください」は、
よくある問題です。

確か文字列に対して演算子を使えば
可能だったんじゃないかな…というわけでやってみました。

```ruby:sample.rb
def hello
  puts "こんにちは!" * 3
end

hello()
```

```:出力結果
こんにちは！こんにちは！こんにちは！
```

迫る勢いのこんにちは！が出力されました。

`puts`メソッドは本来、改行を加えて出力されますが
どうやら演算子を使うと文字列が**連結**されてしまうんですね〜

確かに繰り返しているんだけど、
求めていたのは**改行された**「こんにちは！」でした。

## 特殊文字「\n」を使う

そこで使うのが`\n`
改行したいときに使える特殊文字です。

注意すべきは、**ダブルクオート**で囲むこと。
シングルクオートでは、ただの文字列として出力されてしまいます。

##### ダブルクオートで囲んだ場合

```ruby:sample.rb
def hello
  puts "こんにちは！\n" * 3
end

hello()
```

```:出力結果
こんにちは！
こんにちは！
こんにちは！
```

##### シングルクオートで囲んだ場合

```ruby:sample.rb
def hello
  puts 'こんにちは！\n' * 3
end

hello()
```

```:出力結果
こんにちは！\nこんにちは！\nこんにちは！\n
```

\n　←「やあ！」って感じに見えなくもないけど🙋‍♂️(笑)

## timesメソッドでさらにスッキリ書く

`times`は、整数オブジェクトに対して呼び出すことができるメソッドです。
整数の値だけ、ブロック内の処理が繰り返されます。
単純なものだと、ブロックパラメータなしで実行可能。
（ブロックとかブロックパラメータが何かというのはここでは割愛）

```ruby:sample.rb
3.times do
  puts "こんにちは！"
end
```

なお、do～endを省略した書き方、{～}を使うとさらにスッキリ！

```ruby:sample.rb
3.times { puts "こんにちは！" }
```

```:出力結果
こんにちは！
こんにちは！
こんにちは！
```

あ、そういえば`\n`書いてないけど、ちゃんと改行されましたね〜
演算子がなければ、`puts`メソッドは
改行を加えて出力されることも改めて確認できました👏

## おわりに

こんなところで疑問を感じる人はいないかもしれませんが😂
なんとなく〜で進んでつまずいた経験がたくさんあるので、
ほんの小さなことでも備忘録として残すのは、無駄ではない…はず。
WindowsのWSL2上に構築したDockerコンテナから、ホストWindowsにファイルをコピーする

**環境は以下の通り**
WindowsのWSL2にLinux(Ubuntu)をインストール。
UbuntuにSQL Serverコンテナを作成。

## やりたい事
SQL ServerのバックアップファイルをWindows上に持ってきたい。

## 経路は以下
SQL Serverコンテナ → WSL2のUbuntu → Windows

つまり
①SQL Serverコンテナ → WSL2のUbuntu
②WSL2のUbuntu → Windows
の2段階のやり取りが必要。

## やった事（実際に使用したコマンド）
①SQL serverコンテナ → WSL2のUbuntu
```
docker cp コンテナID:/コンテナ内ファイル /Ubuntuコピー先ファイル
```
②WSL2のUbuntu → Windows
```
cp /Ubuntuコピー先ファイル /mnt/Windowsコピー先
```
これでできた。

# 参考サイト
https://qiita.com/gologo13/items/7e4e404af80377b48fd5

https://qiita.com/RyotaIshii/items/25beeafaa836b4f4819c
CloudFunctions(python)でDiscordのslashコマンドを実装する

https://qiita.com/m77so/items/d4b78f9206944a071f47

↑の記事のpython版です。
@m77so 様の記事がわかりやすいかと思いますので、cloud functionを作るところまではそちらを参考にしてください。

# cloud functionを作る
「関数の作成」を選択して、構成は任意で大丈夫です。
今回は例として
- 環境: 2世代
- 関数名: test
- リージョン: us-central1
にします。また、HTTPSに関しては「未承認の呼び出しを許可」します。

ランタイムはpython3.9で行いました。
```py
import json
import functions_framework
from flask import abort
from flask import jsonify
from nacl.exceptions import BadSignatureError
from nacl.signing import VerifyKey

def validate_request(request):
    verify_key = VerifyKey(bytes.fromhex(public_discord_key))
    signature = request.headers["X-Signature-Ed25519"]
    timestamp = request.headers["X-Signature-Timestamp"]
    body = request.data.decode("utf-8")
    try:
        verify_key.verify(f"{timestamp}{body}".encode(), bytes.fromhex(signature))
    except BadSignatureError:
        return False
    return True

@functions_framework.http
def handle_request(request):
    is_valid = validate_request(request)
    if not is_valid:
        abort(401, "invalid request signature")
    request_type = request.json["type"]
    if request_type == 1:
        return jsonify({"type": 1})
    else:
        message_id = request.json["data"]["id"]
        print(f"message_id: {message_id}")

        return jsonify(
            {
                "type": 4,
                "data": {
                    "tts": False,
                    "content": "You use command!",
                    "embeds": [],
                    "allowed_mentions": {"parse": []},
                },
            }
        )
```
https://www.viorelspinu.com/2022/12/highly-scalable-discord-bot-on-google_9.html

上記の記事のほぼコピペです。pubsub関連の関数を削除しただけです。

# 最後に
「CloudFunction Discord Slash Python」みたいに検索したときに日本語の記事が出てこなかったので、メモ書きとして残しておきます。
EC2にRails/Node.js/rbenv/ruby-build/Ruby/Bundlerのインストール

EC2インスタンスにRailsをインストールする手順は以下の通りです。

必要なパッケージのインストール
まず、必要なパッケージをインストールします。

```
sudo yum update -y
sudo yum install -y gcc-c++ patch readline readline-devel zlib zlib-devel libyaml-devel libffi-devel openssl-devel make bzip2 autoconf automake libtool bison sqlite-devel
```
Node.jsのインストール
Railsのアプリケーションを実行するには、Node.jsが必要です。Node.jsをインストールします。

```
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
```
```
$ . ~/.nvm/nvm.sh
```
```
$ nvm install 17.9.1
```
rbenvのインストール
Rubyのバージョン管理にはrbenvを使用します。rbenvをインストールします。

```
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
```

ruby-buildのインストール
rbenvでRubyのバージョンを切り替えるためには、ruby-buildが必要です。ruby-buildをインストールします。

```
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
```

Rubyのインストール
Rubyをインストールします。バージョンは3.1.2をインストールする例です。
下は使用するバージョンの指定です。

```
rbenv install 3.1.2
rbenv global 3.1.2
```

Bundlerのインストール
Railsアプリケーションを作成するには、Bundlerが必要です。Bundlerをインストールします。

```
gem install bundler
```

Railsのインストール
最後に、Railsをインストールします。

```
gem install rails -v 7.0.4
```

以上で、EC2インスタンスにRailsをインストールする手順は完了です。
Docker Desktopを使用せずにWSL2でdocker環境を構築

# やりたいこと
* タイトルの通り
* windows 環境にて実施する

# WSL2を有効にする
* powershellにて下記コマンドを実行
  * インストール
```
wsl --install
```
 * WSL2をデフォルトにする
```
wsl --set-default-version 2
```

# Ubuntu
## インストール
* 特にバージョンによる違いは無いと思うので、既にインストールされている場合はこの手順は不要
* Microsoft Storeから、実施時点で最新バージョンをインストールした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281492/5bd293f2-33ba-c70d-bc01-dfb17aafd6ce.png)

## 初回起動
* userの作成　任意のuser名、パスワードを設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281492/9efbec2f-b745-dfd3-a28b-9d03fd1418ea.png)

* 初回起動時、こんなエラーが出たが
``` WslRegisterDistribution failed with error: 0x80004002 ```
[このサイト](https://github.com/microsoft/WSL/issues/2851)を参考に、
管理者用PowerShellから下記コマンド実行で解決できた
``` Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux ```

# docker
## インストール
* [参考サイト](https://dev.to/felipecrs/simply-run-docker-on-wsl2-3o8)
* ubuntuより、下記コマンドを実行
* dockerインストール
```
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo groupadd docker
```
* 確認
```
docker run hello-world
```
下記エラーが出た場合
```
docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
```
dockerデーモンが起動していないので、下記コマンドで起動する
```
sudo service docker stop
sudo service docker start
```

## 参考資料
### docker runコマンドについて
* 実行時イメージ
![docker_runコマンド実行時イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281492/e19c2075-f018-9008-2f2f-d90e46fa6b7a.png)
* runコマンドは、下記3つのコマンドを同時に行う
  * pull　　・・・イメージの取得、ダウンロード
  * create　・・・指定したイメージ上に書き込み可能なコンテナ・レイヤを作成　
  * start　 ・・・dockerコンテナを起動
Java public protected private　アクセス修飾子

## アクセス修飾子

指定した変数やクラスなどをどの範囲から参照可能化のスコープを制御する

- public

すべてのクラスからのアクセスを許す

- protected

サブクラスおよび同一パッケージからのアクセスを許す
他パッケージ・他クラスからのアクセスをプロテクトする

- なし

同一パッケージからのアクセスを許す

- private

自クラスからのアクセスしか許さない

<table>
<tr>
<td rowspan="2">アクセス修飾子</tb>
<td colspan="3">自パッケージ</td>
<td colspan="3">他パッケージ</td>
</tr>
<tr>
<td>自クラス</td>
<td>サブクラス</td>
<td>他クラス</td>
<td>サブクラス</td>
<td>他クラス</td>
</tr>
<tr>
<td>public</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
</tr>
<tr>
<td>protected</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
<td>✕</td>
</tr>
<tr>
<td>なし</td>
<td>〇</td>
<td>〇</td>
<td>〇</td>
<td>✕</td>
<td>✕</td>
</tr>
<tr>
<td>private</td>
<td>〇</td>
<td>✕</td>
<td>✕</td>
<td>✕</td>
<td>✕</td>
</tr>

</table>
セレクトボックスの作り方

# 初めに
セレクトボックスの作り方、phpでの値の受け取り方を説明します。

# セレクトボックス
セレクトボックスの作成には`selectタグとoptionタグ`を使用する。
selectタグの中にoptionタグを入れる。
`selectタグでname属性が送信された値をphpで受け取る際のキーになる。`
`optionタグで選択肢の値を記載する`。
`optionタグのvalue属性で実際に送信される値を決める`ことができる。

```php:selectタグ
<select name="fruits">
 <option value="apple">りんご</option>　
 <option value="orange">みかん</option>
 <option value="grape">ブドウ</option>
</select>　

//りんごを選択したとする
echo $_POST[fruits];
//結果：appleが出力される
```

for文とoptionタグを合わせて使うと効率的にセレクトボックスを作ることができる。
また、optionタグを変数展開の要領で記載するが、その際は`""(ダブルクォーテーション)で囲む`必要がある。

```php:for文とoptionタグ
for ($i=1; $i<4; $i++){
    echo "<option value='{$i}'>{$i}</option>";
}
```
```:補足
上記for文で、optionタグが""(ダブルクォーテーション)で囲われており、
変数である{$i}は''(シングルクォーテーション)で囲われている。
{$i}を""で囲うと、(<option value=)が""で囲われていると誤認識して構文エラーが起こるので注意。
(\)エスケープ文字を使うなど、工夫するのもあり。
```
ITパスポート勉強時に必死に覚えたワード

入社後にITパスポートの勉強をしていた時、
どうしても英語の言葉が多すぎて覚えられなかったので
自分なりに調べて解釈したメモたちです。



## :footprints:ムズカシ３文字

BPR : Business Process Reengineering / 業務プロセスの再設計
TPM : Trusted Platform Module / PCマザーボード上のセキュリティチップ
ERP : Enterprise Resource Planning / 経営資源の管理
PLC : Power Line Communications
DoS : Denial of Service
SCM : Supply Chain Management
PKI : Public Key Infrastructure / 公開鍵基盤
KPI : 企業目標の達成に向けて行われる活動の指標
SLA : Service Level Agreement
MoT : Management of Technology
RFI : Request For Information / 情報提供要求
RFP : Requwst For Proposal / 提案要求
CAM : Computer Aided Manufacturing / コンピューター支援製造
WBS : Work Breakdown Structure
EDI : Electronic Data Interchange
PLC : Power Line Communications / 電力線
SFA : Sales Force Automation / 営業支援
DMZ : Demilitarized Zone / 非武装地帯（隔離された中間的エリア）
NTP : Network Time Protocol / 協定世界時UTC
ROE : Return On Equality / 自己資本利益率
JVN : Japan Vnlnerability Notes / ソフトウェアなどの脆弱性関連情報とその対策情報
MRP : Material Requipments Planning / 資材所要量計画
LTE : Long Term Evolution / 4G,携帯電話
ASP : Application Service Provider / 業務用のアプリをインターネットを通して利用可能に
UPS : 自家発電装置が電源を供給し始めるまでの間、サーバーに電源を供給する
MBO : Management BuyOut / 自社の株式の大半を買い取って経営権を取得
CGI : Common Gateway Interface / ユーザーの入力した情報によって表示を変える

## :bug:ムズカシ４文字

MTBF : Mean Time Between Failures
MTTR : Mean Time To Repair
SPEC : Standard Perfomance Evaluation Corporation
DBMS : Database Management System / データベース管理システム
VoIP : Voice of Internet Protocol / ボイップ
ISMS : Information Security Management System /情報セキュリティマネジメントシステム
DRAM : Dynamic Random Access Memory / SRAMより遅い / コンピューター主記憶
MVNO : 仮想移動体通信事業者 / UQmobileみたいなこと

## :robot:ややこしカタカナ

デュアルシステム : 結果のクロスチェック
デュプレックスシステム : 故障に備えて待機している
デブオプス：開発＆運用の協力
コモディティ化 : 汎用品化


以上です。
Nuxt3 のビルドに失敗する

Nuxt３でブログ作成時にビルドに失敗した一時的な解決策。

# やりたいこと
- Nuxt3でブログ作成
- ビルドして **Vercel** でデプロイする

# バージョン
```
Node: v16.15.0
Nuxt: 3.3.1
vuetify: 3.1.4
```

# ビルドする
```
$ yarn build
```

エラー内容
```
~~ 省略 ~~

✔ Server built in 2859ms 
✔ Generated public .output/public
ℹ Building Nitro Server (preset: node-server)

ERROR  RollupError: Could not resolve "./_nuxt/VContainer-styles.a03b508f.mjs" from ".nuxt/dist/server/styles.mjs"

undefined

ERROR  Could not resolve "./_nuxt/VContainer-styles.a03b508f.mjs" from ".nuxt/dist/server/styles.mjs"

  at error (node_modules/rollup/dist/es/shared/node-entry.js:2125:30)
  at ModuleLoader.handleInvalidResolvedId (node_modules/rollup/dist/es/shared/node-entry.js:23776:24)
  at ModuleLoader.resolveDynamicImport (node_modules/rollup/dist/es/shared/node-entry.js:23834:58)
  at async node_modules/rollup/dist/es/shared/node-entry.js:23723:32

error Command failed with exit code 1.
```

# 対応方法
https://github.com/nuxt/nuxt/issues/19850

こちらの issue を参考に`nuxt.config.ts`に以下を追記
```typescript：nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
import { defineNuxtConfig } from "nuxt/config";
import vuetify from "vite-plugin-vuetify";

export default defineNuxtConfig({
  build: {
    transpile: ["vuetify"],
  },
  hooks: {
    "vite:extendConfig": (config) => {
      config.plugins!.push(vuetify());
    },
  },
  vite: {
    ssr: {
      noExternal: ["vuetify"],
    },
    define: {
      "process.env.DEBUG": false,
    },
  },
  css: ["@/assets/main.scss"],

  // 以下追記項目
  experimental: {
    inlineSSRStyles: false,
  },
});
```

再ビルド
```
Σ Total size: 4.43 MB (935 kB gzip)
✔ You can preview this build using node .output/server/index.mjs
✨  Done in 8.78s.
```

※ 23.4.11 時点で issue はまだ未解決状態なので暫定対応。今後の動向に注目したい。

# 参考にしたサイト
https://github.com/nuxt/nuxt/issues/19850
mongodbにデータインポート

## 背景

js勉強中mongodbを触ろうと思い、折角だからコンテナでと思い、、

dockerでmongodbコンテナ作成

コンテナの中に入りmongoimportコマンドでjsonファイルをインポート

ホストPC:Windows11

WSL2:Ubuntu20.04.4

## コンテナ化

使用イメージ:****mongodb/mongodb-community-server****

[https://hub.docker.com/r/mongodb/mongodb-community-server](https://hub.docker.com/r/mongodb/mongodb-community-server)

コンテナ起動方法は公式参照

[https://www.mongodb.com/docs/manual/tutorial/install-mongodb-community-with-docker/](https://www.mongodb.com/docs/manual/tutorial/install-mongodb-community-with-docker/)

ユーザ名(MONGO_INITDB_ROOT_USERNAME)やパスワード(MONGO_INITDB_ROOT_PASSWOR)はコンテナ起動時に環境変数として渡してください。

ホストポートは適当に開けてください。

docker desktopを使用した場合、コンテナ起動時にOptional Settingをクリックすることで

ストレージ共有や環境変数も設定できます。

普段コマンドなのでdocker desktopが使いやすくてびっくりしました。。。

![Untitleda.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2082391/b80d6ec1-4923-ca62-f4e5-0a8269cad0a2.png)



これでホストにインストールしたmongo compassからコンテナに接続。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2082391/cef2485c-4f43-5884-f59d-d27688d3272b.png)

GUIでdbやコレクションを作って、データをインポートもできます。

## コマンドでデータをインポート

ホストPCにデータがあるのでコンテナに移す。

```bash
docker cp <ホストパス> <コンテナID>:<コンテナ内パス>

ex)
docker cp /share/test.json 4tc06fe07dne:/data/
```

mongodbコンテナ内に入り、mongoimportコマンドでインポート

コマンド詳細は

[https://www.mongodb.com/docs/database-tools/mongoimport](https://www.mongodb.com/docs/database-tools/mongoimport/#synopsis)

```bash
mongoimport --authenticationDatabase admin  -u user -p pass --db bookshelf --collection books --file /data/books.json --jsonArray
```

## Error

### データインポート時の認証エラー

ユーザ管理者でMongoDBに接続する場合、--authenticationDatabase adminが必要。

あとユーザとパスワードも。

[http://ittoo.jugem.jp/?eid=841](http://ittoo.jugem.jp/?eid=841)

なかったら以下のエラーが出る。

```bash
error connecting to host: could not connect to server: connection() error occurred during connection handshake: auth error: sasl conversation error: unable to authenticate using mechanism "SCRAM-SHA-1": (AuthenticationFailed) Authentication failed.
```

### データインポート時のデータ形式エラー

インポートしようとしていたデータは以下。

```json
[
	{
    "id": {
      "$oid": ""
    },
    "title": "",
    "description": "",
    "rating": ,
    "comment": "",
    "createdAt": {
      "$date": {
        "$numberLong": ""
      }
    },
    "updatedAt": {
      "$date": {
        "$numberLong": ""
      }
    },
    "__v": 
  },
	{
	
	},
～省略～
	{

	}
]
```

mongoimportコマンドはデフォルトでドキュメントが配列に含まれていないことを想定しているので、私がインポートしようとしていたデータは配列に含まれているデータなので

—jsonArrayパラメータが必要でした。

[https://www.reddit.com/r/mongodb/comments/wshjrd/importing_a_json_array_error_failed_cannot_decode/](https://www.reddit.com/r/mongodb/comments/wshjrd/importing_a_json_array_error_failed_cannot_decode/)

なかったら、以下のようなエラーが出ました。
```
Failed: cannot decode array into a primitive.D
```