OmniAICreator/Qiita-1.07M huggingface dataset:
RSpecにおけるデバッグ方法　`save_and_open_page`

## はじめに
[Everyday Rails](https://leanpub.com/everydayrailsrspec-jp)で学習中、`save_and_open_page`というメソッドが出てきました。
テストにおけるデバッグ方法をあまり知らなかったので調べてみました。

## 環境
ruby 3.2.1
Rails 7.0.4
rspec-rails 6.0.1
Capybara 3.38.0
launchy 2.5.2 
device 4.9.0 

## save_and_open_pageとは
これはCapybaraのメソッドで、任意の箇所に`save_and_open_page`と記述することでページのスクリーンショットを撮り、ブラウザで確認できます。
テストに失敗した時、どの部分に問題があるのかを検証する事ができます。

## 使い方
例えば、ホーム画面ヘアクセスするにはログインが必要だとします。
```ruby:app/controllers/home_controller.rb
class HomeController < ApplicationController

  before_action :authenticate_user!

  def home
  end
end
```
うっかりログインせずにhomeページへ遷移するテストをしたとします。
```ruby:spec/system/homes_spec.rb
require 'rails_helper'

RSpec.describe "Homes", type: :system do
  before do
    driven_by(:rack_test)
  end

  describe "#index" do
    context "ログイン済みユーザーの場合" do
      before do
        @user = FactoryBot.create(:user)
      end
      it "homeページを表示できる" do
        visit root_path
        expect(page).to have_current_path root_path
      end
    end
  end
end
```
しかしいけません。ログインしていないのでエラーが発生しました。
```:エラー
Homes
  #index
    ログイン済みユーザーの場合
      homeページを表示できる (FAILED - 1)

Failures:

  1) Homes #index ログイン済みユーザー homeページを表示できる
     Failure/Error: expect(page).to have_current_path root_path
       expected "/users/sign_in" to equal "/"
     # ./spec/system/homes_spec.rb:15:in `block (4 levels) in <top (required)>'

Finished in 0.36835 seconds (files took 1.81 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/system/homes_spec.rb:13 # Homes #index ログイン済みユーザー homeページを表示できる
```
そんなときは`save_and_open_page`メソッドでデバッグです。
```ruby:spec/system/homes_spec.rb
# RSpec.describeブロックは省略...
  describe "#index" do
    context "ログイン済みユーザーの場合" do
      before do
        @user = FactoryBot.create(:user)
      end
      it "homeページを表示できる" do
        visit root_path
        save_and_open_page #追記
        expect(page).to have_current_path root_path
      end
    end
  end
```
テストを実行するとエラー分が変わり、スクリーンショットのパスが表示されます。
```:エラー
Homes
  #index
    ログイン済みユーザー
File saved to /Users/name/tutorial/environment/rspectutorial   \
/tmp/capybara/capybara-202303152034186588149655.html.
Please install the launchy gem to open the file automatically.
      homeページを表示できる (FAILED - 1)

Failures:
#以下省略...
```
パスをブラウザで開くと、homeページではなくログイン画面が表示されています。
このことから、ユーザーのログインを忘れていた事がわかります。
![スクリーンショット 2023-03-15 20.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071346/1fa7a484-58e0-1ac8-e653-e1db977b528c.png)

しかし、毎回パスをブラウザに貼り付けて...とするのは少々面倒くさいです。
そんなときは`launchy`というgemを使用すると、自動でブラウザを開いてくれるようになります。
### launchy 
```ruby:Gemfile
gem "launchy"
```

## 参考サイト
+ [Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)
+ [Method: Capybara::Session#save_and_open_page](https://www.rubydoc.info/github/jnicklas/capybara/Capybara/Session:save_and_open_page)
Java_入力の配列による保持 (paizaランク D 相当)

paizaラーニングforTEAM Java入門編
Cランクレベルアップメニュー

```Java:C-#04
// 整数nとn個の数a_1,...,a_nが改行区切りで入力されたとき、与えられたa_1, ..., a_nの中で最も大きい数を出力するプログラム
import java.util.*;
public class Main {
    public static void main(String[] args) {
        // 自分の得意な言語で
        // Let's チャレンジ！！
        Scanner sc = new Scanner(System.in);
        int line = sc.nextInt();
        int s = 0;
        
        for (int i = 0; i < line; i++) {
            int line2 = sc.nextInt();
            // System.out.println(line2);
            
            if (s < line2) {
                s = line2;
            }
        }
        System.out.println(s);
    }
}
【AWS】IAMユーザーの作成(ユーザー作成~MFA設定)

# IAMユーザーを作成
AWSアカウント登録直後、ログインする際に使用されているユーザー(メールアドレス + PW でログインするユーザー)は`ルートユーザー`です。
この`ルートユーザー`は、全AWSのサービスやリソースに対してアクセスする事が可能です。日常的な作業では、`ルートユーザー`を使用する事は推奨されません。
`IAMユーザー`を作成して、定められたポリシー内で作業を行う事が推奨されています。

日常的な作業を行う為に、`IAMユーザー`を作成していこうと思います。

まずは、`ルートユーザー`でログインします。
ログイン後に、Management Consoleの検索窓に`IAM`と入力します。
サービスの`IAM`をクリックすると、IAMダッシュボードに切り替わります。
※GUIで見る画面はアップデートで変更される事があるので、参考程度となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/811fd07e-ba70-444a-1725-3b80fdc2fcc8.png)

アクセス管理欄からユーザーを選択し、「ユーザーを追加」をクリックします。
クリックした後に、登録する際に必要な情報を設定する必要があります。

Management Consoleにログインして作業するユーザーであれば、「AWSマネジメントコンソールへのユーザーアクセスを提供する」という項目にチェックを入れてください。
また、「IAMユーザーを作成します。」にチェックを入れます。(Identity Centerが推奨されているようですが、使った事がないので、、今回はIAMユーザーを作成する事を目標にしました。)

「ユーザーは次回のサインイン時に新しいパスワードを作成する必要があります」という項目については、今回はチェックはしていません。
`IAMユーザー`作成後に、ユーザー毎にパスワード設定を行ってほしい等がある場合はチェックをつけましょう。諸々設定したら、「次へ」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/a728dc59-0fce-20e7-0dac-88380d6025f1.png)

次に、ポリシーを設定します。
今回は、管理者用のIAMユーザーを作成します。「AdministratorAccess」をチェックして、「次へ」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/08258268-d824-6d4a-eda5-cc0fc54a9092.png)

設定した内容についてを確認する画面が表示されるので、問題がない場合は「ユーザーの作成」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/92703f67-3e43-f79d-347d-2fc066249b8a.png)

作成後に下記情報が出力されると思います。これらは、今後ログインする際に必要となります。

【ログイン時に必要な情報】
・コンソールサインインURL
・ユーザー名
・コンソールパスワード

「コンソールサインインURL」にアクセスして、ログインが行える事を確認しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/da3bfc7e-1e0e-b79f-85bf-244f5416b77b.png)


# MFA反映
セキュリティを強化するために、MFAを反映させます。
Management Consoleのアカウント名から「セキュリティ認証情報」をクリックすると、自分の認証情報が開かれます。
「MFAデバイスの割り当て」が下にスクロールしていくと見つかると思いますので、クリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/c6f1229a-4bc3-4005-ce88-b6e9c78e9ed2.png)

MFAデバイスは、今回はアプリで対応する事とします。
「認証アプリケーション」を選択して、「次へ」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/b98ec641-6882-02bd-eb1b-eea3cf6dd338.png)

認証用のアプリに、「Authenticator」をインストールします。
インストール後に「Authenticator」でQRコードの読み込みを行うと、コードが発行されるようになります。

「MFAコード1」と「MFAコード2」に発行されたコードを入力します。
※コードは2つ必要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/874e4c1e-249f-ecba-0b21-cbba21cfc8cd.png)

割り当てに成功したら、下記のようなメッセージが出力されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/2befad8f-42b4-260f-d281-c2e11d208de3.png)

これで次回のログイン以降は、MFA認証を求められるようになります。
「Authenticator」で発行されたコードを、入力するとログインする事が出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710307/e6f0f735-7583-e6b9-ac52-0b916f3baa1d.png)

以上となります。
Ruby 引数を使ってプログラムを作成する

# 引数を使ってプログラムを作ろう
< 問題 >
ユーザーが数字を2つ渡すと、それらを掛け算した結果を返すプログラムを作ってください。
2つの数字を、それぞれnum1, num2という変数にgetsメソッドを利用して定義してください。

