OmniAICreator/Qiita-1.07M huggingface dataset:
OpenAI の API の使い方

# OpenAI の API の使い方

https://platform.openai.com

:::note info
主に、テキスト生成に関する情報は、随時追加する予定です。また、エラーが出た場合の事例やその対処も随時追加する予定です。
:::

:::note warn
理解できていない部分もあります。そのため、今後記事を修正する可能性があります。ご了承ください。
:::

## はじめに

はじめまして。現在、私は大学院生（修士課程）です。

この記事では、OpenAI の API の取得の流れと Python を使って、実装をしようと思います。

この記事が、「OpenAI の API を使った開発の手掛かりになった」などの貢献ができるように書かせていただきます。

## アカウントの作成

OpenAI の API キーを作成するためには、OpenAI のアカウントを作る必要があります。以下のリンクから、アカウントを作ることができます。

https://platform.openai.com/signup

## API の取得

[API Key](https://platform.openai.com/api-keys) のページにある「Create new secret key」をクリックすることで、取得することができます。

## 環境構築

### Python バージョン

- Python >= 3.0.0

https://www.python.org/

### ライブラリ

```text:requirements.txt
openai
```

## API の設定

以下の方法のいずれかを使って、API キーを設定してください。すべてのプロジェクトで API キーを適応したい場合は、[MacOS](#macos) あるいは [Window](#windows) を参照してください。一つのプロジェクトのみに適応したい場合は、[Python](#python) を参照ください。

### MacOS

- ターミナルを開きます
- Bash Profile を編集します
    - `nano ~/.bash_profile` あるいは `nano ~/.zshrc`
- 環境変数を追加します
    - `export OPENAI_API_KEY='your-api-key-here'` を Bash Profile に書きます（`your-api-key-here` は自分が取得した API キーに変更します）
- Ctrl+O を押して変更を書き込み、Ctrl+X でエディタを閉じる
- 更新された Profile をロードします
    - `source ~/.bash_profile` あるいは `source ~/.zshrc`
- ターミナルで `echo $OPENAI_API_KEY` を実行すれば、API キーが確認できます

### Windows

- コマンドプロンプトを開きます
- 現在のセッションの環境変数を追加します
    - `setx OPENAI_API_KEY "your-api-key-here"`（`your-api-key-here` は自分が取得した API キーに変更します）
- コマンドプロンプトで `echo %OPENAI_API_KEY%` を実行すれば、API キーが確認できます

### Python

`your-api-key-here` は自分が取得した API キーに変更してください。

```python
from openai import OpenAI

client = OpenAI(api_key="your-api-key-here")
```

## Python を使って、リクエストする

```python
from openai import OpenAI

client = OpenAI()

completion = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role": "system", "content": "You are a poetic assistant, skilled in explaining complex programming concepts with creative flair."},
        {"role": "user", "content": "Compose a poem that explains the concept of recursion in programming."}
  ]
)

print(completion.choices[0].message)
```

`model="gpt-4o"` とすることで、GPT-4o が使えます。他にも、使うことができるモデルはありますが、基本的には、GPT-4o か GPT-4o mini を使えば良さそうです。

## エラー

### RateLimitError

[上記に示したようなコード](#python-を使ってリクエストする)を実行した際に、人によってはこのようなエラーが出ます。

```text
RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}
```

原因は、クレジットの残高が足りないため、エラーが出ます。クレジットの残高を追加する場合は、「[Setting](https://platform.openai.com/settings)」から「[Billing](https://platform.openai.com/settings/organization/billing)」に移動してください。「Add to credit balance」という欄をクリックすることで、クレジットの残高を追加できます。

## おわりに

この記事では、OpenAI の API の取得の流れと Python を使って、実装をしました。

この記事が、エンジニアとして活躍する皆様に貢献できたら幸いです。

## 参考記事

https://platform.openai.com/docs
Cloud Run Jobs入門：環境変数をつかてPythonでAPIのようにジョブを実行する方法

# 背景
- 通常のCloudRunでは最大実行時間が60分に制限されていますが、それ以上の時間がかかる処理を実行する環境が必要です
- コンテナベースで利用時間に応じた従量課金のCloudRunJobsがその解決策として適しています
- しかし、CloudRunJobsではHTTPリクエストが許可されておらず、APIのようにPythonなどのアプリからメソッドや引数を指定して処理を呼び出すことができません
- そこで今回は、CloudRunJobs実行時に設定できる環境変数を活用して、メソッド名と引数を指定することで処理を呼び出せるようにする方法をご紹介します

# CloudRunJobsの実装

```py
import os
import json
from typing import Any


def method_1(args_dict: dict[str, Any]):
    print("method_1")
    print(f"args: {args_dict}")


def method_2(args_dict: dict[str, Any]):
    print("method_2")
    print(f"args: {args_dict}")


def main():
    method = os.getenv('METHOD', None)
    arg_json = os.getenv('ARG_JSON', '{}')

    if method is None:
        raise Exception('env: METHOD has to be given.')

    try:
        arg_dict = json.loads(arg_json)
    except Exception as e:
        print(f'ARG_JSON is not JSON format. : {arg_json}')
        raise e

    if method == 'method_1':
        method_1(arg_dict)
    elif method == 'method_2':
        method_2(arg_dict)
    else:
        raise Exception(f'method_name: {method} not found.')


if __name__ == '__main__':
    main()
```

- `METHOD`という環境変数に対象のメソッド名を実行時に指定します
- `ARGS_JSON`という環境変数に処理の引数となるJSON文字列を指定します


# CloudRunJobsの実行

```py
from google.cloud.run_v2 import JobsClient, RunJobRequest, EnvVar

# 環境変数で METHOD と ARG_JSONを渡して呼び出してください
# METHODは必須です
# ARG_JSONはなくてもいいです. 渡す場合は必ずJSONフォーマットで渡してください


def sample_run_job():
    client = JobsClient()

    request = RunJobRequest(
        name=f"projects/{PROJECT}/locations/{REGION}/jobs/{JOB_NAME}",
        overrides=RunJobRequest.Overrides(
            container_overrides=[
                RunJobRequest.Overrides.ContainerOverride(
                    env=[
                        EnvVar(
                            name="METHOD",
                            value="method_1"),
                        EnvVar(
                            name="ARG_JSON",
                            value='{"key1": "value", "key2": ["nest_key", "nest_value"]}')])]))

    operation = client.run_job(request=request)
    print("Waiting for operation to complete...")
    response = operation.result()
    print(response)


sample_run_job()
```

# 参考

## CloudRunについて


### quickstart

https://cloud.google.com/run/docs/quickstarts/jobs/build-create-python?hl=ja

### docs

https://cloud.google.com/run/docs/create-jobs?hl=ja

### PythonからのJobの実行

https://cloud.google.com/python/docs/reference/run/latest/google.cloud.run_v2.services.jobs.JobsClient#google_cloud_run_v2_services_jobs_JobsClient_run_job

https://cloud.google.com/python/docs/reference/run/latest/google.cloud.run_v2.types.RunJobRequest

### JobやTaskやExecutionについての理解

https://medium.com/google-cloud-jp/cloud-run-jobs-c963a7143367

https://dev.classmethod.jp/articles/20240527-cloudrunjobs/

### Pythonからの実行方法についての解説

https://zenn.dev/optimind/articles/run-cloudrun-jobs

### その他さまざまな実装の紹介

https://qiita.com/sugarperson/items/95cf60dec7511928104e
新機能紹介：Vertex AI SDK for Pythonでトークン数から効率的にコストを予測！

こんにちは。codepoet です。

### はじめに
プレビュー機能として、Vertex AI SDK for Pythonに、トークンリストとカウント機能が追加されました。この機能を使うと、プロンプト内のトークンを簡単に確認でき、コストの予測も正確に行えるようになります。

### 目次
1. [トークンとは？](#トークンとは？)
1. [トークンのリストとカウント方法](#トークンのリストとカウント方法)
1. [まとめ](#まとめ)
1. [参考文献](#参考文献)

### トークンとは？
トークンとは、自然言語処理において、文章を意味のある最小単位に分割したものです。例えば、「吾輩は猫である。」という文章は、複数のトークンに分割されます。単語や句読点だけでなく、サブワードや文字の一部がトークンとなる場合もあります。生成AIモデルは、これらのトークンを基に文章を生成したり、理解したりします。トークンの数は、AIが処理する量に影響するため、重要です。

トークンを数えることで、以下のメリットがあります。

* **コスト予測:** プロンプトの長さに応じて料金が発生するため、トークン数を把握することでコストを予測できます。
* **プロンプト最適化:** トークン数の上限を超えないようにプロンプトを調整することができます。
* **モデル理解:** トークンのリストを見ることで、モデルがどのように文章を解釈しているのかの理解に役立ちます。

### トークンのリストとカウント方法
トークンのリストを取得する例：
```python
from vertexai.preview.tokenization import get_tokenizer_for_model

# トークナイザを取得
tokenizer = get_tokenizer_for_model("gemini-1.5-flash")

# プロンプトのトークンを取得
prompt = "吾輩は猫である。"
response = tokenizer.count_tokens(prompt)
print(f"トークン数: {response.total_tokens}")

# トークンの分割箇所を検証
prompt = "吾輩は"
response = tokenizer.count_tokens(prompt)
print(f"トークン数: {response.total_tokens}")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665456/5b6a2b69-0e18-a226-5ee0-de49de27dd93.png)

`'吾', '輩', 'は', '猫', 'である', '。'` に分割されていそうですね。

トークン数と課金対象文字数を取得する例：

```python
import vertexai
from vertexai.generative_models import GenerativeModel

# プロジェクトとロケーションを設定
vertexai.init(project=PROJECT_ID, location=LOCATION)

# モデルを取得
model = GenerativeModel("gemini-1.5-flash")

# トークン数と課金対象文字数を取得
prompt = "吾輩は猫である。"
response = model.count_tokens(prompt)
print(f"トークン数: {response.total_tokens}")
print(f"課金対象文字数: {response.total_billable_characters}")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/665456/d4b3f141-049b-72da-38e3-6105ebdd3c6f.png)

### まとめ
トークンリストとカウント機能を使うことで、プロンプト内のトークンを効率的に確認、コストを管理できるようになります。ぜひ活用してみてください。

### 参考文献

* [公式ドキュメント](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/list-token)
Amazon QuickSight で選択された期間の差分を計算する

## はじめに

Amazon QuickSight を利用する中で検索期間を絞り込んだ際に検索期間の差分を表示する必要があるケースを想定して手順を備忘録として残していきます。



## 手順

### 1. パラメータの作成
フィルターの開始日と終了日を格納するためのパラメータを2つ作成します。
- 開始日用パラメータ（例：StartDate）
- 終了日用パラメータ（例：EndDate）

![Screenshot 2024-08-02 at 16.47.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147300/40d31367-f108-7bb3-d309-cc40de7eaa3b.png)

### 2. コントロールの追加
手順 1 で作成したパラメータをそれぞれコントロールに追加します。
今回は選択された期間の月の差分を取ろうと思うので、日付形式も一旦 `YYYY/MM/DD` の形式にしておきます。

![Screenshot 2024-08-02 at 16.48.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147300/ee463045-8f38-fa2e-5406-82e82b6899af.png)

### 3. 日付フィルターの作成とパラメータの連携
データセットの日付フィールドに対してフィルターを作成します。
左上のフィルターマークをクリックして、フィルター追加をクリックします。
フィルターの対象に販売日を選択して、期間の条件として作成したパラメータを選択します。

![Screenshot 2024-08-02 at 16.53.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147300/e7e0bed4-ea85-c4a8-d4a4-f4dc92f4b490.png)

### 4. 計算フィールドの作成 
選択された期間の月数を計算するための計算フィールドを作成します。

![Screenshot 2024-08-02 at 16.59.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147300/fefb16dd-1807-b37c-456f-5e371b556d7b.png)

今回は月数の差分を表示するため、以下のような関数を使用します
```
min(dateDiff(${StartDate}, ${EndDate}, "MM")+1)
```

この関数は、開始日と終了日の差を月単位で計算し、1を加えて選択期間の月数を正確に表します。

### 4. ビジュアルへの適用:
作成した計算フィールドをビジュアルに追加して、選択された期間の月数を表示します。

![Screenshot 2024-08-02 at 17.04.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147300/03ff7769-33ab-8b0f-1d56-cd6550d5ddc5.png)


## 注意点
ただし、この方法を利用する場合はデフォルトのフィルター機能で提供されるコントロールではなく、パラメータのコントロールでのみ期間を変更出来るようになるので注意です。
[paiza]と～う！！

# 塔とか大砲とかってなんかいいよね
バベルの塔　とか
塔の上のゲフンゲフン（著作権怖い）　とか　
結晶塔の帝王 ENTEI（個人的にはSUIKUN派）　とか
ネオアームストロングサイクロンジェットアームストロング砲（完成度たけーなオイ）　とか

https://paiza.jp/works/mondai/real_event/hanoi

# 面倒くさいので画面パタメータをListでもらった後のFunctionだけ
ちなみに引数のListを作ってる箇所は[こっちの記事](https://qiita.com/AsilHatake/items/842548e48ae4a0eae119)に書いてあるYO!!

```javascript:hanoi.js
// [問題文（原文）]
// ハノイの塔というパズルがあります。
// 
// 3つの杭があり、左から順にA,B,Cの杭とします。
// 杭Aに円盤が下から大きい順に n 個重なって置かれています。
// この円盤は必ず1つ上の円盤は下の円盤より小さくなくてはいけないルールがあります。
// 
// このルールを守りながらAの杭からCの杭へ円盤を動かす操作をするパズルです。
// 
// 例えば3つの円盤が入力として与えられる場合、その時の最短手順は以下の通りになります。
// この時、円盤の数に寄らず最短手順は常に一意に決まります。
// 
// 円盤の数 n が与えられます。1つも動かしていない状態を t = 0 とし、円盤を動かした回数を t とします。
// 円盤の数 n と、円盤を動かした回数 t が与えられるので n 個の円盤を最短手順で動かしていった時に円盤を動かした回数 t の状態を出力してください。
function hanoi(lines) {
  // 入力は以下のフォーマットで与えられます。
  // 
  // n t
  const [n, t] = lines[0].split(' ').map(Number);

  // 配列の初期化
  // 最初の棒に円盤セット
  const rods = [Array.from({ length: n }, (_, i) => i + 1).reverse(), [], []];

  // 移動直後の状態
  const steps = [];
  // 移動回数
  let currentStep = 0;
  // 再帰用
  const moveDisks = (numDisks, fromRod, toRod, auxRod) => {
    // 円盤がない、指定回数に達したら何もしない
    if (numDisks === 0 || currentStep >= t)
      return;

    moveDisks(numDisks - 1, fromRod, auxRod, toRod);

    if (currentStep < t) {
      rods[toRod].push(rods[fromRod].pop());
      steps.push(JSON.parse(JSON.stringify(rods)));
      currentStep++;
    }

    if (currentStep >= t)
      return;
    moveDisks(numDisks - 1, auxRod, toRod, fromRod);
  }

  // 最初は左から右へ移動
  moveDisks(n, 0, 2, 1);
  // n 個の円盤を最短手順で動かして行った時、 t 回動かした時点の各杭に積み上がっている円盤を出力してください。
  // 
  // 1行目を杭A、2行目を杭B、3行目を杭Cとし、各行の一番左を杭の一番下として円盤の大きさを 1 から n の数字でスペース区切りで出力して下さい。
  // 1つも円盤がない杭の行には「-」と出力して下さい。
  steps[t - 1].forEach(rod => console.log(rod.length ? rod.join(" ") : "-"))
}

module.exports = {
  hanoi
};
```

## 再帰でやってること
- n-1枚の円盤を元のロッド（fromRod）から補助のロッド（auxRod）に移動
- 最も大きな円盤（n番目の円盤）を元のロッド（fromRod）から目的のロッド（toRod）に移動
- 補助のロッド（auxRod）に移動したn-1枚の円盤を目的のロッド（toRod）に移動

# ちなみに
元サイトの問題文の下に条件式ってところに書いてあったんだけど、
これの最短回答って「$2^n$ - 1」らしいよ。
2枚：$2^2$ - 1 = 3回
3枚：$2^3$ - 1 = 7回
4枚：$2^4$ - 1 = 15回
16枚：$2^1$$^6$ - 1 = 65535回
※16は問題の最大のNの数

なお16枚の12345、54321回動かした結果は以下
```text
16 12345
16 15 12 11 10 9 8 7
3 2
14 13 6 5 4 1
```
```text
16 54321
14 11 6 5
13 10 9 8 7 4 3 2
16 15 12 1
```
FuelPHP のパフォーマンス確認方法

# はじめに
FuelPHP にはデフォルトで Profiler が用意されています
http://fuelphp.jp/docs/1.7/general/profiling.html

:::note info
`config.php` は `fuel/app/config/config.php` もしくは `fuel/app/config/$FUEL_ENV/config.php` となります
:::

# フロントエンドで確認する

`config.php` で `'profiling'  => true` を指定すると画面右下に `Code Profiler` が出てきます
![355344427-d4a78362-6ecb-4dd9-a249-4b1f9d284817.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357904/d61ddc36-07a8-bfc8-5f7c-4270c9307cc8.png)

それを選択することで簡単なパフォーマンスを確認できます
![355344647-0107f1ea-1afd-4627-b2b2-00dcd7fb333f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357904/0bb1d919-8f4c-b098-e2f4-9f282aa04982.png)

これを元にボトルネックを探すことができます

# API を確認する
API の場合はフロントエンドに表示される `Code Profiler` では情報を得ることができません
そのため Profiler の結果を Log に落とす必要があります

`config.php` に `'log_profile_data' => true` を指定すると `fuel/app/logs/YYYY/MM/DD-profiler-YYYYMMDDHHmmSS.php` にログを出力してくれます

![4cea05f897d7be9608ac48d7b8fac728.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357904/3f17a6bc-97b5-a975-eb21-c2f0f834be1f.png)

データは JSON 形式になっているため少し見辛いかもしれませんが、逆に JSON 形式であるからこそプログラムやコマンドでの調査が捗りそうです
![スクリーンショット 2024-08-06 16.10.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357904/05b8be60-3a8c-01e8-c2a8-1696ce5d53de.png)

:::note info
Ajax の場合は JSON 形式、通常リクエストの場合は var_export の結果が出力されるようです
:::

:::note warn
この設定は公式ドキュメントには記載されていないと思いますので利用する際は自己責任でお願いいたします
:::

# まとめ
* フロントエンドで確認する場合は `config.php` に `'profiling'  => true` を設定
* ログの表示が必要な場合は `config.php` に `'log_profile_data' => true` を設定
一番最後の git リビジョン番号 を知りたい時のコマンド

```bash
git log -n 1 --format="%H"
```

用心深く確認したいとき

```bash
$ git log -n 1 --format="%H"　&& git log | head -5
```

例

```bash
$ git log -n 1 --format="%H"
f9a3e2b6d7c8f1a4b0d2e3f4c9a1b7d6e8f5c0a3

$ git log -n 1 --format="%H"　&& git log | head -5
f9a3e2b6d7c8f1a4b0d2e3f4c9a1b7d6e8f5c0a3
commit f9a3e2b6d7c8f1a4b0d2e3f4c9a1b7d6e8f5c0a3
Author: ymatsukawa <ymatsukawa@example.com>
Date:   Thu Aug 1 14:31:25 2024 +0900

    fix: bye-bye tiny bug
```


## 使い所

* squash でまとめる
* (リビジョン番号をコピー) << ここ
* ブランチ変更
* リビジョン番号を基に cherry-pick する

mac OSX なら下のようにコピー可

```bash
git log -n 1 --format="%H" | pbcopy

# コマンド + v で貼り付け可
```

pbcopy はクリップボードへのコピー
MySQL 本番環境で general_log の有効化は慎重に

MySQL8 より、デフォルトでは無効な general_log に関する話

## TL;DR

* 有効化すると、DB システムへよりパフォーマンスが求められる
  * 「全クエリ」を「クエリ発行ごとに」ログへ記録するから
  * すなわち、クエリ発行分の I/O 負荷が DB システムに上乗せされる
* なので、理由もなく本番適用をするのは避ける

### 実体験と記録経緯

本番稼働 MySQL8 サーバー の CPU 使用率が 70% だった(普段は 30% 前後)

原因調査のため general_log を有効化すると、CPU 使用率が 90% に上がった

当時そうなった理由を一部でも理解しておきたかった

## general_log とは

「発行される全クエリ」を都度記録するログおよび設定値のこと。アプリ開発で例えると

「ログレベル関係なし(VERBOSE, DEBUG...)の全部盛りログ」である(あくまでイメージ)

### 記録されるもの

* `SELECT`, `UPDATE`, `INSERT`, `DELETE` のデータ操作
* `BEGIN`, `COMMIT` のトランザクション文
* `PREPARE`, `EXECUTE` プリペアードステートメント
* `SET`, `SHOW` など、管理者が実行するステートメント
* その他「クエリ」に該当するもの全て

を記録する。とにかく「出せるものを、**一行一行全て**」出す

<details>
  <summary>general_log の例 (クリックで開閉)</summary>

```
2024-07-31T14:29:04.166514Z	    8 Query	BEGIN
2024-07-31T14:29:08.366769Z	    8 Query	SELECT * FROM users
2024-07-31T14:29:55.463541Z	    8 Query	SELECT * FROM users as u INNER JOIN user_profiles AS up ON u.profile_id = up.id
2024-07-31T14:29:59.079863Z	    8 Query	COMMIT
2024-07-31T14:31:55.079049Z	    8 Query	PREPARE statement_x FROM ...
2024-07-31T14:31:55.082580Z	    8 Prepare	SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse
2024-07-31T14:32:02.069460Z	    8 Query	SET @x = 3
2024-07-31T14:32:05.571490Z	    8 Query	SET @y = 4
2024-07-31T14:32:30.532325Z	    8 Query	EXECUTE statement_x USING @x, @y
2024-07-31T14:32:30.535364Z	    8 Execute	SELECT SQRT(POW(3,2) + POW(4,2)) AS hypotenuse
2024-07-31T14:32:51.108459Z	    8 Query	DEALLOCATE PREPARE statement_x
```
</details>

### 実装俯瞰

MySQL 実装より 「**[クエリ発行ごと](https://github.com/mysql/mysql-server/blob/596f0d238489a9cf9f43ce1ff905984f58d227b6/sql/sql_parse.cc#L2065-L2086)**」にログへ記録していることが分かる

つまり、general_log 有効化で DB へ追加負荷がかかることは予測できる

## 本番への適用はどうすべきか

私見では次に挙げるものが特にない限り、本番環境での有効化は推奨しない

* general_log をどう管理するかの具体的な計画・ポリシー
* 万が一の場合、追加負荷を賄えるだけの力
  * ハードウェアのスペック
  * 平たく言えば財力
  * etc.
【今さら聞けない！】　オンライン会議ツールのショートカットキー一覧

こんにちは！
1年目の小川です。

今回はオンライン会議ツールのショートカットキーについてまとめていきたいと思います。

コロナウイルスの流行により、オンラインで会議やイベントを行うことが当たり前になりました。
オンライン会議ツールといっても多くの種類があるので、今回は、**zoom**、**Google meet**、**slackのハドルミーティング**の３つについてまとめていきたいと思います。

# こんな人に読んで欲しい！
* 音声読み上げソフトを利用している方
* マウスでの操作が難しい方
* キーボードのみでの操作を習得中の方
* 時短したい方

# 使用環境
* Mac

※Windows向けのコマンドも紹介します！

# 記号の説明
これからコマンドを紹介していく上で、記号を用いることがあるので、ここであらかじめ紹介しておきたいと思います。

 | 記号 | ボタン | 
 | ----- | ----- | 
 | ^ | コントロール | 
 | ⌘ | コマンド | 
 
# zoomのショートカットキー
まず最初に**zoom**のショートカットキーについてまとめていきたいと思います

 | 操作内容 | Mac | Windows |  
 | ------ | ------ | ------ | 
 | ギャラリービューの時に前後の25人を表示 | ^ + P | PageUP/PageDown | 
 | スピーカービューに切り替える | ⌘ + W | Alt + F1 | 
 | ギャラリーんビューに切り替える | ⌘ + W | Alt + F2 | 
 | ビデオのオン/オフ | ⌘ + V | Alt + V | 
 | ミュートのオン/オフ | ⌘ + A | Alt + A | 
 | 画面共有の開始/終了（コントロールツールバーにフォーカスがある時） | ⌘ + S | Alt + S | 
 | 画面共有の一時停止/再会（コントロールツールバーにフォーカスがある時） | ⌘ + T | Alt + T | 
 | ローカルレコーディングの開始 | ⌘ + R | Alt + R | 
 | クラウドレコーディングを開始 | ⌘ + C | Alt + C | 
 | レコーディングの一時停止/再会 | ⌘ + P | Alt + P | 
 | 全画面表示のオン/オフ | ⌘ + F | Alt + F | 
 | チャットパネルの表示/非表示 | ⌘ + H | Alt + H | 
 | 参加者パネルの表示/非表示 | ⌘ + U | Alt + U | 
 | 招待ウィンドウを表示 | ⌘ + I | Alt + I | 
 | ミーティングの終了 | ⌘ + Q | Alt + Q | 
 | 全員をミュート（ホストのみ可能）| ⌘ + ^ + M | Alt + M | 
 
 
 # Google meetのショートカットキー
 続いて以下では、**Google meet**のショートカットキーをまとめていきます。

  | 操作内容 | Mac | Windows |  
  | ------ | ------ | ----- |  
 | 現在発言している人を知らせる | ^ + ⌘ + S | ^　+　Alt　+　S | 
 | 字幕を表示または非表示にする | C | c
 | ピクチャー イン ピクチャー モードを開く | Shift + M | Shift + M | 
 | カメラを有効または無効にする | ⌘ + E | ^ + E | 
 | マイクをミュートまたはミュート解除する | ⌘ + D | ^ + D | 
 | 参加者を表示するタイルの数を増やす | ^ + ⌘ + K | ^ + Alt + K | 
 | 参加者を表示するタイルの数を減らす | ^ + ⌘ + J | ^ + Alt + J | 
 | 会議のチャット ウィンドウを表示または非表示にする | ^ + ⌘ + C | ^ + Alt + C | 
 | 参加者を表示または非表示にする | ^ + ⌘ + P | ^ + Alt + P | 
 | 挙手する、挙手をやめる | ^ + ⌘ + H | ^ + Alt + H | 
 | 自分の動画フィードを最小化または拡大する | ^ + ⌘ + M | ^ + Alt + M | 
 | 話す | スペースキーを長押しする | スペースキーを長押しする | 

  # slackのハドルミーティングのショートカットキー
最後に**slackのハドルミーティング**のショートカットキーを紹介します。

 | 操作内容 | Mac | Windows | 
 | ------ | ------ | ------ | 
 | ハドルミーティングを開始、参加、退出、または終了する | ⌘ + shift + H | ^ + shift + H | 
 | ハドルミーティングでミュートのオン / オフを切り替える | ⌘ + shift + スペース | ^ + shift + スペース | 


 # 最後に
今回は、3つのオンライン会議ツールのショートカットキーについてまとめてきました。
ここで紹介していない以外にも複数のオンライン会議ツールがあると思うので、興味がある方はぜひ調べてみてください！
ここまで読んでいただきありがとうございました！
【C#5】抽象クラスを使った例

# はじめに
C#で抽象クラスを使った例を実装しました。

## クラス
抽象クラスを***魚クラス***とし、***さんまクラス***、***カツオクラス*** を作りました。

## 実装したソースコード
実装したソースコードです。
```C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace Fish1{
    //  抽象クラス動物
    abstract class Fish
    {
        //  名前
        protected string name = "";
        //  泳ぐ
        public abstract void Swim();
        //  名前を表示する
        public void ShowName()
        {
            Console.WriteLine("名前:{0}", name);
        }
        public Fish()
        {

        }
        public Fish(string name)
        {
            this.name = name;
        }
        public string Name
        {
            get { return name;}
            set { name = value;}
        }

    }


    //  さんまクラス
    class Pike : Fish
    {
        public Pike()
        {
            name = "さんま";
        }
        //  泳ぐ
        public override void Swim()
        {
            Console.WriteLine("さんまが泳ぐ");
        }
        
    }
    
    //  さんまクラス
    class Bonito : Fish
    {
        public Bonito()
        {
            name = "カツオ";
        }
        //  泳ぐ
        public override void Swim()
        {
            Console.WriteLine("カツオが泳ぐ");
        }
        public Bonito(string name)
        {
            this.name = name;
            
        }
        
        
    }


    class Program
    {
        static void Main(string[] args)
        {
            //  Fishクラスのオブジェクトの配列を生成
            Fish[] fishes = new Fish[2];
            fishes[0] = new Pike();     //  さんまクラスのインスタンス生成
            fishes[1] = new Bonito();   //  カツオクラスのインスタンス生成   
            
            for (int i = 0; i < fishes.Length; i++)
            {
                fishes[i].ShowName();
                fishes[i].Swim();
                Console.WriteLine("------------");
            }
            //  Fishクラスのオブジェクトの配列を生成
            
            Fish[] fishes1 = new Fish[1];
            fishes1[0] = new Bonito("カツオ");     // カツオクラスのインスタンス生成
            
            for (int i = 0; i < fishes1.Length; i++)
            {
                fishes1[i].ShowName();
                fishes1[i].Swim();
                Console.WriteLine("------------");
            }
        }
    }
}
```

# 最後に
改造する予定です
Openpyxl: コラムの削除

## プログラム

```py:delete_columns.py
#! /usr/bin/python
# ---------------------------------------------------------------
#	delete_columns.py
#
#					Aug/06/2024
# ---------------------------------------------------------------
import sys
import openpyxl
#
# ---------------------------------------------------------------
xlsx_in=sys.argv[1]
#
sys.stderr.write("xlsx_in = %s\n" % xlsx_in)
wb = openpyxl.load_workbook(filename=xlsx_in)
ws = wb.worksheets[0]
#
ws.delete_cols(29,5)
ws.delete_cols(23,5)
ws.delete_cols(17,5)
#
wb.save(xlsx_in)
# ---------------------------------------------------------------
```

## 実行コマンド

```bash
./delete_columns.py in01.xlsx
```
Python ログ出力

## はじめに
Python でコーディングを行うとき、logging の機能を使ってログを出力するととても便利です。Python 3.xx であれば、既定で備わっている機能なので、モジュールの追加インストールなども必要ありません。

同機能の詳細な動きは、以下のサイトを参考にさせていただきました。

https://zenn.dev/dencyu/articles/2b58f669bcd473

ただ、詳細なアーキテクチャを理解していなくても、基本的なログ出力機能を実装することはさほど難しくありません。
簡単な実装例とともに、私が理解している範囲で、ログ出力の機能を説明します。

## ログレベル
ログレベルとしては、Debug から Critical までの５段階が準備されています。
Info レベル以上をログ出力し、Error が出力される場合に、具体的な対処につなげるような実装が一般的なようです。
|レベル|説明|
|---|---|
|Debug|詳細なシステムの動きを診断|
|Info|想定通りに動作し|
|Warning|想定通りに動作していない場合や、将来的に問題につながる可能性|
|Error|問題が発生しており、対処が必要|
|Critical|深刻な問題が発生しており、プログラムが動作を継続不可能|

## 実装
具体的な実装例を通じて、処理の流れを説明します。

### 基本設定

```python: ログ基本設定
logging_format = '%(name)s:%(asctime)s:%(levelname)s:%(message)s'
logging.basicConfig(filename='/home/testuser/test.log', filemode = 'a', level=logging.INFO, format=logging_format)
logger = logging.getLogger('計算')
```

logging_format でログの書式を設定します。設定値は以下の通りです。

|設定|説明|
|---|---|
|%(name)s |logger で指定するロガーオブジェクト名|
|%(asctime)s|ログを出力する日時|
|%(levelname)s |Info、Error などのログレベル|
|%(message)s |任意で指定するメッセージ|

logging.basicConfig でログファイルや書式などを指定します。filemode=’w’ を指定するとログファイル上書き、filemode=’a’を指定すると既存ログファイルに追加です。

logger の中でログを出力する処理名を指定します。
### ログ出力
実際にログを出力する箇所に、条件に応じた出力を指定します。

```python: ログ出力
try:
    new_value = value1 / value2
    logger.info("正常に処理完了")
except Exception as e:
    logging.exception("例外発生")
    exit()
```

上記の例では、new_value が正常に算出されると、以下のようなログが出力されます。

```test:正常時
計算:2024-08-06 14:49:38,800:INFO:正常に処理完了
```

一方で、new_value が算出できない場合は、エラーログが出力されます。下記は意図的に`value2=0` を指定して ZeroDivisionError を発生させた際の出力例です。

```text:異常時
root:2024-08-06 14:49:25,321:ERROR:例外発生
Traceback (most recent call last):
  File "/home/testuser/test.py", line 12, in <module>
    new_value = value1 / value2
                ~~~~~~~^~~~~~~~
ZeroDivisionError: division by zero
```

## 最後に
Yellowfin で参照するデータは正確なものである必要があります。
そのためには、詳細にログを出力し、問題がある場合は迅速な対応が望まれます。

ということで、ログ出力に関する極めて基本的な内容をまとめてみました。
一方で、複雑な環境で運用する場合には、色々と考慮することも多いようです。
私も他の方の記事などを参考に、継続して学んでいこうと思います。

では皆様、良いデータ分析を!
新しいReactプロジェクトの始め方

## アジェンダ
新しいReactプロジェクトをセットアップするための基本的なステップをまとめます。

## 1. Node.jsとnpmのインストール
Reactプロジェクトを始めるには、まずNode.jsとnpm（Node Package Manager）が必要です。これらがインストールされていない場合は、[Node.jsの公式サイト](https://nodejs.org/)からインストールしてください。

### インストール方法（例：Homebrewを使用）

```sh
brew install node
```

インストール後、以下のコマンドでバージョンを確認できます。

```sh
node -v
npm -v
```

## 2. Create React Appの利用

Reactアプリケーションを素早くセットアップするために、Facebookが提供する公式のCLIツールであるCreate React Appを使用します。このツールは、プロジェクトの設定や依存関係の管理を自動化します。

### 新しいReactプロジェクトの作成

次に、`create-react-app`コマンドを使用して新しいReactプロジェクトを作成します。

```sh
npx create-react-app my-react-app
```

このコマンドにより、`my-react-app`という名前のディレクトリが作成され、その中にReactプロジェクトの初期設定が行われます。

## 3. プロジェクトのディレクトリに移動

プロジェクトが作成されたら、そのディレクトリに移動します。

```sh
cd my-react-app
```

## 4. 開発サーバーの起動

Reactの開発サーバーを起動して、アプリケーションをブラウザで確認します。

```sh
npm start
```

開発サーバーが起動し、ブラウザで `http://localhost:3000` にアクセスすると、新しいReactアプリケーションが表示されます。

## 5. プロジェクト構造の理解

作成されたプロジェクトのディレクトリ構造は以下のようになっています。

```
my-react-app/
├── node_modules/
├── public/
│   ├── index.html
│   └── ...
├── src/
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   └── ...
├── .gitignore
├── package.json
└── README.md
```

- `public/`ディレクトリには、静的なファイルが含まれています。
- `src/`ディレクトリには、Reactコンポーネントやスタイルシートなどのソースコードが含まれています。
- `package.json`ファイルには、プロジェクトの依存関係やスクリプトが定義されています。

## 6. プロジェクトのカスタマイズ

プロジェクトを作成したら、自分のアプリケーションに必要なカスタマイズを行っていきます。以下はよく行われるカスタマイズの例です。

### コンポーネントの作成

新しいコンポーネントを`src`ディレクトリに作成します。

```jsx:src/Hello.js
import React from 'react';

function Hello() {
  return <h1>Hello, React!</h1>;
}

export default Hello;
```

### コンポーネントの使用

作成したコンポーネントを他のコンポーネントで使用します。

```jsx:src/App.js
import React from 'react';
import Hello from './Hello';

function App() {
  return (
    <div className="App">
      <Hello />
    </div>
  );
}

export default App;
```

## まとめ

このガイドでは、新しいReactプロジェクトをセットアップするための基本的な手順を紹介しました。Create React Appを使用することで、迅速かつ効率的にReactアプリケーションを作成することができます。

## 参考
- [React プロジェクトを始める – React](https://ja.react.dev/learn/start-a-new-react-project)
- [新しい React アプリを作る – React](https://ja.legacy.reactjs.org/docs/create-a-new-react-app.html)
【Flutter】文字の下線を波線にするには？

こんにちは。
今回は、文字の下線を波線にする方法を紹介します。

# 方法
![スクリーンショット 2023-08-13 20.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/ce57fee2-1687-ea40-8538-b4c8b9db94e0.png)

Textの下線を波線にするには、TextStyleの引数「decorationStyle」を使います。

具体的には、TextStyleの引数「decorationStyle」に「TextDecoration.wavy」を指定します。


# 使用例
```dart
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(
        child: Text(
          '波線になります',
          style: TextStyle(
            decoration: TextDecoration.underline,
            decorationStyle: TextDecorationStyle.wavy,
          ),
        ),
      ),
    );
  }
}


```

# 実行例
![スクリーンショット 2024-08-06 15.14.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/33d1d8d9-6b98-3ffd-8c03-8c7797ecadba.png)




# 最後に
ここまで読んでいただき、ありがとうございました！
いいねしてくれたら、スキップして喜びます:heartpulse:
C#でPDFをパスワードで保護する

機密データを含むPDFファイルを扱うときは、許可された人だけがPDFを開く、表示、編集、または印刷できることを制限するために保護パスワードを設定することができます。

この記事では、無料の.NET PDFライブラリを使用して**C＃でパスワードでPDFファイルを暗号化する方法**を共有します。

## PDFを暗号化するための無料の.NETライブラリ
使用するPDFライブラリは[**Free Spire.PDF for .NET**](https://www.e-iceblue.com/Introduce/free-pdf-component.html)です。始める前に、手動でダウンロードしてインストールするか、Nuget経由で直接インストールする必要があります。
ダウンロードリンク: 

https://www.e-iceblue.com/Download/download-pdf-for-net-free.html

## C#でPDFファイルをパスワードで保護する方法
[Free Spire.PDF for .NET](https://www.e-iceblue.com/Introduce/free-pdf-component.html)では、PDF ファイルに以下の 2 種類のパスワードを設定できます。
* **ドキュメントを開くパスワード**：PDFファイルを開くためのパスワード。
* **許可パスワード**：印刷、コンテンツのコピー、コメントなどを制限するためのパスワード。


> PDFファイルをパスワードで暗号化するには、以下の手順を参照してください：

1\. 必要なネームスペースをインポートする；
```c#
using Spire.Pdf;
using Spire.Pdf.Security;
```
2\.	PdfDocumentクラスのインスタンスを作成し、PDFファイルを読み込みます；
```c#
PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile("input.pdf");
```
3\. オープンパスワードとパーミッションパスワードでPDFを暗号化し、パーミッションと暗号化キーサイズを設定します；
(FreeSpire.PDF for .NETは、暗号化キーサイズ：40bit、128bit、256bitをサポートしています)
```c#
pdf.Security.Encrypt("123", "abc", PdfPermissionsFlags.Print | PdfPermissionsFlags.CopyContent, PdfEncryptionKeySize.Key128Bit);
```
4\.	結果のPDFファイルを保存する。
```c#
pdf.SaveToFile("Encrypt.pdf", FileFormat.PDF);
```

![EncryptPDF.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2811193/40b3c865-03dc-dc08-e68d-f979dcaef550.png)



**フルC#コード:**
```c#
using Spire.Pdf;
using Spire.Pdf.Security;

namespace EncryptPDF
{
    class Program
    {
        static void Main(string[] args)
        {
            // PDFサンプルファイルの読み込み
            PdfDocument pdf = new PdfDocument();
            pdf.LoadFromFile("sample.pdf");

            //パスワードでPDFファイルを暗号化する
            pdf.Security.Encrypt("123", "abc", PdfPermissionsFlags.Print | PdfPermissionsFlags.CopyContent, PdfEncryptionKeySize.Key128Bit);

            //結果ファイルの保存
            pdf.SaveToFile("ProtectPDF.pdf", FileFormat.PDF);
        }
    }
}
```

## もっと見る
パスワードでPDF文書を暗号化した後、[パスワードを削除するには](https://www.e-iceblue.com/Tutorials/Spire.PDF/Spire.PDF-Program-Guide/Security/C-Encrypt-and-Decrypt-PDF-file.html#2)、無料の.NET PDF APIを使用することもできます。さらに、[PDFへのデジタル署名](https://www.e-iceblue.com/Tutorials/Spire.PDF/Spire.PDF-Program-Guide/PDF-Protection-Create-Digital-Signature-in-PDF-with-C-VB.NET.html)や他のPDF処理＆変換機能もサポートしています。

https://www.e-iceblue.com/Tutorials/Spire.PDF/Spire.PDF-Program-Guide/Spire.PDF-Program-Guide-Content.html
G検定合格しました！：市場価値を飛躍的に向上させるエンジニアの革新


![J-280.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82835/a665b396-d229-5b20-1130-76f1e1db3dbc.png)

---

この度、AIの知識を深めるために挑戦したG検定（ジェネラリスト検定）に無事合格しました！この合格を通じて、私の市場価値を飛躍的に向上させることができたと確信しています。ここでは、合格に至るまでの道のりと、今後の展望についてお話ししたいと思います。

#### G検定に挑戦した理由

AI技術がますます重要視される現代において、iOSエンジニアとして新たな価値を提供するためには、AIの知識が不可欠だと感じました。G検定に挑戦することで、AIの基礎から応用まで幅広く学び、私のスキルセットを大幅に拡充することができました。

#### 学んだ内容

G検定を通じて、以下の知識を深めました：

- **AIの基本概念**：機械学習、深層学習、データサイエンスの基礎
- **AIの応用分野**：自然言語処理、画像認識、推薦システムなど
- **倫理と法規**：AIの倫理的課題や法律の理解

#### 今後の目標

今回のG検定合格をきっかけに、さらなる成長を目指します。具体的には：

- **AI技術の実践的な応用**：実際のアプリ開発にAI技術を統合し、より高度な機能を提供
- **継続的な学習**：最新のAIトレンドを追い続け、常に最先端の知識を習得
- **チームとの協力**：AI技術をチーム内で共有し、全体のスキルアップを図る

#### 最後に

これからも自己成長を続け、新しい技術に挑戦し続けたいと思います。今回のG検定合格は、その一歩に過ぎません。引き続き応援よろしくお願いします！
誰でも分かるReactNative

## React Nativeとは？
React Native（リアクト ネイティブ）は、JavaScriptを使用してクロスプラットフォームのモバイルアプリケーションを開発するためのフレームワークです。Facebookによって開発され、iOSとAndroidの両方で動作するアプリを一度のコーディングで作成できるため、多くの開発者に愛用されています。

### React Nativeの特徴
#### 1. クロスプラットフォーム開発:

一つのコードベースでiOSとAndroidのアプリを開発可能
プラットフォーム固有のコードを必要最小限に抑えることができる
#### 2.ホットリロード:

コードの変更をリアルタイムでアプリに反映
開発効率が大幅に向上
#### 3.豊富なライブラリとコミュニティ:

多数のサードパーティライブラリが利用可能
活発なコミュニティによるサポート
## なぜReact Nativeを選ぶべきか？
### コストと時間の節約
一度の開発で複数のプラットフォームに対応できるため、開発時間とコストを大幅に削減できます。例えば、小さなスタートアップ企業が限られた予算でiOSとAndroidの両方に対応するアプリをリリースしたい場合、React Nativeは最適な選択です。

### 優れたパフォーマンス
React Nativeは、JavaScriptで記述されたコードをネイティブコードに変換するため、パフォーマンスが非常に高いです。ユーザーにとってもネイティブアプリと同様のスムーズな体験を提供できます。

## React Nativeの基本的な使い方
### 環境構築
React Nativeを始めるには、まず開発環境を整える必要があります。以下は簡単な手順です。

1. Node.jsをインストール
2. React Native CLIをインストール
```npm install -g react-native-cli```
3. 新しいプロジェクトを作成
```npx react-native init MyNewProject```
### 基本的なコード例
```import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

const App = () => {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Hello, React Native!</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#f0f0f0',
  },
  text: {
    fontSize: 20,
    color: '#333',
  },
});

export default App;
```
この例では、基本的なコンポーネントの作成方法とスタイルの適用方法を示しています。Viewコンポーネントはレイアウトのためのコンテナであり、Textコンポーネントはテキスト表示のために使用します。

## まとめ
React Nativeは、効率的にクロスプラットフォームのモバイルアプリを開発するための強力なツールです。初心者でも学びやすく、多くのリソースが揃っているため、今後のプロジェクトにぜひ取り入れてみてください。
webデザイナーによるターミナル入門 -画像の操作-

先日、ターミナル入門の記事を書いたばかりですが、再びターミナルについてです！
https://qiita.com/marietti/items/ab4cb8835b1b3a733502

今回は、画像を一気にリサイズしたい場面に直面し諸々調べて操作したので、
ターミナルでの画像操作をまとめていこうと思います！

## 基本の記述法
```shell-session
$ ships -hoge(option) ***(該当の画像,ディレクトリ) -out(出力先のディレクトリ)
```
macで画像操作をする際は、**`ships`コマンド**を使います。
基本的には上書き保存されてしまうので、`-out hoge`で出力先(hogeディレクトリ)を指定してあげることが多くなりそうです。

## 画像をリサイズしたい
リサイズの基準はいくつかあります。
    ここで紹介するものは縦横比固定でリサイズできます！

### 最大値を指定してリサイズ
```shell-session
$ sips -Z 1000 sample.png
```
`1000`の部分に希望のpx数を記載すればOK
縦を指定したい場合は`–resampleHeight`オプションをプラス
横を指定したい場合は`–resampleWidth`オプションをプラスしましょう

### 幅を指定してリサイズ
```shell-session
$ sips --resampleWidth 1000 sample.png
```
`1000`の部分に希望のpx数を記載すればOK

### 高さを指定してリサイズ
```shell-session
$ sips --resampleHeight 1000 sample.png 
```
`1000`の部分に希望のpx数を記載すればOK


## 画像の拡張子を変換したい
```shell-session
$ ships -s format 形式(拡張子名)
```
画像フォーマットの変換は`-s`オプションで可能です。
指定できる拡張子は下記の8種類のようです。
**`jpeg`,`tiff`,`png`,`gif`,`jp2`,`bmp`,`psd`,`tga`**

### webpの変換
webpについては、別途「cwebp」というコマンドをインストールし、実行していく必要があります。

#### 手順01：「cwebp」のインストール。
```shell-session
$ brew install webp
```
`brew list`コマンドで一覧にwebpがあればインストール成功です。

#### 手順02：Shellスクリプトの用意
変換したい画像をまとめたディレクトリ内に、Shellスクリプトを作成します。保存形式は**`hoge.sh`**です。
下記がShellスクリプトの中身です
```shell
#!/bin/sh
for file in `find . -type f -name "*.jpg" -or -name "*.jpeg" -or -name "*.png"`;
do
cwebp $file -o "${file%.*}.webp" >/dev/null 2>&1
done
```
こちらの例では`jpg`pr`png`のファイルを全てwebpに変換します。

#### 手順03：Shellスクリプトに実行可能な権限を付ける
Shellスクリプトは作った段階では実行権限がありません。
実行権限を付与するため**Shellスクリプトがあるディレクトリ**で以下の操作を実行します。
```shell-session
$ chmod 755 webp.sh
```
これで準備完了！

#### 手順04：Shellスクリプト実行
先ほど作ったShellスクリプトがあるディレクトリに移動し、Shellスクリプトを実行します
```shell-session
$ ./webp.sh
```
スクリプトを実行後、webpファイルが元あった画像データと同じ数生成されていれば成功です！


## その他
```shell-session
$ sips --help
```
ヘルプを見ればオプションが一覧で表示されます！


ターミナルの苦手意識をなくし、業務効率化していきたいです
paiza Bランク「神経衰弱」Rubyで解いてみた

## はじめに
paiza x Qiitaコラボ企画に乗っかってみました。

## 問題概要
問題文は[こちら](https://paiza.jp/works/mondai/b_rank_skillcheck_archive/concentration)
トランプの神経衰弱です。
最初にカードの配置と各ターンでどの位置のカードが捲られたかの情報が与えられ、それを元に各メンバーが何枚カードをゲットしたか出力します。
与えられる入力のフォーマットは下の通りです。
```
H W N                        # トランプは縦H枚、横W枚並べられ、プレイヤーはN人
t_{1,1} t_{1,2} ... t_{1,W}  # H行にわたってトランプの番号の情報が与えられる
t_{2,1} t_{2,2} ... t_{2,W}
...
t_{H,1} t_{H,2} ... t_{H,W}
L                # 捲られたトランプの情報が以降L行に渡って与えられる
a_1 b_1 A_1 B_1  # 1ターン目ではa_1行b_1列のトランプとA_1行B_1列のトランプが捲られた
a_2 b_2 A_2 B_2
...
a_L b_L A_L B_L
```
例えば下の入力例では、
- 縦2枚、横3枚のトランプが並べられ、プレイヤーは2人（1行目）
- 置かれたカードの情報(2~3行目)
- 捲られたカードの情報数(4行目)
- 各ターンで捲られたカード情報(5行目~)
  例えば1ターン目では(1,1)と(2,1)の場所のカードが捲られた
  
ということがわかります。
```
2 3 2
1 2 3
2 1 3
5
1 1 2 1
1 1 1 2
1 1 2 2
1 3 2 3
1 2 2 1
```

## 自分の解答
``` Ruby
# 入力値の受け取り
H,W,N = gets.split.map(&:to_i)
cards = H.times.map { gets.split.map(&:to_i) }
L = gets.to_i
turns = L.times.map { gets.split.map(&:to_i) }

# 各プレイヤーが何枚カードをゲットしたか記録するハッシュ
result = {}
N.times do |i|
  result[i] = 0
end

# プレイヤーの交代回数を記録する変数
count = 0

# 各ターン毎に、捲ったカードの数字が一致したかどうかで場合分けをする
turns.each do |a1,b1,a2,b2|
  if cards[a1-1][b1-1] == cards[a2-1][b2-1]
    result[count % N] += 2
  else
    count += 1
  end
end

# 各プレイヤーの保持するカード枚数を出力
puts result.values
```
## 解説
### 入力値の受け取り
``` Ruby
H,W,N = gets.split.map(&:to_i)
cards = H.times.map { gets.split.map(&:to_i) }
L = gets.to_i
turns = L.times.map { gets.split.map(&:to_i) }
```
ここではgetsメソッドで標準入力から値を取得しています。
[公式リファレンス/getsメソッド](https://docs.ruby-lang.org/ja/3.3/method/IO/i/gets.html)
getsメソッドは与えられた入力を1行、文字列として読み取ります。

`gets.split.map(&:to_i)`では、getsで取得した文字列をsplitメソッドで分割し、`map(&:to_i)`で各要素をIntegerに変換しています。

`cards = H.times.map { gets.split.map(&:to_i) }`では、上記の作業をH回行うことで、cards変数にトランプの置き場所の情報が二次元配列として格納されます。
上の入力例であれば、`[[1, 2, 3], [2, 1, 3]]`となります。

同様に、turns変数には各ターンでめくったカード情報を二次元配列で格納しています。

### ハッシュ、変数の用意
次に、必要な数値を記録するための変数を用意します。
``` Ruby
# 各プレイヤーが何枚カードをゲットしたか記録するハッシュ
result = {}
N.times do |i|
  result[i] = 0
end
```
上記のハッシュは、キーをプレイヤーの番号(0 ~ N-1)、値をそのプレイヤーがゲットしたカードの枚数として記録します。
``` Ruby
# プレイヤーの交代回数を記録する変数
count = 0
```
捲ったカードの数字が一致しなかった場合は次のプレイヤーに交代します。
交代回数を記録するcount変数を用意しました。

### 各ターン毎の処理
``` Ruby
# 各ターン毎に、捲ったカードの数字が一致したかどうかで場合分けをする
turns.each do |a1,b1,a2,b2|
  if cards[a1-1][b1-1] == cards[a2-1][b2-1]
    result[count % N] += 2
  else
    count += 1
  end
end
```
各ターン毎に行う処理です。
- 捲ったカードの数字が一致した場合
  該当プレイヤーのカード枚数を2枚増やす
- 捲ったカードの数字が一致しなかった場合
  プレイヤーの交代回数を1増やす

【ポイント】
- 二次元配列cardsのindexは0から始まりますが、カードの場所として与えられる入力値は1から始まります。そのずれを調整するため、`cards[a1-1][b1-1]`と記述しています
- 現在のプレイヤー番号の割り出し方法
  プレイヤーの交代回数をプレイヤーの人数Nで割った余り`count % N`が、現在のプレイヤーの番号となります。
  なお、解答例ではプレイヤーの番号を変数で保持しておき、プレイヤー交代時は
   ``` Ruby
   player += 1
   player = 1 if player == N + 1
   ```
   としていました。
   要はプレイヤーの順番が最初の人に戻るのをどのように表現するかですが、解答例の方が直感的にわかりやすいのでこちらの方が良かったなと思います。

### 出力
``` Ruby
puts result.values
```
Hash#valuesでハッシュの値のみを得ることができます。

## 終わりに
記事にすることで、「こういう書き方をする意図は？」「もっといい書き方があるのでは？」と考えるきっかけになりました。

## 参考文献
[プログラミング言語 Ruby リファレンスマニュアル](https://docs.ruby-lang.org/ja/)
Unminify picoCTF writeup for beginner

<h3>記事説明</h3>
CTF初めて一カ月の主がPicoCTFのEasyをbeginnerむけに書いている記事です。CTFのwriteupではCTF常識が前提になっている記事が多く無知すぎてよくわからなかったので書くことにしました。至らぬところ等あると思いますのでご指摘いただけると嬉しいです。
#picoCTFbeginner


<h3>事前知識</h3>
<h4>Web inspector</h4>
表示中のWebページを構成するファイルや、HTMLやCSS、JavaScriptなどのソースコード、各要素のスタイルや属性などの詳細を表示するツール。

参考記事

https://qiita.com/KenRoda/items/c775f767645ead57fabc


<h3>flag獲得方法</h3>
与えられたwebサイトをweb inspectorで開く。

```html:index.html
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>picoCTF - picoGym | Unminify Challenge</title>
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <style>
            body {
                font-family: "Lucida Console",Monaco,monospace
            }

            h1,p {
                color: #000
            }
        </style>
    </head>
    <body class="picoctf{}" style="margin:0">
        <div class="picoctf{}" style="margin:0;padding:0;background-color:#757575;display:auto;height:40%">
            <a class="picoctf{}" href="/">
                <img src="picoctf-logo-horizontal-white.svg" alt="picoCTF logo" style="display:inline-block;width:160px;height:90px;padding-left:30px">
            </a>
        </div>
        <center>
            <br class="picoctf{}">
            <br class="picoctf{}">
            <div class="picoctf{}" style="padding-top:30px;border-radius:3%;box-shadow:0 5px 10px #0000004d;width:50%;align-self:center">
                <img class="picoctf{}" src="hero.svg" alt="flag art" style="width:150px;height:150px">
                <div class="picoctf{}" style="width:85%">
                    <h2 class="picoctf{}">Welcome to my flag distribution website!</h2>
                    <div class="picoctf{}" style="width:70%">
                        <p class="picoctf{}">If you're reading this, your browser has succesfully received the flag.</p>
                        <p class="picoCTF{pr3tty_c0d3_ed938a7e}"></p>
                        <p class="picoctf{}">I just deliver flags, I don't know how to read them...</p>
                    </div>
                </div>
                <br class="picoctf{}">
            </div>
        </center>
    </body>
</html>
```
クラス名にpicoCTF{pr3tty_c0d3_ed938a7e}となっているところがあり、flagを獲得できる。

<h3>答え</h3>


```:
picoCTF{pr3tty_c0d3_ed938a7e}
```
WebDecode picoCTF writeup for beginner

<h3>記事説明</h3>
CTF初めて一カ月の主がPicoCTFのEasyをbeginnerむけに書いている記事です。CTFのwriteupではCTF常識が前提になっている記事が多く無知すぎてよくわからなかったので書くことにしました。至らぬところ等あると思いますのでご指摘いただけると嬉しいです。
#picoCTFbeginner


<h3>事前知識</h3>
<h4>Web inspector</h4>
表示中のWebページを構成するファイルや、HTMLやCSS、JavaScriptなどのソースコード、各要素のスタイルや属性などの詳細を表示するツール。

参考記事

https://qiita.com/KenRoda/items/c775f767645ead57fabc


<h3>flag獲得方法</h3>
与えられたwebサイトにはHome,About,Contactの3つのページがあることがわかる。
Web inspectorを使って各ページのHTMLを見ていくとAboutのページに怪しいnotify_trueという属性を見つける。

```html:about.html
<section class="about" notify_true="cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfZjZmNmI3OGF9">
   <h1>
    Try inspecting the page!! You might find it there
   </h1>
   <!-- .about-container -->
  </section>
```
cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfZjZmNmI3OGF9
この怪しい文字列を<a href="https://gchq.github.io/CyberChef/">デコードツール</a>でデコードしてみる。

![スクリーンショット 2024-08-06 143032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1972935/128cf4df-0f89-fc4e-95d7-10b43b0baf77.png)

「cGljb0NURnt3ZWJfc3VjYzNzc2Z1bGx5X2QzYzBkZWRfZjZmNmI3OGF9」この文字列はbase64で表現されており、UTF-8で表現すると「picoCTF{web_succ3ssfully_d3c0ded_f6f6b78a}」であることがわかる。

<h3>答え</h3>


```:
picoCTF{web_succ3ssfully_d3c0ded_f6f6b78a}
```
Vite + React + React Router製のアプリをGitHubPagesでカスタムドメインに公開する

## 話すこと
- すでにGitHubでソース管理しているかつ実装済のReactアプリをGitHubPagesにデプロイする

## 話さないこと
- GitリポジトリやReactプロジェクトの作成方法
- カスタムドメインの取得方法

## 環境

|　技術 / ツール | バージョン |
|:-----------------|------------------:|
| Bun | 1.1.5 |
| TypeScript | 5.4.5 |
| Vite | 5.3.1 |
| React | 18.3.1 |
| React Router | 6.26.0 |

## 手順

### 1. GitHubリポジトリの設定

リポジトリのSettings > Pagesから、デプロイするブランチを選択後、docsを選択してSaveを押下します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679599/a635f7c5-8f9d-d988-0602-2abbd714101c.png)

### 2. vite.config.tsの変更

デプロイ時にディレクトリ名を変更したりコピーするのが面倒なのでビルドの出力先を予めデフォルトの`dist`から`docs`に変更しておきます。

```vite.config.ts
export default defineConfig(({ mode }) => ({
    // 他のオプションは省略します
    build: {
        outDir: 'docs',
    },
}));
```

### 3. デプロイスクリプトの作成

`bun run deploy`でデプロイできるようにスクリプトを作成します

```deploy.sh
# 前回のビルドを削除
rm -rf docs

# ビルド実行
bun run build 

# カスタムドメインの設定
touch docs/CNAME
echo "{カスタムドメイン}" > docs/CNAME

# 個別に必要なファイルをコピー
cp docs/index.html docs/404.html

# コミット & プッシュしてGitHubActionsをトリガー
git add .
git commit -m "deploy"
git pull
git push
```

> ```deploy.sh
> # カスタムドメインの設定
> touch dist/CNAME
> echo "{カスタムドメイン}" > dist/CNAME
> ```
ここでCNAMEを作成することで、GitHub側でいう下記のCustom domainの設定が完了します。
作成を忘れるとカスタムドメインで公開されなくなってしまいます。

このように手動でCNAMEを作成しないと毎回Custom domainがリセットされていたので、リセットされない方法があれば教えていただきたいです🙏

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679599/a0a8cbb7-9303-b194-8f8e-a2f0eb61a5aa.png)

また今回React Routerを使用していますが、普通にデプロイするとルーティングができずに下記のようなGitHubPagesの404ページが表示されてしまう事態が発生しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679599/e6470bee-5660-fbbe-e601-44308cb67306.png)

> ```deploy.sh
> # 個別に必要なファイルをコピー
> cp dist/index.html dist/404.html
> ```

上記のようにindex.htmlを404.htmlとしてコピーしてやると解決し、単純に存在しないページにアクセスした際にもReact Routerの`errorElement`が表示されるようにできました。

参照: [Github Pages で BrowserRouter が正常に処理されない](https://qiita.com/NagaJun/items/bed10dc5d826fda49ea0)

### 4. package.jsonに追加してデプロイ

package.jsonのscriptsに以下のコマンドを追加します

```package.json
  "scripts": {
    "deploy": "sh deploy.sh"
  }
```

`bun run deploy`を実行するとカスタムドメインへの公開が完了します。
[個人開発]「単行本の発売日？いつやねん！」を解決する、超シンプル漫画発売日検索サービスを作ってみた

## はじめに
こんにちは！24卒の新米社内SEです。大手ECサイトの保守・運用をメインの会社で社内SEとして勤務しています。最近はbrSEとして中規模ECサイトの案件で中国側とやり取りする業務も担当しています。そんな私ですが、仕事の合間や帰宅後の時間を使って、ちょっとしたサービスを作ってみました。

## サービス概要
「漫画発売日検索サービス」
URL: https://manga-service.web.app/
漫画の最新刊がいつ発売されるのか、いちいち調べるのがめんどくさい！そんな「めんどくさい」を解決するために作りました。

## 技術スタック

フロントエンド: React
バックエンド: Firebase (Firestore)
データ収集: Python (Selenium for web scraping)
ホスティング: Firebase Hosting

## データフロー

1.PythonスクリプトでSeleniumを使用し、週刊少年ジャンプの公式サイトから最新の漫画情報をスクレイピング(集英社は個人運営であれば問題ないみたいです)

2.Seleniumを使用することで、動的に生成されるコンテンツも取得可能
3.ヘッドレスモードで実行し、バックグラウンドでデータ収集を行う

4.収集したデータをFirestoreに保存

5.ReactアプリがFirestoreからデータを取得して表示

6.ユーザーが検索すると、Firestoreでクエリを実行

7.検索結果を表示し、各漫画タイトルにAmazonの検索リンクを付与

## 苦労したポイント
1. Seleniumを使ったスクレイピング
動的に生成されるコンテンツを取得するために、Seleniumを使用しました。以下は簡略化したコードの一部です：
```
pythonCopyfrom selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager

# Seleniumの設定
options = webdriver.ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)

# スクレイピングするURL
url = "https://www.shueisha.co.jp/books/newreleases/?gid=5"
driver.get(url)

# 必要な情報を抽出
comics = driver.find_elements(By.CLASS_NAME, 'book-item')
for comic in comics:
    title = comic.find_element(By.CLASS_NAME, 'book-title').text
    author = comic.find_element(By.CLASS_NAME, 'book-author').text
    release_date = comic.find_element(By.CLASS_NAME, 'book-release-date').text
    # ここでFirestoreにデータを保存する処理を行う

driver.quit()
```
2.Amazonリンクの生成
漫画のタイトルを使ってAmazonの検索リンクを生成するのが意外と難しかったです。最初は「めんどくさい」の精神に反して頑張りすぎてしまい、Amazon Product Advertising APIとにらめっこしていました。結局、シンプルに検索URLを生成する方法に落ち着きました。
```
javascriptCopyconst generateAmazonLink = (title) => {
  const encodedTitle = encodeURIComponent(title);
  return `https://www.amazon.co.jp/s?k=${encodedTitle}&tag=myassociateid-22`;
};
```
3.データベースの肥大化対策
「めんどくさい」のは使う側だけでなく、管理する側も同じ。データベースが肥大化するのを防ぐため、6ヶ月以上前に登録された漫画のデータを自動で削除するPythonスクリプトも作成しました。
```
pythonCopyimport firebase_admin
from firebase_admin import credentials, firestore
from datetime import datetime, timedelta

# Firebaseの初期化
cred = credentials.Certificate("path/to/serviceAccount.json")
firebase_admin.initialize_app(cred)
db = firestore.client()

# 6ヶ月前の日付を計算
six_months_ago = datetime.now() - timedelta(days=180)

# 古いデータを削除
comics_ref = db.collection('comics')
old_comics = comics_ref.where('release_date', '<', six_months_ago).get()

for comic in old_comics:
    comic.reference.delete()

print(f"{len(old_comics)} comics deleted.")
```
## まとめ
このサービスを通じて、フロントエンドからバックエンド、データ収集まで一通りの流れを経験できました。また、Amazonのアフィリエイトリンクも設置してみました（買ってくれたら紹介料入るんですかね？わからないけど）。
「めんどくさい」を解決するサービスを作るのは、意外と「めんどくさい」作業の連続でした。でも、自分で使ってみると「あ、これ便利かも」と思えるものができて満足しています。


## 追記
今までは集英社のみ対応していましたが
講談社も対応できるようにしました(結構てこずりました)
試し読みがある漫画にはそちらのリンクも追加しました
基礎ツール　picoCTF writeup for beginner

<h3>記事説明</h3>
CTF初めて一カ月の主がPicoCTFのEasyをbeginnerむけに書いている記事です。CTFのwriteupではCTF常識が前提になっている記事が多く無知すぎてよくわからなかったので書くことにしました。至らぬところ等あると思いますのでご指摘いただけると嬉しいです。
#picoCTFbeginner


<h3>必要ツール</h3>
CTF界で常識的に行われていること。ツールを紹介します。<br>

<h4><a href="https://gchq.github.io/CyberChef/">デコードエンコードツール</a></h4>
CTF界では怪しい文字列を発見するとその文字列をデコードすることがあります。
例えばbase64(64種類の印字可能な英数字のみデータ表記方法)からUTF-8(8バイトで表現される一般的な文字列表記方法)への変更が行われる。

![スクリーンショット 2024-08-06 143032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1972935/1935ee97-4347-badb-1f4a-9efdbde2aebb.png)


<h4><a href="https://portswigger.net/burp/communitydownload">Burp Suite</a></h4>
webサイトの脆弱性を見つけるためのアプリケーション。Proxy機能があり、HTTPS通信の内部をみることができる。

![ダウンロード (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1972935/885aac64-aa3a-661a-66ea-589964e6c166.png)

[参考]Download方法

https://www.leon-tec.co.jp/blog/10174/#first






<h3>終わりに</h3>
この記事には随時ツールを追加していきます。
もしCTFで使える便利ツール等ご存じでしたらご教授いただければ幸いです。
React × Firebase】感情共有SNS「MoodBoard 2.0」を作ってみた！

## 初めに
こんにちは！24卒の駆け出しエンジニアです。大手ECサイトの保守・運用をメインに行う会社で、社内SEとして働いています。最近はブリッジSEとして中規模ECサイトの案件を中国側とやり取りする業務も担当しています。そんな私が、仕事の合間や帰宅後の時間を使って作った「MoodBoard 2.0」というサービスを紹介します！

## MoodBoard 2.0とは？
MoodBoard 2.0は、感情を共有するSNSです。ユーザーは自分の気分や感情を選択し、短い投稿と共に共有することができます。他のユーザーの投稿に「いいね」をしたり、コメントを残したりすることもできます。
サービスURL: https://moodboard-2.web.app/

## なぜ作ったの？

「身内で使える掲示板みたいなの作ってみたいなー」という軽いノリから始まりました（笑）
映画「インサイド・ヘッド」を見て感動し、感情を可視化することの面白さに惹かれたからです。
React、Firebase、その他モダンな技術を実践的に学びたかったからです。

## 技術スタック

フロントエンド: React、React Router、styled-components
バックエンド: Firebase (Firestore, Authentication)
状態管理: React Query
デプロイ: Firebase Hosting

## 主な機能と仕様

ユーザー認証（サインアップ、ログイン、ログアウト）
感情付きの投稿作成
タイムライン表示（無限スクロール）
いいね機能
コメント機能
ユーザープロフィール
投稿の編集・削除
通知機能
フレンド機能

## データフロー

ユーザーが投稿を作成すると、Firestoreに保存されます。
タイムラインはFirestoreから取得した投稿データをReact Queryを使って管理します。
いいねやコメントの操作も、Firestoreのデータを更新し、React Queryで即時反映します。
認証情報はFirebase Authenticationで管理し、Reactコンポーネントで利用します。

## つまずいたポイントと解決策
### 無限スクロールの実装
無限スクロールの実装では、以下の点で苦労しました：

・スクロール位置の検出
・新しいデータの取得とレンダリング
・パフォーマンスの最適化

解決策として、Intersection Observer APIとreact-queryのuseInfiniteQueryを組み合わせて使用しました。これにより、効率的なデータ取得と滑らかなスクロール体験を実現できました。

```
 javascriptCopyconst {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage
} = useInfiniteQuery(['posts'], fetchPosts, {
  getNextPageParam: (lastPage) => lastPage?.lastVisible || undefined,
});

useEffect(() => {
  const observer = new IntersectionObserver(
    entries => {
      if (entries[0].isIntersecting && hasNextPage) {
        fetchNextPage();
      }
    },
    { threshold: 1 }
  );

  if (observerTarget.current) {
    observer.observe(observerTarget.current);
  }

  return () => {
    if (observerTarget.current) {
      observer.unobserve(observerTarget.current);
    }
  };
}, [fetchNextPage, hasNextPage]);
```

## これからの展望
まだまだ修正が必要な箇所は多いですが、以下の点を中心に改善していく予定です：

UI/UXの改善（レスポンシブデザインの強化、アニメーションの追加）
パフォーマンスの最適化（メモ化、仮想化の導入）
テストの追加（単体テスト、統合テスト）
新機能の追加（ハッシュタグ、検索機能の強化）

## さいごに
「MoodBoard 2.0」の開発を通じて、モダンなWeb開発の技術やプラクティスを学ぶことができました。同時に、感情を共有することの面白さや難しさも実感しています。
これからも「楽しみながら学ぶ」をモットーに、新しいチャレンジを続けていきたいと思います。みなさんも、ぜひMoodBoard 2.0を使って、今の気分を共有してみてください！きっと、予想外の感情の繋がりが見つかるはずです。