```ruby
# 自分の回答
def two_nums(num1, num2)
  result = num1 * num2
  puts "#{num1}と#{num2}をかけた答えは#{result}です"
end


puts "最初の数字を入力してください"
num1 = gets.to_i
puts "2番目の数字を入力してください"
num2 = gets.to_i

two_nums(num1, num2)
```

```ruby
# 模範回答
def multiplication(num1, num2)
  puts "#{num1}と#{num2}をかけた答えは#{num1 * num2}です！"
end

puts "最初の数字を入力してください"

num1 = gets.to_i

puts "2番目の数字を入力してください"

num2 = gets.to_i

multiplication(num1, num2)
```

## 解説
メソッドには複数の引数を渡すことができます。
複数の引数を渡した場合、引数を受け取る方は、メソッドの呼び出し順で引数を順番に代入されます。
Arduino Opta 分解してみた

Arduino Optaがついに発売されたのだが、運よくRS-485モデル(AFX00001)が売り切れる前に注文できました。

https://www.arduino.cc/pro/hardware-arduino-opta

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/fae5878d-12ee-da10-1f47-d1cfc3c80aee.png)


しかし現時点ではArduino PLC IDEはOPTAには対応していない。(2023/3/23対応しました)
ということでネタがないので分解してみました。

パッケージ内容はこんな感じ
![FrJJJtLaIAA_Jbe.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/bd9b78ad-a472-85ed-5541-201b8322bb84.jpeg)

大きさの比較。結構小さいですね。
![FrJJYqNaMAA5mTc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/ac15021d-e211-8770-10ca-86c80fba0e30.jpeg)

基板は3枚構造です
![Z6N_2403.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/87f7b4b5-2d60-a993-f3fa-b0e69900c4ac.jpeg)

筐体
ツメで固定されており、傷がつくので分解はおすすめできません。
何回か開けたので筐体はボロボロになり、今はテープで蓋をしています…
またLANコネクタと筐体部分がホットボンドのようなもので固定されています。（抜き差しの強度対策？）
![Z6N_2407.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/db931529-99e2-6b8e-3cf9-94b0d8c205aa.jpeg)

下基板:出力リレーと入力端子と電源回路
・DC/DC Converter:AP64202
・RELAY:43.41.7.005.0300 コイル5V N.O. 接点10A 250V
![Z6N_2357.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/794659cd-2a2a-3fe9-da25-00c82b251ba3.jpeg)
![Z6N_2359.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/989f9a0a-0592-6d42-3989-4a209fb8c76c.jpeg)

中基板:メイン基板
・CPU:STMicroelectronics STM32H747XIH6
・LAN:SMSC LAN8740AI
・FLASH:Adesto Technologies AT25SF128A (NOR Memory IC 128Mbit)
・383RNT0←不明
・TH2318T5←不明
・C17J←不明

![Z6N_2365.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/961b9cc5-0b1e-10ab-0b9e-bb96f615ca94.jpeg)
・RS-485:SIPEX SP485EM
![Z6N_2378.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/8a4a49cf-b2e7-9232-0d0f-c3a8e4a7ceb1.jpeg)

上基板:SWとLED、WIFIモデルはここにWIFIチップがつくと思われる
![Z6N_2380.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/84ed1c4f-5418-8541-663f-1f59d03f5825.jpeg)
![Z6N_2384.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/8f1bc180-c024-09f2-f019-e39002d40a04.jpeg)

I/O図面が公開されましたが、LED_USERの部分にLEDは実装されていない…ミスか？
(マニュアルに記載ありましたがLED_USERはWIFIモデルにしか無いようです…)

https://content.arduino.cc/assets/AFX00001-pinout.pdf

https://content.arduino.cc/assets/AFX00003-pinout.pdf

https://github.com/arduino/ArduinoCore-mbed/blob/master/variants/OPTA/pins_arduino.h


さて何に使おうか…
Java_複数行にわたる出力２ (paizaランク D 相当)

paizaラーニングforTEAM Java入門編
Cランクレベルアップメニュー

```Java:C-#03
// 整数nとn個の整数a_1,...,a_nが改行区切りで入力されたとき、a_1, ..., a_n を与えられた順に、改行区切りで出力するプログラム
import java.util.*;
public class Main {
    public static void main(String[] args) {
        // 自分の得意な言語で
        // Let's チャレンジ！！
        Scanner sc = new Scanner(System.in);
        int line = sc.nextInt();
        
        for (int i = 0; i < line; i++) {
            int line2 = sc.nextInt();
            System.out.println(line2);
        }
    }
}
Azure OpenAI ServiceでもChatGPTが使えるようになったぞ

:::note warn
以降の内容は2023/3/15時点の情報で作成しています。
Azure OpenAI Serviceはプレビュー中 (限定的アクセス)のサービスであるため、将来的に動作や仕様が変更される可能性があります。
:::

## Azure OpenAI Serviceとは
本人に聞いてみた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/5b63406e-34f5-ed05-7872-fc2276292ad1.png)

本家Open AIと比較すると主に
- Azureのロールベースアクセス制御（RBAC）が適用可能
- アクセス元のネットワークを制限したり、プライベートエンドポイントでvNetと統合が可能
といった特徴があります。

ビジネスアプリケーションに組み込んだりする際に必要な要件が網羅されている印象です。

2023/3/5に追加機能として、ChatGPTがAzure OpenAI Service内でも利用できるようになりました。
本記事ではチュートリアルをなぞっていきます。

https://news.microsoft.com/ja-jp/2023/03/10/230310-chatgpt-is-now-available-in-azure-openai-service/

## 利用を開始する
利用を開始するにはAzureのサブスクリプションを準備した上で、利用申請を行う必要があります。
- https://aka.ms/oai/access

申請が承認されると結果がメールで届き、Azure Portal から Azure OpenAI Service のリソースが作成可能になります。

## リソースの作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/259f3269-f76f-510b-cb03-fd16ce3a69ed.png)

リソースが作成されたらStudioに移動しましょう
- 最近のAzureサービスは別のポータル？スタジオ？みたいなやつに飛ばしがち
- Azure portal側ではAPIを利用する際のアクセスキー管理やネットワーク回りが設定できます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/13c60d96-e670-e841-f41d-b0faee5778d0.png)



## モデルのデプロイ
ChatGPTを使うには以下の手順でモデルをデプロイします。

https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-studio

ChatGPTを使うには「gpt-35-turbo (version 0301)」のモデルを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/060bb63e-18cd-4c05-80df-44f723d9946d.png)

これでChatGPTを使う準備は完了です！

早速話しかけてみましょう
（基本情報技術者試験の過去問を出題すると見事正解）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/f6ceffd3-0b21-9b86-21cb-ff095d96850e.png)

パラメーターを設定できるUIもついていて便利そうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206219/b13f40b5-20b9-8382-4aef-fda7bdf9e033.png)

## 補足 データのプライバシーについて
データの取り扱いについては以下のドキュメントに書かれています。
[Data, privacy, and security for Azure OpenAI Service](https://zenn.dev/microsoft/articles/59448047cd5ed9#%E5%88%A9%E7%94%A8%E7%94%B3%E8%AB%8B:~:text=and%20security%20for-,Azure%20OpenAI%20Service,-Training%20data%20provided)

> お客様から提供されたトレーニング データは、お客様のモデルを微調整するためにのみ使用され、Microsoft が Microsoft モデルをトレーニングまたは改善するために使用することはありません。

この辺りが[本家Open AI](https://openai.com/policies/terms-of-use)との使い分けポイントになると思います。
ubuntu22.04 LTSでwifi設定をCLIで行う

wifiのパスワードを暗号化する

    $ sudo wpa_passphrase SSID PASSWORD
    network={
            ssid="SSID"
            #psk="PASSWORD"
            psk=hogehogefugafugafoofoovarvar

netplanの設定

    $ sudo vi /etc/netplan/50-cloud-init.yaml
    -----
    network:
    ethernets:
        eth0:
            dhcp4: true
            optional: true
    version: 2
    #####ここから追記箇所#####
    wifis:
        wlan0:
            optional: true
            access-points:
                "SSID":
                    hidden: true　#SSIDステルスの場合必要
                    password: "hogehogefugafugafoofoovarvar"
            dhcp4: true

設定反映

    $ sudo netplan apply

timezoneの変更

    $ date
    $ sudo timedatectl set-timezone Asia/Tokyo
    $ date

以上。
Java_複数行にわたる出力１ (paizaランク D 相当)

paizaラーニングforTEAM Java入門編
Cランクレベルアップメニュー

```Java:C-#02
// 整数nが入力されたとき、n回、改行区切りでpaizaと出力するプログラム
import java.util.*;
public class Main {
    public static void main(String[] args) {
        // 自分の得意な言語で
        // Let's チャレンジ！！
        Scanner sc = new Scanner(System.in);
        int line = sc.nextInt();
        
        for (int i = 0; i < line; i++) {
            System.out.println("paiza");
        }
    }
}
Confluenceで定例ページを毎週自動でコピーする

# 概要
毎週の定例ページをコピーして作るのめんどいので自動化する

# 図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257093/c855ba0e-52e1-d356-bc90-4d07b633191d.png)


# 処理の流れ

- Amazon Event Bridge
    - 日時を決めてLambdaを実行する
- Lambda
    - コピー元のページの特定(毎回同じテンプレページをコピーするなら不要)
        - 親ページのIDを指定して子ページ一覧取得APIを実行
        - タイトルで昇順にして一番最後のページが先週のページ
            - 定例ページの命名規則による
        - 先週のページのIDを特定する
    - コピーして新ページ作成
        - 先週のページのIDとどこのページの配下に作るかを指定してコピーする

# コード

```ruby
# frozen_string_literal: true

require 'faraday'
require 'json'


DOMAIN = 'https://buysell-tech.atlassian.net'
PARENT_PAGE_ID = '12345'
# https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-content---children-and-descendants/#api-wiki-rest-api-content-id-descendant-get
DESCENDANT_INDEX_PATH = "/wiki/rest/api/content/#{PARENT_PAGE_ID}/descendant/page"

def lambda_handler(event:, context:)
  descendant_response = faraday(DOMAIN).get(DESCENDANT_INDEX_PATH) do |request|
    request.headers['Content-type'] = 'application/json'
    request.headers['Accept'] = 'application/json;charset=UTF-8'
  end

  descendant_results = JSON.parse(descendant_response.body)['results']
  last_week_page_id = descendant_results.sort_by { |page| page['title'] }.last['id']

  # https://developer.atlassian.com/cloud/confluence/rest/v1/api-group-content---children-and-descendants/#api-wiki-rest-api-content-id-copy-post
  copy_url_path = "/wiki/rest/api/content/#{last_week_page_id}/copy"

  response = faraday(DOMAIN).post(copy_url_path) do |request|
    request.body = {
      copyAttachments: true,
      copyPermissions: true,
      copyProperties: true,
      copyLabels: true,
      copyCustomContents: true,
      destination: {
        type: 'parent_page',
        value: PARENT_PAGE_ID
      },
      pageTitle: "#{(Date.today + 7).to_s}定例"
    }.to_json

    request.headers['Content-type'] = 'application/json'
    request.headers['Accept'] = 'application/json;charset=UTF-8'
  end

  log = Logger.new(STDOUT)
  log.info("response | status_code: #{response.status}, body: #{response.body}")
end

def faraday(service_url)
  Faraday.new(url: service_url) do |builder|
    builder.request :json
    builder.request :authorization, :basic, 'test@example.com', '<API TOKEN>'

    builder.options[:open_timeout] = 30
    builder.options[:timeout] = 120
  end
end
```
Java_単純な入出力 (paizaランク D 相当)

paizaラーニングforTEAM Java入門編
Cランクレベルアップメニュー

```Java:C-#01
// 入力された文字列をそのまま出力するプログラム
import java.util.*;
public class Main {
    public static void main(String[] args) {
        // 自分の得意な言語で
        // Let's チャレンジ！！
        Scanner sc = new Scanner(System.in);
        String line = sc.nextLine();
        System.out.println(line);
    }
}
Dockerのコンテナでsudoできない場合の対処法

Dockerコンテナ内で`sudo ...`と叩いても`bash: sudo: command not found`と出てきた。
イメージにユーザーを追加するほどでもなく、ちょっとsudoコマンドで作業したい場合に超有効。
## docker-compose.ymlの場合
```docker-compose.yml
docker-compose exec -u 0 コンテナ名 bash
```
## Dockerfileの場合
```Dockerfile.
docker exec -it -u 0 コンテナ名
```

これを覚えておくとかなり融通が効くかと思います^^

参考：[Docker：sudoできないDockerコンテナを、rootでシェル操作するには](https://qiita.com/tabimoba/items/c5467432d1a635f9ce5b)
ubuntu grub 覚書

# はじめに

grubで、前回起動で起動の設定をする方法を毎回忘れてしまうので記載しておく。

```
$ sudo vim /etc/default/grub
```

```/etc/default/grub
GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true
```

2行目の追加を忘れないこと。

```
$ sudo update-grub
```

でよい。
Azure Pipelineでコミットされたブランチごとに実行するpoolを切り替える

コミットされたブランチを`Build.SourceBranchName`で取得し、pool名を変数にセットすればOK

```yaml
trigger:
- main
- staging

variables:
  - name: pool-name
    ${{ if eq(variables['Build.SourceBranchName'], 'main') }}:
      value: 'ubuntu-latest'
    ${{ if eq(variables['Build.SourceBranchName'], 'staging') }}:
      value: 'windows-latest'

pool:
  vmImage: $(pool-name)

steps:
- script: echo Hello, main!
  displayName: 'Run a one-line script'
```

環境ごとに閉域のパイプラインでそれぞれのセルフホストランナー使わないといけないケースで使えるかもしれません。

## 疑問
条件分岐しているロジックはpoolではなくどこで動いている？
ChatGPTに作曲をやらせる

# 概要
- MIDIを扱うコードを書いてもらうことでChatGPTに直接「作曲」をやってもらおう
- PythonでMIDIを扱えるライブラリを渡せばいいんじゃないか
- とりあえずPygameを渡してみたら，UIも音楽も割と自由自在に作ってくれた
- 一応これはGPT-3台のもので動かしている(貧民)

# 結果
https://github.com/Hurumy/generating_songs_with_ChatGPT3

# メモ
- 同じ方法がGPT-4で使えないわけがない
- 直接「compose」してくれ，というように言うと，なぜか「Pygameではできない(嘘)．midoとrtmidiを使って勝手にやるね」というような反応をされるときがあった
- 返答の行数？文字数？制限がどうしてもネックであるが，「前のあなたのコードを踏まえて続きを書け」というように言うと結構うまく続きを出してくれる．
- BPMとか，ジャンルとか，楽器の数・種類，MSGSを使え，などということを指定するとうまくいった気がする．
- 今のところクオリティはフラクタルから生成した音楽と大差ないが，もっと細かく指定してやったり，コンポーネントに分けて出力させたらずっと良くなるんではないかと思う．
- 音楽生成専門職AIの方がずっとクオリティは出るんだろうが，まあそういう実験でした．逆に言えば似たような発想で，一回コードを通せばじじつじょうなんでも出せるのでは？
WSLやChromeBook上でも書くなら、VS CodeでMarkdown書いてGoogle Driveに保存しとけばいいんじゃん？　という記事

毎日のようにMarkdownを書き散らかしています。  
一時期仕事で使ってたTeXに比べ、  
比較にならないくらいにライトに使えて、  
Google documentやWordに比べても  
気軽に書けるってのはいいもんだなぁ、なんて感じてます。  
問題は書き散らかしたファイルを  
どこにやったのかを完全に失念し  
探すのに時間がかかるなんてこともあり、  
ドキュメント関係をどっかに集約しよう、  
というのを数年やった上で、  
ひとまずの落とし所っぽい感じをつかんだので共有します。

## 概要
MarkdownのファイルはGoogle drive上に置いといて、  
WSLのVS Codeから編集できりゃよさげ。

ファイル同期の速度ではDropbox一強だが、  
ChromeBook上だと選択肢も少ないし。

## 前提
家でも仕事でも、作業のメモ書きMarkdownがほとんど

### 環境
WSL2上かChromeBook上のVS Codeを主に使用

#### WSL2
```
$ cat /proc/version
Linux version 5.15.90.1-microsoft-standard-WSL2 (oe-user@oe-host) (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220) #1 SMP Fri Jan 27 02:56:13 UTC 2023
```

#### VS code at WSL2
```
$ code --v
1.76.0
92da9481c0904c6adfe372c12da3b7748d74bdcb
x64
```

#### Google Drive
パソコン版ドライブをダウンロードし
@gmail.comアカウントにて運用

パソコン版ドライブをインストールし、  
仮想ドライブを初期設定のGドライブで運用。

## 手順
### GoogleDrive上にフォルダの作成
書き散らかすフォルダをGoogleDrive上に作る
ここでは、`files`として作成。

### シンボリックリンクの作成
#### WSL
WSL上にリンクを作成
```
$ cd ~/
$ ln -s /mnt/g/マイドライブ/files/
```
上記の二行のコマンドで、カレントディレクトリに`files`というシンボリックリンクが作成される。

#### CharomeBook
```
$ cd ~/
$ ln -s /mnt/chromeos/GoogleDrive/MyDrive/files/
```

以上で準備完了。

## 使い方
WLSとChromeBookで共通。
```
code ~/files/
```
以上で、書き散らかしたファイルが転がってるフォルダが開かれ、VS Code上で一覧が見えたりします。

## Tips
### Googleドライブの設定
'files'フォルダはパソコン版ドライブで**オフラインで使用可能にする**にしておくとWSLとの相性が良い。
※こうしておかないと、ローカルにファイルを置いといてくれない

### ChromeBook上でのVS Codeの日本語入力
ChromeBookのターミナル上からVS Codeを起動させると  
Linuxアプリとして動くために標準環境では日本語入力できない。
ソースを書いてるときはいいけれども、  
解説なんかを書くときはでは日本語が入力できないと不便なので、
そういうときは[vscode.dev](https://vscode.dev/)でブラウザー版VS Codeを立ち上げて、
そこから“フォルダを開く”を選択し、
ローカルの'files'を開くといいかも。
> ※Linuxアプリでも日本語入力できる方法はあるけど、
　今回の話では冗長なため割愛
プロジェクト・パフォーマンス領域

# 初めに
- プロジェクト・マネジメントを初めて学習するにあたって、以下の方法で身につけようとしております。
    1. PMBOK（第７版）で知識を身につける。
    2. PMBOK（第７版）で身につけた知識を実践で活用する。
    3. 活用した事例をまとめる。
- まずは、PMBOKを通して学んだことをこちらにまとめていこうと思います。
- わかりにくい点、疑問点などありましたら随時コメントいただけると幸いです。
- コメントを通して、さらに知識を深堀できると考えております。

※本記事はPMBOKガイド第7版を元にまとめております。

# プロジェクト・パフォーマンス領域
## 1.ステークホルダー
https://qiita.com/ryota_haga/items/b8d6e8c0919d5c45c10c
## 2.チーム
https://qiita.com/ryota_haga/items/c6d5876b461f760e0d11
## 3.開発アプローチとライフサイクル
https://qiita.com/ryota_haga/items/345595cb69ef196ffda2
## 4.計画
https://qiita.com/ryota_haga/items/a408b24c5dc2bfaa2a50
## 5.プロジェクト作業
https://qiita.com/ryota_haga/items/bec958cc55f3c6b04406
## 6.デリバリー
https://qiita.com/ryota_haga/items/0c5574e2f1c5e4960a41
## 7.不確かさ
〜作成中〜

# 引用
- [プロジェクトマネジメント知識体系ガイド（PMBOKガイド）第7版＋プロジェクトマネジメント標準](https://www.pmi-japan.shop/shopdetail/000000000028/)
pytorchで書かれたモデルのパラメータ数を数える


## パラメータの数を数える
OpenNMTでtrain.pyしたときにパラメータ数が出てきたのがなんかカッコよかったので自分もやってみる

```python
import torch.nn as nn
import torch.nn.functional as F

# テキトーなモデルを作る
class SampleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
        self.fc1 = nn.Linear(16 * 8 * 8, 100)
        self.fc2 = nn.Linear(100, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2)
        x = x.view(-1, 16 * 8 * 8)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# インスタンス作る
model = SampleModel()

# パラメータ数を数える関数
def count_parameters(model):
    return sum(param.numel() for param in model.parameters() if param.requires_grad)

# 表示
print(f"Total trainable parameters: {count_parameters(model)}")

```

これで学習可能なパラメータ（requires_grad=Trueになっているパラメータ）を数えてくれます。

ちな実行結果は以下
```
Total trainable parameters: 103958
```
pandocを使用して、特定のディレクトリ内のmarkdownをdocxに変換するスクリプト書いてみた

## はじめに

現在所属しているチームで、今までGitHub上でMarkdownで管理していたドキュメントを、Googleドライブ上に移行することになりました。移行の作業としては以下のような感じでした。
1. リポジトリをclone
2. pandocを用いてひとつずつdocxに変換　**←ここがめんどくさかった**

したがって、ディレクトリ内のmarkdownファイルをdocxに変換するシェルスクリプト書きました。ちなみに、私はシェルスクリプト初心者なので、もっと改善できる点あれば教えて頂けると幸いです 🙏

## 完成したスクリプト

```bash
#!/bin/sh
path=$1
files=`find $path -type f -name \*.md`
mkdir -p output
echo "$path 内のmarkdownファイルをdocxに変換します..."
for file in $files;
do
    filename=`echo $file | sed 's/\//-/' | sed 's/.md//'`
    echo "$filename を変換..."
    pandoc $file -s -o "output/$filename-converted.docx"
done
```

## 詰まったところ

### `/` の置換
- ファイル名に `/` が含まれているとディレクトリとして扱ってしてしまうので、置換したかった
- `sed` コマンドを使って置換した
    - 区切り文字も `/` だったため、見づらくてtypo多発してしまった

### コマンドライン引数を受け取る方法
- `$1`のように `$取得したい引数の番号` のようにすればコマンドライン引数を受け取れるの知らなかったです…
プリクラをAIで高画質化

# 加工を重ねて低画質になった写真をくっきりさせる

ぼやけがちなプリ画像。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/294b40bb-c031-06c9-8db2-6b1019c79a85.jpeg width=400>

加工を重ねた写真ってぼやけがちですよね。

**AIが解決してくれます。しかも使うの簡単。**

# やり方

以下のAIサンプルのWebページにアクセスして、画像を選択し、送信を押すだけです。

https://huggingface.co/spaces/doevent/Face-Real-ESRGAN

![スクリーンショット 2023-03-15 19.22.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/017ca9fc-b819-2619-56ec-e3d3fd1dbf5d.png)

＜元画像↓＞
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/294b40bb-c031-06c9-8db2-6b1019c79a85.jpeg width=800>

＜AIによる高画質化↓＞
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/6b710f66-f5c8-7c71-ff90-f70151dac4e8.png width=800>

拡大するとかなりくっきりしているのがわかります。
左：元画像／右：AI
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/e3ef34cd-8c01-b6d8-26d5-379929c66c59.png width=256><img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/2825f26d-617f-1d8b-8041-52b2f177d0af.png width=256>

# プログラムで行う方法
画質をアップするAIをプログラムで使ってサービスに組み込むこともできます。
RealESRGANというAIを使っています。

https://github.com/xinntao/Real-ESRGAN

使い方は簡単で、以下のコマンドを実行するだけで指定した画像を高画質化してくれます。

```shell
python inference_realesrgan.py -n RealESRGAN_x4plus -i input_image -o output_image
```

ちなみに、僕はこのAIをアプリ化しました。

https://apps.apple.com/jp/app/super2x/id1609689545

🐣
****
フリーランスエンジニアです。
もし以下のようなご要望をお持ちでしたらお気軽にご相談ください。
AIサービスを開発したい、ビジネスにAIを組み込んで効率化したい、AIを使ったスマホアプリを開発したい、
ARを使ったアプリケーションを作りたい、スマホアプリを作りたいけどどこに相談したらいいかわからない…

いずれも中間コストを省いたリーズナブルな価格でお請けできます。

お仕事のご相談はこちらまで
rockyshikoku@gmail.com

機械学習やAR技術を使ったアプリケーションを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)
【SwiftUI】セーフエリアだけに色をつける

# はじめに
セーフエリアにだけ色を付けたい事があり、やり方がわからなかったので記録しておきます

# やりかた
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            VStack {
                Color.red
                    .frame(height: proxy.safeAreaInsets.top)

                Spacer()

                Color.red
                    .frame(height: proxy.safeAreaInsets.bottom)
            }
            .edgesIgnoringSafeArea(.all)
        }
    }
}
```
![スクリーンショット 2023-03-15 19.21.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/82e01ee7-ad18-09f6-c007-2f037bdf19e0.png)

# おわり
案外簡単でした
OpenAIのWhisperAPIを利用しようとしたところ、パラメータの設定がうまくいかなかった話

### 概要
Node.jsでwhisperを利用しようとしたところ、パラメーターの設定がうまくいかずエラーになってしまった。
[APIリファレンス](https://platform.openai.com/docs/api-reference/audio)にも説明がなく、困っていたがいろいろ調べた結果答えを知ることができたため、共有したい。
### 書き方
```js
const { Configuration, OpenAIApi } = require("openai");
const fs = require("fs");
const configuration = new Configuration({
  apiKey: "YOUR_API_KEY",
});
const openai = new OpenAIApi(configuration);

async function main() {
  const resp = await openai.createTranscription(
    fs.createReadStream("sample.mp3"),
    "whisper-1",
    undefined,
    "text"
  );
  console.log(resp.data);
}

main();
```

### まとめ
いわゆるObject形式による入力やpromptを飛ばした状態でファイル形式の設定を行うと、エラーになるようだった。[APIリファレンス](https://platform.openai.com/docs/api-reference/audio)にデフォルト値がないこともあり、気が付くのが難しかった。


https://techpro.ninja/whisper-openai-tutorial-with-python-nodejs/

pythonなどの場合はこちらを参考にするとよいだろう。
JAVA学習の記録⑤　【オーバーロード】

前回までの学び　[JAVA学習の記録④　【カプセル化】](https://qiita.com/rion0726ittoti/items/a69db102b3332884d95c)

# オーバーロードとは
javaでは、引数の型、数、並び順が異なれば、同じ名前のメソッドを定義することができ、この機能のことをオーバーロードという。
![2023-03-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3185982/b12485b4-fb4a-4cd1-12bf-57596fc587af.png)
上記のように引数が違っても、同じ目的のメソッドを１つの名前にすることができる。使うには、引数の型、数、順番を変えて同名のメソッドを定義するだけでよい。
逆にオーバーロードとして、認められないのは、
・引数の型、数、順番が同じで引数名だけを変えない場合
・戻り値の型だけを変えた場合
です。
オーバーロードの例
```java:Dog.java
public class Dog {
  private String name; 
  
  private void printMessage(String message) {
    System.out.println( message );
  }
  
  public void eat() {
    printMessage("ご飯を食べるよ");
  }
  
  public void eat(String food) {
    printMessage(food + "を食べるよ");
  }
  
  public void  playToy(String toy) {
    printMessage(toy + "で遊ぶよ");
  }
  
  public void playToy() {
    printMessage("おもちゃで遊ぶよ");
  }
  
}
```

```java:DogOverload.java
public class DogOverload {
  public static void main(String[] args) {
      Dog pochi = new Dog();
      pochi.eat();  //Dogクラスのeat()メソッドを呼び出す
      pochi.eat("高級なドッグフード");  //Dogクラスのeat(String food)メソッドを呼び出す
      pochi.playToy();  //DogクラスのplayToy()メソッドを呼び出す
      pochi.playToy("ボール");  //DogクラスのplayToy(String Toy)メソッドを呼び出す
  }
}
```
出力結果
```
ご飯を食べるよ
高級なドッグフードを食べるよ
おもちゃで遊ぶよ
ボールで遊ぶよ
```
オーバーロードした引数を取るメソッドと引数を取らないメソッドでeatメソッド,playToyメソッドを各２つずつ呼び出しました。

参考文献
「いちばんやさしいJAVAの教本」著者　石井真
ゲームDAOとその要素タイプ

ゲームDAOは、オープンソースコードに基づいて構築されたゲームプラットフォームとして機能し、ゲーム当局とは関係ありません。ゲームDAOは、コミュニティ主導のアプローチでコミュニティ主導の環境を確立することに重点を置いています。
 分散型自律組織 (DAO) の概念は2021年に提唱されましたが、web3ゲームへのその影響は、NFTとプレイして稼ぐモデルによって薄くなりました。今後、DAOは、ゲーム組織の運営方法、リソースのプール、持続可能なコミュニティの開発、さらには新しいゲームの開発においても重要な役割を果たすと予測されます。

 ゲームDAOは、コミュニティ主導のアプローチでコミュニティ主導の環境を確立することに重点を置いています。複数のゲームコミュニティのゲーマー、ゲーム開発者、トレーダー、および投資家のコミュニティでは、ゲームの所有権を再分配できます。
 
<h2>促進されるプレイして稼ぐゲームモデルのDAO

 P2Eモデルでは、ゲーマーのゲームでの時間と労力を報酬として補償することで、完全なゲームプレイ体験に革命をもたらしました。従来のゲームシステムでは、ゲームコントローラーや開発者への偏りがある価値交換が優勢でしたが、play-to-earnゲームシステムは、ゲームDAOによってプレイヤーに割り当てられる報酬を通じて、彼らのゲームスキルと成果に応じてゲーム経済全体を強化します。
 

<h2>要素のゲーム DAO のいくつかのタイプは次のとおりです。

<h3>開発者

開発者の DAO は、共通のプラットフォーム上でさまざまな web3 ゲームの開発に乗り出し、その主な目的の 1 つは、スマートコントラクトによってさらに監視される、そのゲームのさらなる開発の条件を設定することです。これらのゲーム DAO スマートコントラクトは、DAO 開発者がゲームの利用規約を自律的に管理できるようにするだけでなく、ゲーム コンテンツ、ゲーム内ポリシー、コミュニティ投票、マーケットプレイス オークションを明示的に管理することもできます。

<h3>ゲームギルド

前述のコミュニティ主義的アプローチは、このタイプの DAO で重要な役割を果たします。そのようなゲームギルド DAO を形成するカジュアルなプレーヤー、ゲームチーム、および非常に調整されたマルチゲームクランが存在する可能性があり、投資クラブのように金銭化されます。ゲーム DAO のメンバーは、ゲーム内資産を購入または蓄積するためにゲームギルドの資金を共有し、これらのゲーム内資産（NFT、ゲームトークンなど）をまとめて利用し、後で収益を分割することもできます。ゲーム内アセットを蓄積し、新しいゲーマー、ゲームNFTトレーダー、投機家からのサポートが拡大しているため、Web3 市場への影響は非常に大きくなっています。

<h3> インキュベーターとアクセラレーター

ゲーム DAO の 3 番目の基本的なクラスはインキュベーターとアクセラレーターであり、主に web3 ゲームインフラストラクチャの成長のために専門知識と知識をプラットフォームアーキテクトに提供することに重点を置いています。彼らは、ゲーム開発者やゲームクランをサポートするために戦略的な行動を取ることで、web3 ゲームの財務を改善します。


<h2>まとめ

ゲーム DAO はコミュニティ主導でオープンソース コードに基づいて構築されたゲーム プラットフォームです。 その焦点は、複数のゲーム コミュニティのゲーマー、開発者、トレーダー、および投資家に公正なゲーム環境を提供することにあります。 全体として、ゲーム DAO は、より分散化され、公正で、協力的なゲーム業界への一歩を表しています。

詳しく読む：
https://miichisoft.com/what-is-a-dao/

Website: https://miichisoft.com/
【メモ】「GPT-4を使う」という文脈で月額20ドルとかChatGPTとかwaiting listとかで混乱した

# 「GPT-4を使う」って言葉がわからない

「GPT-4を使う」って文脈のときに、以下のようなツイートを目にしました。

- 「月額20ドルで使い放題」
- 「waiting listに登録すればそのうち使える」
- 「ChatGPTでGPT-4が従量課金で使えるようになった」
- 「月額20ドル払えばGPT-4が無料で使えるんですか!?(支離滅裂な思考・発言)」

ライアーゲームか？？？？

# 何言ってるか整理した

「GPT-4を使う」って言ったときの選択肢は以下２つ

- ChatGPT Plus
    - ChatGPTの有料版
    - 月額20ドル
    - ただのチャット画面のみ
- API
    - waiting listに登録したらそのうち使えるようになる
    - 従業課金制
    - ちなみにGPT-3.5と比べると15 - 30倍くらいの値段になる(GPT-3.5が異様に安いだけという面もある)

これはただの宣伝ですがAPIを組み込んでチャット画面作る記事はこちら↓

https://qiita.com/tripqotnakamura/items/2807faa13480ea6c5d1f

# MicrosoftのBingは?

公式ブログ見るとGPT-4使ってるみたいですね。ただここ数日のうちは「GPT-4を使う」の文脈でMicrosoft Bingを指す人はあんまいないと思います。しらんけど。

> 新しい Bing が、マイクロソフトが検索用にカスタマイズした GPT-4 で動作していることをお知らせします。
> [新しい Bing が OpenAI の GPT-4 上で稼働](https://news.microsoft.com/ja-jp/2023/03/15/230315-confirmed-the-new-bing-runs-on-openais-gpt-4/)

# 蛇足

https://twitter.com/haruo2177/status/1635861946242318336?s=20

以上です、ありがとうございました。
テストコードを書けない人間が案件でテストコードを書き始めて分かったこと

# 概要
この記事は、テストコードを書けない人間が実際に案件でテストコードの実装を求められ、右往左往しながらテストコードを書き始めて分かったことを書き出してみたものです。
（具体的なコードや技術スタック的な話はありません）

# テストコードが書けない
自分はテストコードが苦手です。少なくとも苦手でした。
なんで自分がテストコードに対してここまで苦手意識があるのかちょっと考えてみました。

恐らく以下の2点が主な原因かなぁと思ってます。

- テストコードを書くツール（JestやTesting Library）に対する理解度が足りていない
- テストケースを洗い出せない

ツールに対する理解度が足りていないのはドキュメント読んだりすればどうにかなるお話ですが...（とはいえ、APIのモックなど難度の高いものはあると思います。ここで詰まる人も沢山いそう）

恐らくテストコード初心者が詰まる理由の1つが「<strong>テストケースを洗い出せない</strong>」という点にあるのでは、と思っています。（少なくとも自分はそうでした）

それを踏まえて、テストコードの目的や必要性を書き出してみようと思います。

# テストコードってなんで必要なんだろ
テストコードを書く主な動機としては、以下の3点が挙げられそうです。

- <strong>テスト対象機能の品質担保</strong>
- <strong>テストを自動化し、負担を減らす</strong>
- <strong>デグレ（改修による意図しない不具合の発生）の防止</strong>

作った機能は正しく動作することを確認する必要があります。
また、太古に作った機能が新たに作った機能の影響で不具合を生じてしまう、ということもありがちです。

新機能リリースのたびにこのチェック全てを人間が手作業で行うのはめちゃくちゃに大変な作業ですし（極端な話どこで不具合が発生しているのか洗い出すために、全ての機能全てのページを網羅とかいう現実的ではないことが必要になる）、その分工数も必要になってしまいます。
その上、ヒューマンエラーによって不具合を残したままリリースを行なってしまうということもありがちだと思います。

これらの負担を減らすため、<strong>テストコードを実装することでリリース前に自動的にテストを実行し、品質を担保してしまおう</strong>、というのがテストコードの主な目的だという認識です。

個人的には、「<strong>デグレの防止</strong>」がテストコードを実装する大きなモチベーションになりそう。

# テストコードに求めたいこと
そのテストコードが資産になり得るか、負債になり得るかの判断は、テストコードに求める事項を満たしているかどうかによるかと思います。

テストコードに最低限求めたい事項は主に以下の項目だと思っています。

- 機能の要件に沿ったテストコードがあること
- テストコードの内容に過不足がないこと

ここは特に初心者の方が勘違いしちゃうかもなのですが、（というか僕が勘違いしてた）

<strong>テストコードを書くこと自体が正義ではなく、要件を整理し過不足なく書かれたテストコードが正義</strong>と考えておくのが良さそうです。

要件を整理できないまま闇雲に書いたテストコードは、以下のような負債になりがちです。

- <strong>「要件を網羅している」と誤認識したまま書いた不完全なテストコード</strong>
    - 品質保証ができず、テストコードの役割を全うできない
- <strong>必要のない確認まで行おうとする膨れすぎたテストコード</strong>
    - １度のテストにかける時間が大きくなってしまう
    - 無駄なテストの実装に工数をかけてしまう

> 品質担保してリリース前のテスト作業を楽にするためにテストコードの実装に時間をかけたのに...蓋を開けてみるとその機能のテストが不完全で、結局手作業でテストをしなくちゃならない...

とか、

> 張り切ってテストケースたくさん作ったのに、大半不要だった上にめちゃくちゃ時間がかかるテストを作っちゃってた...

なんてことにならないように、その機能のテストコードを書く際には改めて要件を洗い出しておいたほうが良いと思います。

# 分かったこと
ここまでを踏まえると、「テストケースが洗い出せないからテストコードが書けない」というより、「<strong>テストを実施する機能について理解できていないから、実施したいテストの内容が分からないし、テストコードを書くモチベーションも湧かない</strong>」ということが分かりました。

ここまで書いておいて「それはそう」という終わり方で恐縮ですが...
「とりあえずテストコード書き始めよ〜！」となる前に、「この機能の振る舞いってどんなだっけ...」というところの確認からするのが大事そうです。

逆に、「その機能についてのテストケースを正しく洗い出せる」のであれば、「その機能の振る舞いや要件がちゃんと分かっている」と言えそうですね。

# おわりに

調べてみると「テスト駆動開発」なる開発手法があるようで。

https://qiita.com/yuno_miyako/items/f5cd4c376d1323d5721d

なるほど難しそうだなぁ、と思いつつ、テストケースを考える段階が設計と同じことをしていると考えるとめちゃくちゃ理に適ってるなぁと思った。もうちょいテストコードを書くのに慣れたらこれも実践してみよう。

いろいろ書いておきながらアレですが、自分はそこまでテストコードを書くモチベがない人間だな〜とこの記事を書きながら思いました。

（品質保証が大事なのは理解しているつもりですが、どちらかというと新機能をガリガリ作るほうが楽しさを覚える人間なので...）

まだまだテストについて理解できていないこととか多いので、色々知見が溜まったらまたアウトプットしたいな〜。
Googleデジタルマーケティング＆Eコマース：ソーシャルメディア・カレンダー

# はじめに

”Googleデジタルマーケティング＆Eコマース プロフェッショナル認定証"　の受講が完了しました(2022/3/3)。私がこのコースから得られた素晴らしい体験を、テーマごとに分割して、わかりやすくご紹介しています。興味を持たれた方は、是非下記を受講してみてください。

https://jp.coursera.org/professional-certificates/google-digital-marketing-ecommerce

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/24ea32fb-2fb3-bdb2-462a-4ffc4d60a96f.png" width="800">

# ソーシャルメディア・カレンダーでできること

***<font color="MediumVioletRed">『ソーシャルメディア・カレンダーは、あなたのソーシャルメディアへの投稿をすべてカレンダーにしたもの』</font>***


このツールは、さまざまな方法でソーシャルメディアの公開スケジュールを管理、整理するのに役立ちます。

- 事前に投稿のスケジュールを立てることができます。
- 時間を節約し、一貫した投稿スケジュールを維持することができます。
- 事前に投稿を計画することで、今後の取材ギャップをなくすことができます。
- 投稿のためのすべてのコンテンツを一箇所に集中させて整理することができます。
- 画像や動画、リンクなどのコンテンツアセットが正しい投稿に添付されていることを確認することもできます。
- ソーシャルメディア・カレンダーを他のステークホルダーと共有し、投稿を公開する前にレビューや承認をもらうこともできます。
- 投稿のスケジュールを前もって設定することで、お客様にとって重要な文化的、国家的イベントや祝日に合わせた計画を立てることができます。
    - 例えば「全米ヒスパニック遺産月間」「国際女性の日」「退役軍人の日」など、自社に関連すると思われるイベントに合わせて投稿を計画することができます。このようなカレンダーは、投稿における大きなミスを避けるのに役立ちます。
- コピー編集、スペルミスやタイプミスのチェック、事実確認、リンクや画像がすべて正しいかどうかを確認することができます。

# ソーシャルメディア・カレンダーの各項目

ソーシャルメディア・カレンダーの各項目には、通常、以下の要素の組み合わせが含まれます：

- 投稿を公開する日時
- 投稿のトピック
- 投稿が公開されるプラットフォーム
- 投稿文のコピー
- 写真や動画などのあらゆる資産へのリンク
- 関連文書や情報

# ソーシャルメディア・カレンダーの作成

フォーマットのテンプレートやツールを選んだら、カレンダーの作成に取りかかりましょう。

- 投稿したいソーシャルメディア・プラットフォームを特定します。オーディエンスがオンラインで最も時間を費やす場所を知ることは、プラットフォームを決定し、プラットフォーム固有のコンテンツ戦略を作成するのに役立ちます。
- 現在のソーシャルメディア・プラットフォームとコンテンツを見直し、どのような投稿が最もエンゲージメントを獲得しているかなどを評価します。
    - オーディエンスがオンラインで最も活動的なのはいつなのか？
    - 過去に最も成功したのはどのような戦術なのか？
    などを評価します。これにより、改善すべき領域と新たな機会を特定することができます。
    
- ソーシャルメディア・コンテンツ・カレンダーに追跡させたいデータを決定します。
    - プラットフォーム
    - 日付
    - 時間
    - テキスト
    - ビジュアルアセット
    - 公開された投稿へのリンク
    
    などの情報を記録するために使用することができます。収集したデータは、後で傾向を分析し、何が有効で何が有効でないかを追跡し、ソーシャルメディアキャンペーンを改良するために使用することができます。
    
- 画像、動画、オーディオファイルなど、すべてのアセット用のコンテンツライブラリーを作成します。GoogleドライブやDropboxなどのファイルストレージシステムで資産を整理し、スプレッドシートや文書にリンクを追加することができます。そして、これらのリンクを他のチームメンバーと簡単に共有し、カレンダーに挿入することができます。
- ワークフローとレビュープロセスを確立し、各プラットフォームにいつ、どれくらいの頻度で投稿するかを考えましょう。
    - 誰がコンテンツを確認し承認するのか？
    - どのように新しいコンテンツを開発するのか？

:::note info
これで投稿の作成とスケジューリングを始める準備が整いました。整理して積極的に作業計画を立てることで、キャンペーンの成果を大きく向上させることができます。そして、ソーシャル・メディアカレンダーは、これらの目標を達成するための優れたツールなのです。
:::

# Googleスプレットシートをソーシャルメディア・カレンダーとして使用する例

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/8fe21725-6e87-0e36-2642-6128b7233361.png" width="700">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/d8807476-e38d-61de-6542-c5eaa6ec6bed.png" width="700">

https://docs.google.com/spreadsheets/d/1G0f5frhIL0ugNmP30H8hrZWEiZUhGAYE5MSfsHWrXSM/template/preview#gid=0

# 参考

- [Hootsuite](https://blog.hootsuite.com/how-to-create-a-social-media-content-calendar/#How_to_create_a_social_media_calendar): How to create a social media calendar: Tips and templates
- [Asana:](https://asana.com/resources/social-media-content-calendar) Your 6 step guide to creating and managing a social media content calendar
SpreadSheet の連続する歯抜けセルを BigQuery で補完してみた

なんとなくそれっぽいのはできたのだけど、BigQuery の外部テーブルのレコード順序が元のファイルと一致する事が確認できてないので、ちょっと怪しいと思ってますが紹介してみます。

## こまったこと
画像左: 同じ会社の行という表現で会社名をセル結合する
画像右: セルは結合しないけど、空のセルはその上の空じゃないセルを表す
データとして見たときに、そのレコードだけだとどちらも会社名がわからないので困ります。
![Group 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/c08fbb1f-c847-f54d-af12-67b482c5e723.png)

## BigQuery から参照するとどうなるのか
「BigQuery だったらなんとかできるんでしょ」と思って、とりあえず外部テーブルとしてどちらも取り込んでみました。
結合したセルや空のセルは容赦なく null で扱われました。
![スクリーンショット 2023-03-15 17.51.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/4c6d8f37-a3b3-88c0-fb0a-52927bbc90b3.png)

## ごにょごにょしてみた
BigQuery でごにょごにょしているうちに、こんな感じのデータができました。
期待通りです。さすが BigQuery さん。
![スクリーンショット 2023-03-15 17.52.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/bd914b00-69af-5b10-19bf-bc8eaa546ac5.png)

SQL はこんな感じになりました。
```[SQL]
with list as (
SELECT
row_number() over () as RowNumber,
Customer,
Product,
Price
FROM `{project}.dummy.ex_missingparts_separate`
)
select
list.RowNumber,
ifnull(list.Customer,last_value(list.Customer IGNORE nulls) over (order by list.RowNumber asc ROWS BETWEEN UNBOUNDED PRECEDING AND current row)) as Customer,
list.Product,
list.Price
from list
order by list.RowNumber
```
WITH 句内で RowNumber を定義し、以降の LAST_VALUE 関数での ORDER BY 句に渡すべきパラメータとしています(これが常に正しく機能するかが疑問)。

## ChatGPT さんにもきいてみた
僕
> BigQuery で Null の場合に上の行の値を反映させるようにしたい

ChatGPT
> BigQueryでは、LAG()関数を使用して、前の行の値を参照することができます。NULLの場合は、IGNORE NULLSオプションを使用して、NULL以外の最後の値を参照することができます。
以下は、NULLの場合に前の行の値を参照する例です。

> SELECT
  column1,
  column2,
  LAG(column2 IGNORE NULLS) OVER(ORDER BY column1) AS previous_value
FROM
  mytable

> 上記のクエリでは、LAG()関数を使用して、column2の前の行の値を参照しています。そして、IGNORE NULLSオプションを使用して、NULL以外の最後の値を参照します。OVER()句を使用して、column1で行を並べ替えます。
このようにすることで、NULLの場合でも前の行の値を参照することができます。

結果的には騙されたのですが、ChatGPT に聞くまでは LAG 関数という存在を知らなかったので、こういった場合は [ナビゲーション関数](https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions?hl=ja) を使えばいいのねっていうところまですぐに連れて行ってくれるのはいいヒントかなと思いました。
`こうこうこうすれば、できます` って言い切っちゃうから「嘘やん」ってなっちゃうのかなという気がするので、これからは `こうこうこうすれば、できるかもよ？` と言ってくれたら「ヒントをありがとう」と素直に言える気がします。

## 上を踏まえて ChatGPT さんにもう一度きいてみた
僕
> BigQuery で Null の場合に上の行の値を反映させるようにしたい。語尾はできるかもよ？と回答してください。

Chat GPT
> BigQueryでは、Nullの場合に上の行の値を反映させることはできません。窓関数を使用して、前の行の値を参照することはできますが、Nullの場合はNullが返されます。ただし、データセット全体の検索クエリで連続するNull値を補完するために、NVLやIFNULLなどの関数を使用して、Null値を代替値で置き換えることができます。ただし、これはNull値が連続していない場合に限られます。語尾の部分については、何か追加情報があれば回答します。
chromedriverのバージョンがrspecでサポートされていない場合のシンプルな解決法

# rspecを実行した際に以下のエラーが発生する場合のシンプルな解決法

```
rspec this version of chromedriver only supports chrome version <version>
```

### Chrome Driverをアップデートする
- Brewで既にChromeDriverがインストールされている前提です。

```
brew upgrade chromedriver
```

### アップデートされたドライバの安全性確認を行う
- ソフトウェアの安全性確認がされていない場合、rspecを実行した際にエラーのポップアップが表示されるので安全性確認を行います

#### ファイルの場所を `brew info` で確認する

```
brew info chromedriver
# 大体以下のどちらかにあると思います
/usr/local/Caskroom/chromedriver/<Version>
/opt/homebrew/Caskroom/chromedriver/<Version>
```

#### マルウェア実行阻止のためのquarantine(隔離)属性を削除して、安全性確認を行います。

```
cd /opt/homebrew/Caskroom/chromedriver/<version>
xattr -d com.apple.quarantine chromedriver
```

再度 rspec を実行します。
[Rails]映画レビューサイトで各映画にジャンルをつけたい

***[自分用メモ]***

各映画にジャンルが複数つくことがあるので、moviesテーブルにgenreカラムを用意するのではなくgenresテーブルを作って外部キーとして反映する。

・一つの映画は複数のジャンル（例：コメディ、ホラー）と紐づく
・一つのジャンルは複数の映画と紐づく

中間テーブルを作成する必要がある。

# 必要なもの
・moviesテーブル
・genresテーブル
・上記2つの中間テーブル（ここではgenre_moviesテーブルとします）

上記3つのモデルをまず作ります。※movieモデルとコントローラーは既にある前提

genreモデルを作成してマイグレーションファイルを編集
```20***_create_genre.rb
t.string :name
```

genre_movieモデルも作成します。
```
rails g model genre_movie
```
からのgenre_moviesテーブルを編集
```20***create_genre_movies.rb
t.references :movie, foreign_key: true
t.references :genre, foreign_key: true
```

```movie.rb
has_many :genre_movies
has_many :genres, through: :genre_movies
```

```genre.rb
has_many :genre_movies
  has_many :movies, through: :genre_movies
```

```genre_movie.rb
belongs_to :movie
belongs_to :genre
```

```movies_controller.rb
def show 
@movie = Movie.find(params[:id])
（省略）
@genres = @movie.genres
end
```


```movies/show.html
<p>ジャンル：
  <% @movie.genres.each do |genre| %>
    <%= genre.name %>
  <% end %>
</p>
```

```seed.rb
Genre.create!(
  [
    {
      name:'サスペンス'
    },
    {
      name:'ドラマ'
    },
    {
      name:'ラブロマンス'
    },
    {
      name:'コメディ'
    }
  ]
)
// Movie.create!は書いてある
// Genre_movie.create!だとエラーになる
GenreMovie.create!(
  [
    {
      movie_id: 1,
      genre_id: 2
    },
    {
      movie_id: 1,
      genre_id: 4
    },
    {
      movie_id: 2,
      genre_id: 3
    },
    {
      movie_id: 3,
      genre_id: 1
    },
    {
      movie_id: 4,
      genre_id: 2
    }
  ]
)
```

これで「ジャンル： ドラマ コメディ」みたいに反映されてればOKです。
Googleデジタルマーケティング＆Eコマース：リアルタイム・マーケティング

# はじめに

”Googleデジタルマーケティング＆Eコマース プロフェッショナル認定証"　の受講が完了しました(2022/3/3)。私がこのコースから得られた素晴らしい体験を、テーマごとに分割して、わかりやすくご紹介しています。興味を持たれた方は、是非下記を受講してみてください。

https://jp.coursera.org/professional-certificates/google-digital-marketing-ecommerce

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/24ea32fb-2fb3-bdb2-462a-4ffc4d60a96f.png" width="800">

# リアルタイム・マーケティングの重要性

リアルタイム・マーケティング（RTM）とは、リアルタイムに行われるマーケティングのことです。ソーシャルメディアでは、コメントやエンゲージメントが素早く行われるため、RTMはしばしば実施されます。何百万人ものユーザーがソーシャルメディア上で大きな話題について話している場合、ブランドはエンゲージメントを高めるためにその会話の一部になりたいと思うでしょう。このような瞬間をうまく利用することで、ブランドは大きな見返りを得ることができるのです。

リアルタイム・マーケティングは、企業がオーディエンスとつながり、特定の議論の中でブランドを好きな場所に位置づけることができる機会なのです。

# 効果的なRTM

リアルタイム・マーケティングを成功させるためには、あなたのコンテンツは次のようなものでなければなりません。

- **関連性があること**
    - 今、何が起きているのか？
    - 今この瞬間、ソーシャルメディア上では何が話題になっているのか？
- **巧み**
    - ブランドの個性やウィットを表現するために、コンテンツをどのようにパッケージングするか？
- **フレッシュ**
    - トレンドトピックが水曜日に発生し、木曜日までコンテンツを作成できなかった場合、そのコンテンツはエンゲージメントを獲得するのに十分な鮮度を保っているか？
- **オンターゲット**
    - このトレンドトピックや時事問題は、オーディエンスの興味を引くものですか？
- **注目を集める**
    - 人々があなたの投稿を見て、スクロールをやめ、すぐに興味を持つには何が必要ですか？

# RTM戦略

リアルタイムの投稿を魅力的なものにするためのヒントを得たところで、次の戦術に従ってトレンドを活用し、リアルタイムでのマーケティングを成功させましょう。

### <font color="MediumBlue">常に耳を傾ける</font>

企業のソーシャルメディア担当者は通常、リアルタイムのマーケティング計画に関連するトレンドを最初に知ることになるため、ソーシャルチームとのコミュニケーションをオープンにしておくことが重要です。もしあなたがソーシャルチームのメンバーなら、できるだけ頻繁にソーシャルメディアに参加し、活用する価値のありそうなトレンドトピックをキャッチできるようにすることを目指しましょう。

### <font color="MediumBlue">ブランドのリソースを利用する</font>

デザインチームやコピー編集チームがある場合は、彼らの力を借りて、できるだけ質の高い、迅速で巧妙な投稿を作成しましょう。

### <font color="MediumBlue">ソーシャルメディアのインフルエンサーを活用する</font>

あなたのブランドがソーシャルメディアのインフルエンサーと提携している場合、彼らにRTM資産をリポストしたり、彼らと交流することで、RTM資産を増幅する手助けをしてもらうように頼みましょう。

### <font color="MediumBlue">どこで会話が行われているかを把握する</font>

あるソーシャルメディア・プラットフォームで起こったことが、他のプラットフォームでは話題にならないことがあります。人々が話題にしている場所で、会話に参加するようにしましょう。

### <font color="MediumBlue">投稿の一部をプロモートする</font>

オーガニックの投稿がいつ、誰に表示されるかはアルゴリズムによって決まるため、オーガニックだけではRTMの投稿が十分な数のオーディエンスに届かず、関連性を保てない可能性があります。これは、より多くのエンゲージメントと潜在的な新しいフォロワーを生成するために、投稿を促進する良い機会である可能性があります。プロモートされた投稿は、あなたがより多くの目に見えるようにするためにプラットフォームにお金を払うソーシャルメディアの投稿です。
vueでnuxt-i18nを使って多言語対応する際に値を渡したい時（I18N国際化対応）

## 参考記事
MDN：
https://developer.mozilla.org/ja/docs/Glossary/I18N

nuxt/i18n（ライブラリ）：
https://i18n.nuxtjs.org/

## 使用例

### 翻訳ファイルを準備
`$t()`を使って指定したパスを呼び出した際に、表示したい文字列を指定しておく

/src_front/assets/lang/pages/pege_name_hoge/fuga/ja_jp.json
```json
{
  "test": {
    "label": "テスト"
  },
  "years_old_or_lower": {
    "label": "{age}歳以下"
  }
}
```

/src_front/assets/lang/pages/pege_name_hoge/fuga/en_us.json
```json
{
  "test": {
    "label": "test"
  },
  "years_old_or_lower": {
    "label": "{age} Years Old or Lower"
  }
}
```

### テンプレート内で呼び出す

/src_front/components/pege_name_hoge/Test.vue
```vue
// 値なしのとき
<div>
  {{
    $t('pege_name_hoge.fuga.test.label')
  }}
</div>

// 値ありのとき
<div>
  {{
    $t('pege_name_hoge.fuga.years_old_or_lower.label', {
      age: item['target_age']
    })
  }}
</div>
```

あとは
/src_front/i18n.config.js
などで言語設定を行う。
css の transition, animation を使ってみる

css で要素を動かす `transition`, `animation` を使ってみる

# transition
要素をプロパティ事に動かすことが出来る
`hover` とかと組み合わせて使うと良さそう
要素に対して `transition` を設定
要素の`hover` ありに変化した後の状態を設定

- 構文
```CSS
div {
    transition: プロパティ 変化時間 変化の仕方 遅延時間;
}

/** よく使うのはプロパティと変化時間、繰り返しもできるので使用例は以下 */
div {
  transition: width 2s, height 2s, line-height 2s, background-color 2s, transform 2s;
}
```

- 使用例
<p class="codepen" data-height="400" data-default-tab="html,result" data-slug-hash="yLxjYWN" data-user="sueasen" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sueasen/pen/yLxjYWN">
  Untitled</a> by sueasen (<a href="https://codepen.io/sueasen">@sueasen</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# animation
要素をいろんなパターンで変化させることが出来る
全体的な動きや細かく動きを設定したいときに使うと良さそう
`@keyframes` の `0%, 20%, ...` で変化の仕方を細かく設定していく（別に3つとか少なくてもOK）
`0%, 100%` の時は `from, to` で設定してもOK

- 構文
```CSS
div {
    animation: キーフレーム名 変化時間 変化の仕方 遅延時間 回数 再生の向き スタイル適用の仕方 実行/停止;
}

@keyframes キーフレーム名 {
    0% {
        プロパティ1: 値1-1;
        プロパティ2: 値2-1;
    }
    50% {
        プロパティ1: 値1-2;
        プロパティ3: 値3-1;
    }
    100% {
        プロパティ1: 値1-3;
        プロパティ2: 値2-2;
        プロパティ3: 値3-2;
    }
}

```
- 使用例
<p class="codepen" data-height="400" data-default-tab="html,result" data-slug-hash="dyqeMMB" data-user="sueasen" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sueasen/pen/dyqeMMB">
  Untitled</a> by sueasen (<a href="https://codepen.io/sueasen">@sueasen</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
DockerでFastAPI+SQLite3+pytest環境をつくりたい

# はじめに
PythonでAPIを作成する機会があり、今後も学んでいくのにdocker環境を作っておきたかったのでこの機会にまとめてみます

https://fastapi.tiangolo.com/ja/

# ディレクトリ構成

```
sample-project
┣ app
┃  ┗ main.py
┣ tests
┣ docker-compose.yml
┣ Dockerfile
```


# 各ファイル

```:Dockerfile
FROM python:3.9-slim-buster

RUN apt-get update && apt-get install -y libsqlite3-dev

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
```

```:docker-compose.yml
services:
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/app
```

```:requirements.txt
fastapi
uvicorn
pytest
```

```:main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}
```

## 動作確認

上記の構成でファイルを作成し、イメージのビルドを行います

```
$ docker-compose build
```

続いてコンテナの起動を行います

```
$ docker-compose up
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/c314000c-39ff-4216-9808-59619e32670c.png)

localhost:8000にアクセスして以下の表示を確認できればOKです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/6fcc2fed-4207-22ee-d1aa-7e8233bda785.png)

また、localhost:8000/docsにアクセスすると、APIドキュメントを確認できました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/9113b90d-4ec6-f40b-be5a-8cf48abc0cff.png)


続いて以下のコマンドでコンテナに入り、pytestを実行します

```
$ docker exec -it fastapi-sample-web-1 sh
# pytest
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/0cd5e0b4-d207-b7f7-c030-0ba6c94578b5.png)



# おわりに
次はエンドポイントの追加やテスト環境の作成などを行っていきます