OmniAICreator/Qiita-1.07M huggingface dataset:
GoogleスプレッドシートでGA4(Googleアナリティクス4)のレポートを出す方法

# Googleスプレッドシートでレポートを出す前に
* データを抽出したいGA4(Googleアナリティクス4)の権限があるアカウントで作業を行ってください

# Googleスプレッドシートレポート作成をする
## 1.「Create new report」を表示させる
* 「拡張機能」を選択
* 「 GA4 Reports Builder for Google Analytics™」を選択
* 「Create new report」を選択
※「GA4 Reports Builder for Google Analytics™」がなかったら以下のものをダウンロードしてください。
https://workspace.google.com/marketplace/app/ga4_reports_builder_for_google_analytics/589269949355

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/626aa441-8176-30bb-f040-eb5fe66f8436.png)
## 2.レポート作成の入力画面
* 以下のレポート作成の入力画面はそれぞれ以下の通りです
※それぞれ説明していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/e0db5d56-a5f2-7f69-85f5-25f6ec832a84.png)

### 2-1.レポート名

* 「Report name」を入力

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/02283df3-b02a-85de-a6fb-5bb475ac7971.png)

※レポート(シート)名です。後半で説明しますが、レポート作成をすると自動でデータ抽出したシートを作成してくれます。
例)集計日や集計項目などなんのレポートか分かりやすいように記載しましょう。(後で修正できるので適当なものを入力してもOK）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/93f03db5-2768-28d5-1d2c-20d258a84da2.png)


### 2-2.GA4のアカウント・プロパティ
* データを抽出したいGA4の **アカウント** と **プロパティ** を選択してください
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/9ca707ac-9d95-3b48-46ad-01e16787b00b.png">



### 2-3.データ抽出期間
* データを抽出したい期間を設定してください

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/76b81148-523c-5e39-7625-9005b16f59b5.png">

### 2-4.ディメンション・メトリクス
* それぞれだしたいものを選択してください
※ディメンション・メトリクス(指標)については以下のサイトを見てみてください。
https://reposub.jp/blogs/ga4/ga4_dimension_metrics
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/d0af4298-e1b5-3aaa-f26c-f1caf8c27739.png">


以下のサイトのチートシートが便利です。
サイト：https://www.march-consulting.jp/analytics/ga4-magic-reports-introduction/
チートシート：https://docs.google.com/spreadsheets/d/1hHvNZMFp-R6AJD2X0Qt_wdROWdWIMaLy0Np-b2eNLyY/edit?pli=1#gid=0

## 3.全項目に入力が完了したら「Create Report」ボタンを押す
* 全て記入終わったら「Create Report」ボタンを押してください
* <img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/3da5f62f-5a49-f29b-cf62-f2a508b3496e.png">
* Create Reportボタンを押すと以下のように設定したものが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/1e644d3c-c88f-37ef-3f81-c35cc29d73dd.png)


## 4.「Run reports」を実行
* 「拡張機能」を選択
* 「 GA4 Reports Builder for Google Analytics™」を選択
* 「Run reports」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/d928ac15-9c86-c02c-5064-866023dc812a.png)

## 5.レポート生成
新たにタブで先ほど設定したレポート名のレポートシートが生成され、データ抽出ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/4345282b-f953-a719-67d2-77764318a55c.png)
CakePHP5 Paginate 表示件数(limit)を一括設定

Cakeのルール的に合ってるのかなぁ。
※おひとりさま開発が多く、私はオレオレフレームワーク大好きな人

で、情報少なく
・
・
・
　...　こうなった。

AppController.php（か、各コントローラが継承しているAppControllerの子クラス）にて


    class AdminController extends AppController
    {  
        public function initialize(): void
        {
            parent::initialize();
            $this->loadComponent('Authentication.Authentication');
    
            $this->paginate['maxLimit'] = 500;
            $this->paginate['limit'] = 500;    
        }
    }

各コントローラーはこんな感じ
 
    class UsersController extends AdminController
    {
        public function index()
        {
            ...
        }
    }
Oracleで直前に実行したSQLを取得する。

## はじめに

- ブラックボックス化しているアプリを解析している際に、Oracleにて実行されたSQLの内容を知りたいことがあるかと思います。ソースを読むのも手ですが、実際に実行されているSQLの内容を手軽に調べたい際に有効です。

## やりたいこと

- IISのアプリケーションより実行されているSQLの内容を調べる。


## 手順

- SQL*Plusにログインして以下のSQLを実行する。（直近5分以内に実行されたSQLが取得出来ます。）

```sql
SELECT 
SQL_ID ||','|| MODULE ||','|| FIRST_LOAD_TIME
,DBMS_LOB.SUBSTR(t1.SQL_FULLTEXT, 1000, 1) AS C1
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 1000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 1001) END AS C2
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 2000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 2001) END AS C3
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 3000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 3001) END AS C4
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 4000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 4001) END AS C5
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 5000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 5001) END AS C6
,CASE WHEN DBMS_LOB.GETLENGTH(SQL_FULLTEXT) > 6000
 THEN DBMS_LOB.SUBSTR (t1.SQL_FULLTEXT,1000, 6001) END AS C7
from v$sqlarea t1
where
     MODULE = 'w3wp.exe'
 and FIRST_LOAD_TIME BETWEEN TO_CHAR(SYSDATE - (5 / 24 / 60), 'YYYY-MM-DD/HH24:MI:SS') AND TO_CHAR(SYSDATE, 'YYYY-MM-DD/HH24:MI:SS')
order by FIRST_LOAD_TIME;
```


## 参考
- [Oracle SQL_FULLTEXT(CLOB)を分割しながらすべてを表示する](https://oha-yo.com/oracle/sql_fulltext/)
【React】コンテンツをスライドさせて表示したい

# はじめに

https://react-slick.neostack.com/docs/example/auto-play

react-slickを使って自動でスライド送りするような動きを実装してみます


# やりかた

https://react-slick.neostack.com/docs/get-started

```
yarn add react-slick
yarn add slick-carousel
```

次に公式に記載されているコードを参考にコンポーネントを実装します。
(この記事では少し変えています。cssは適用していないです。)

https://react-slick.neostack.com/docs/example/auto-play

<details><summary>コンポーネント</summary><div>


```tsx
import React from "react";
import Slider from "react-slick";
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';

import a from '../assets/images/a.jpg';
import b from '../assets/images/b.jpg';
import c from '../assets/images/c.jpg';

function ViewSlide() {
  const settings = {
    dots: true,
    infinite: true,
    slidesToShow: 3,
    slidesToScroll: 1,
    autoplay: true,
    speed: 2000,
    autoplaySpeed: 5000,
    cssEase: "linear",
    pauseOnHover: false,
  };
  return (
      <Slider {...settings}>
      <img src= {a} alt="Slide a"></img>
        <img src= {b} alt="Slide b"></img>
        <img src= {c} alt="Slide c"></img>
      </Slider>
  );
}


export default ViewSlide;

```
</div></details>

react-slickをインストールして実装をすると、以下のエラーが発生しました

![l_9007702_1840_58116007703a6480698f93068c83b65e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2889192/69545161-a7ce-863f-3134-6c7c930c87a1.png)

プロジェクト配下にreact-slick.d.tsファイルを作成し、ファイルに以下を記載することで解決しました。
```
declare module 'react-slick';
```

# おわりに
簡単でした！


# 参考記事

https://devpediacode.com/information/WEB/533dc1b3-85d6-558c-ad20-21dde079e12a
Radeon用プロファイラとアセンブラなど

かつてRadeon用プログラムの作成に有用だったもの

# プログラム  
### [Radeon™ GPU Profiler](https://github.com/GPUOpen-Tools/radeon_gpu_profiler)  
RadeonDeveloperPanelでプロファイルデータを収集し、RadeonGPUProfilerで情報を表示します。  
RadeonGPUAnalyzerではOpenCL Cをコンパイルし、そのニモニックを表示します。  
  
:::note info  
gfx900(Vega 10)の場合は[v1.15.1](https://github.com/GPUOpen-Tools/radeon_gpu_profiler/releases/tag/v1.15.1)、おそらくgfx800(RX 480など)の場合は[v1.13](https://github.com/GPUOpen-Tools/radeon_gpu_profiler/releases/tag/v1.13)が対応している中で最も新しいものだと思います。  
:::  
  
:::note info  
clangなどOpenCL Cをクロスコンパイルできるファイルが入っています。  
直接使おうとするとbitcodeの指定で引数が非常に長くなります。  
また、私の環境ではclangで生成したデータを使おうとしてもclBuildProgramがCL_BUILD_PROGRAM_FAILURE, Error while BRIG Codegen phase: the binary is incompleteを返しました。  

:::  
<details><summary>clangに指定した引数の詳細</summary>  

```
# clclc.py

import sys
import os


kRDTS_Dir = 'D:\\tools\\RadeonDeveloperToolSuite'
kHeader_Path = kRDTS_Dir + '\\utils\\lc\\opencl\\include\\opencl-c.h'
kClang_Path = kRDTS_Dir + '\\utils\\lc\\opencl\\bin\\clang.exe'
kBitcode_Dir = kRDTS_Dir + '\\utils\\lc\\opencl\\lib\\bitcode'


def quot(s):
	return '\"' + s + '\"'


if __name__ == '__main__':
	args = [kClang_Path]
	args.append('-nogpulib')
	args.append('--target=amdgcn-amd-amdhsa')
	args.append('-fshort-wchar')
	args.append('-include')
	args.append(quot(kHeader_Path))

	for f in os.listdir(kBitcode_Dir):
		if f.lower().endswith('.bc'):
			args.append('-Xclang')
			args.append('-mlink-bitcode-file')
			args.append('-Xclang')
			args.append(quot(kBitcode_Dir + '\\' + f))

	for a in sys.argv[1:]:
		args.append(a)

	os.execv(kClang_Path, args)
```

```
clclc -x cl -cl-kernel-arg-info -cl-std=cl2.0 -D__OPENCL_VERSION__=200 -O3 -mcpu=gfx900 hoge.cl -o clang_kernel.bin
```
</details>

### [CLRadeonExtender](https://github.com/CLRX/CLRX-mirror)  
アセンブラとディスアセンブラです。  
アセンブルした生成物をclCreateProgramWithBinaryで流し込んでカーネルプログラムとして使用できます。  
clGetProgramInfo(, CL_PROGRAM_BINARIES)で取ってきたデータをディスアセンブルできます。  
  
# ドキュメント
### [AMDGPU Compute Application Binary Interface](https://github.com/ROCm/ROCm-ComputeABI-Doc/blob/master/AMDGPU-ABI.md)  
どこのようにカーネル呼出をするのかを設定する方法。
  
### [Heterogeneous System Architecture Foundation: Standards](http://hsafoundation.com/standards/)  
前述ABIの補完その他。
  
### [AMD GPU ISA documentation](https://gpuopen.com/amd-isa-documentation/)  
:::note info  
Vega ISAなどの情報は不十分なので、GCN 3 ISAも読むことをおすすめします。  
:::  
  
### [AMD Documentation Hub](https://www.amd.com/en/search/documentation/hub.html)  
OpenCL™ Optimization Guideなどがあります。
Next.js 名詞抽出サンプル

# 名詞をクライアント側で抽出


```
npm install kuromoji
```
- テキストエリアに入力しフォーカスを外す
```typescript
'use client'
import React, { useState } from 'react';
import kuromoji from 'kuromoji';

// kuromojiの辞書のパスを指定します。publicの場合以下
const DICT_PATH = '../kuromoji/dict';

// 名詞のスタイル
const nounStyle = {
    color: 'red',
};

type Token = {
    surface_form: string;
    pos: string;
};

const NounHighlighter: React.FC = () => {
    const [text, setText] = useState<string>('');
    const [tokens, setTokens] = useState<Token[]>([]);

    const handleAnalyze = () => {
        kuromoji.builder({ dicPath: DICT_PATH }).build((err, tokenizer) => {
            if (err) {
                console.error(err);
                return;
            }
            const tokens = tokenizer.tokenize(text);
            setTokens(tokens);
        });
    };

    return (
        <div>
            <h1>単語抽出 サンプルコード</h1>
      <textarea
          value={text}
          onChange={(e) => setText(e.target.value)}
          onBlur={handleAnalyze}
          placeholder="ここにテキストを入力してください"
          rows={10}
          className="w-full p-2 border"
      />
            <div>
                {tokens.map((token, index) => (
                    <span key={index} style={token.pos === '名詞' ? nounStyle : undefined}>
            {token.surface_form}
          </span>
                ))}
            </div>
        </div>
    );
};

export default NounHighlighter;
```
Next.js APP (ver14) SSEのサンプルコード

# NEXTJS SSEのサンプルコードです。

NextResponse でのサンプルコードがあまり見つからないので
- src/app/api/xxxの場合

## サーバー側

```typescript
import { NextRequest, NextResponse } from "next/server";

/*
sse通信のサンプルコード
 */

export async function GET(req:NextRequest) {
    const stream = new TransformStream()
    // 以下のwriterにメッセージを入れるとストリーミングメッセージとなる writer.write(message)
    const writer = stream.writable.getWriter()
    const encoder = new TextEncoder()

    // 1秒後にファイルチェックの結果を流し込む 改行２個がデータエンドなので消さないこと。本文中の改行連続はエンコーダーで処理されており保存される
    setTimeout(() => {
        const message = encoder.encode(`data: ${JSON.stringify('File has been checked')}\n\n`);
        writer.write(message);
        console.log(message);
    }, 1000);

    // 2秒後にファイル解析の結果を流し込む
    setTimeout(() => {
        const message = encoder.encode(`data: ${JSON.stringify('File has been parsed')}\n\n`);
        writer.write(message);
        console.log(message);
    }, 2000);

    // 3秒後に接続を閉じる
    setTimeout(() => {
        writer.close();
        console.log("finish")
    }, 3000);

    return new NextResponse(stream.readable, {
        headers: {
            'Content-Type': 'text/event-stream',
            Connection: 'keep-alive',
            'Cache-Control': 'no-cache, no-transform',
        },
    });
}
```
## クライアント側
```typescript
'use client'
import { useState, useEffect } from 'react';
/*
SSE通信のモデルコード
 */



const UploadComponent = () => {
    const [status, setStatus] = useState<string>('');

    useEffect(() => {
        const handleEventSourceMessage = (event:any) => {
            setStatus(JSON.parse(event.data));
        };

        const handleEventSourceError = () => {
            eventSource.close(); // 接続が閉じられたら EventSource を明示的に閉じる
        };

        const eventSource = new EventSource('/api/sample/sse');

        eventSource.addEventListener('message', handleEventSourceMessage);
        eventSource.onerror = handleEventSourceError; // エラーハンドラを追加

        return () => {
            eventSource.removeEventListener('message', handleEventSourceMessage);
            eventSource.close();
        };
    }, []);
    const handleUpload = () => {
        // TODO: 実際のアップロードロジック
        // axios などを利用してファイルをサーバに POST
        // 成功したら以下を実行
        setStatus('File has been uploaded');
    };

    return (
        <div>
            <button onClick={handleUpload}>Upload File</button>
            <p>Status: {status}</p>
        </div>
    );
};

```
python sqlite ベクトル

# sqliteでベクトルを保存管理する方法


```python
    text = "こんにちは"
    vector = ai.getTextVector(text)
    print(len(vector))

    # ------書き込み
    # 配列データ

    # データをバイナリ形式に変換する
    blob_data = pickle.dumps(vector)

    # SQLite データベースに接続（データベースが存在しない場合は新しく作成される）
    conn = sqlite3.connect('example.db')

    # カーソルを作成
    cursor = conn.cursor()

    # テーブルを作成（すでに存在している場合は先に削除）
    cursor.execute("DROP TABLE IF EXISTS my_table")
    cursor.execute("CREATE TABLE my_table (id INTEGER PRIMARY KEY, data BLOB)")

    # データを挿入
    cursor.execute("INSERT INTO my_table (data) VALUES (?)", (blob_data,))

    # 変更をコミット
    conn.commit()

    # データベースの接続を閉じる
    conn.close()

```
# 読み取り方法

```python
    # --------読み取り
    import sqlite3
    import pickle

    # SQLite データベースに接続
    conn = sqlite3.connect('example.db')

    # カーソルを作成
    cursor = conn.cursor()

    # データを取得
    cursor.execute("SELECT data FROM my_table WHERE id = 1")
    blob_data = cursor.fetchone()[0]

    # バイナリデータを配列に変換
    data = pickle.loads(blob_data)

    # 配列を表示
    print(data)

    # データ比較
    print(vector == data)
```

あとは類似度なり計算なりに活用
rbenvでRubyのバージョンを指定できてない理由はパスが通ってないからだった

この前rbenvでRubyのバージョンを3.3.0にしたはずなのに、`ruby -v`で確認したら2.6.10に戻っていた。なんでだ？

```ruby
$ ruby -v
ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin23]
```

少し前にRubyのバージョンを更新したときはここを指定してたんだよな...。
(そのときのメモに書いてあった)
```ruby
$ which ruby
/Users/*username*/.rbenv/shims/ruby
```

rbenvでインストールしようとしたら、すでにあると言われた。
```ruby
$ rbenv install 3.3.0
rbenv: /Users/*username*/.rbenv/versions/3.3.0 already exists
continue with installation? (y/N) N
```

rbenvのパスを通していないからか？
rbenvはどこにあるんだ？

```ruby
$ which rbenv
/opt/homebrew/bin/rbenv
```

ここにパスを通してみると、Rubyのバージョンは変わるか？
```ruby
$ echo 'export PATH="/opt/homebrew/bin:$PATH"' >> ~/.bash_profile
[~]$ source ~/.bash_profile
[~]$ ruby -v
ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin23]
```
変わらない。。。

[こちらの記事](https://zenn.dev/maimai_neet/articles/8228b119de7518)
を参考に、

rbenv shimsにパスを通してみたら、、、
```ruby
echo 'export PATH="$HOME/.rbenv/shims:$PATH"' >> ~/.bash_profile
```

おお、rubyのパスが変わっている。
Rubyのバージョンもrbenvでインストールしたほうになっている。
```ruby
$ which ruby
/Users/*username*/.rbenv/shims/ruby
[~]$ ruby -v
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
```

shimsについては[こちらの記事](https://mogulla3.tech/articles/2021-07-12-understanding-rbenv-4/)で丁寧に解説されていた。

[rbenvの公式リファレンス](https://github.com/rbenv/rbenv)にも`~/.rbenv/shims`にパスを通すことが必要と書いてあった。
> Prepends `~/.rbenv/shims` directory to PATH. This is basically the only requirement for rbenv to function properly.
AWS Redshiftのスキーマ・テーブル情報取得SQL

Resshiftでスキーマとテーブルを作成した時のSQLメモ

# テーブル

### テーブル一覧の表示
```
select * from pg_tables 
where schemaname = '<スキーマ名>'
```
### テーブルのカラム一覧の表示
```
select *
from information_schema.columns
where
        table_catalog = 'dev'
    and table_schema = '<schema_name>'
    and table_name = '<TABLE_NAME>'
order by
    table_catalog, table_schema, table_name, ordinal_position
;
```

https://dataedo.com/kb/query/amazon-redshift/list-columns-names-in-specific-table
https://docs.aws.amazon.com/redshift/latest/dg/r_SHOW_TABLE.html
https://stackoverflow.com/questions/50209790/retrieving-column-names-from-table-redshift
https://qiita.com/kazuho39/items/ebf4c69c91cedb655460

### テーブルの所有者を変更
```
ALTER TABLE <schemaname>.<tablename> OWNER TO <new_user>;
```

### テーブルの所有者変更SQLを一括作成
```
SELECT
    'ALTER TABLE ' || schemaname || '.' || tablename || ' OWNER TO new_user;'
FROM pg_tables 
WHERE tableowner ='old_user'
```
https://qiita.com/YusukeHigaki/items/ea81633bd1ccac078c7c


# スキーマ
### スキーマ一覧の表示
```
select
  n.nspname as "schema"
  ,pg_catalog.pg_get_userbyid(n.nspowner) as "owner"
  ,n.nspacl
from pg_catalog.pg_namespace n
where n.nspname !~ '^pg_' and n.nspname <> 'information_schema' order by 1;
```
https://oha-yo.com/postgresql/tab_privs/

### スキーマ名の変更
```
ALTER SCHEMA name RENAME TO new_name
```
https://www.postgresql.org/docs/current/sql-alterschema.html

### スキーマ所有者の変更
```
ALTER SCHEMA name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }
```
assistants api を laravel php から使ってみよう

# assistants api ってなんだろ

phpやpythonから使えるgptsみたいなもの

# 命令

おはようと入力したらおっはーと返して欲しい

# 早速使ってみる

![入力結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31338/d23acbad-145c-aced-e344-3b848a709342.png)

Instructionsも一緒に送られるため、文字数を多くしすぎると
token数を大量に消費してしまう。

# 結果

おっはー
と帰ってきましたね。

# ではphpから使ってみる

# threadを作る

5chの1つのスレッドみたいな感じね

まずは $key は open ai の apikey を用意してね
古いのだと動かないので、新しいのを作ろう。
課金しておいたほうがいいかもね。



```hoge.php

    $key = 'yourapikey';
    $asst_id = "asst_youryour";//上記画像のNameの↓にあるグレー色の

    //スレッドを作る
    $response = Http::withHeaders([
        'Content-Type' => 'application/json',
        'Authorization' => $key,
        'OpenAI-Beta' => 'assistants=v1'
    ])->post('https://api.openai.com/v1/threads', [
    ]);

    if ($response->failed()) {
        $res = 'Error: ' . $response->status();
    } else {
        $res = $response->json();
    }

    //スレッド名
    $thread = $res['id'];

    //メッセージを送る
    $body = [
        'role' => 'user', // role が必須
        'content' => 'おはよう' // content が必須
    ];

    $response = Http::withHeaders([
        'Content-Type' => 'application/json',
        'Authorization' => $key,
        'OpenAI-Beta' => 'assistants=v1'
    ])->post('https://api.openai.com/v1/threads/' . $thread . '/messages', $body);


    if ($response->failed()) {
        $res = 'Error: ' . $response->status();
    } else {
        $res = $response->json();
    }

    //スレッドを開始する
    $response = Http::withHeaders([
        'Content-Type' => 'application/json',
        'Authorization' => $key,
        'OpenAI-Beta' => 'assistants=v1'
    ])->post('https://api.openai.com/v1/threads/'.$thread.'/runs',
        ['assistant_id' => $asst_id]
    );

    if ($response->failed()) {
        $res = 'Error: ' . $response->status();
    } else {
        $res = $response->json();
    }

    //$res['status']がcompletedになるまで結果を取得できない。
    //とりあえず適当に sleep させる待つ。

    sleep(10);

    $response = Http::withHeaders([
        'Content-Type' => 'application/json',
        'Authorization' => $key,
        'OpenAI-Beta' => 'assistants=v1'
    ])->get('https://api.openai.com/v1/threads/'.$thread.'/messages');


    $messageText = $response['data'][0]['content'][0]['text']['value'];
    echo $messageText;
```

結果
`おっはー`

これで Assistant API を phpから使うことができますね。


# 考え方

・まずはスレッドを作成
・スレッドにメッセージを送る
・スレッドを開始

このときのスレッドIDだけ保存しておけば1つの掲示板みたいに
まとまるので、何度も同じ情報を送る必要もなく保存してくれる。

たろうさん専用スレッド
花子さん専用スレッド

を作れば各ユーザーごとのスレッドが出来上がるというわけだ。

・結果が出るまで待つ

今回は sleep(10) と適当にやったけど、`$res['status']がcompleted`になるまで毎秒アクセスしても良いかな。
もしくは、DBに処理結果を突っ込み、cronで30分後に取得させるとか

・結果を取得して完了
JOBをある程度決まった時間に実行【GitHub Actions】

## はじめに
ある程度決まった時間とタイトルに書いてますが、例えば
「cron: '0 0 * * *'」 ← ※UTC時間で書く必要がある
と書いていても朝7時ジャストには動きません。
大体30分前後のラグがあります。

## GitHub Actionsとは
GitHub ActionsはGitHubがサービスの一環として提供する、ワークフロー自動化サービスです。
一言でいうとGitにアップしているソースを、時間を指定して自動実行できます。

## 触ってみよう
GitにソースをアップしたらActionタブをクリック 検索窓にsimpleと入力し、「simple workflow」を選択します。![1754924d-9806-5d6f-eb79-388753079afd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576208/3f8084d6-8f8e-ad45-fc28-a03e74bc1276.png)こんな感じで雛形がでてきます。
![a9b05bed-2ba1-9a58-66d0-ad21ad1ae4a3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2576208/a0d8a475-2770-a6bb-8dcc-93897a9e7d6b.png)
以下サンプル
```
# ワークフロー名
name: test_chatbot

# 発火タイミング
# 日本時刻の朝7時に定期処理(分 時 日 月 曜日)
# UTC の 02:00 は JST だと 11:00 。
# 曜日指定には [0, 6] の整数を使います。 0 が日曜日、 6 が土曜日です。
# 実はgithub actionsのcronの時刻はUTCなので、日本時間に合わせるために日本時刻から9時間マイナスで設定する必要があります。
 on:
   schedule:
     - cron: '0 0 * * *'


jobs:
  build:
    # Ubuntuの最新版環境内で処理を実行することを指定
    runs-on: ubuntu-latest

    # 実行する処理＆コマンド指定
    steps:
      # リポジトリからチェックアウトして以下の処理を実行していく
      - uses: actions/checkout@v2
      - name: Set up Python 3.9
        uses: actions/setup-python@v1
        with:
          python-version: 3.9
      - name: Install dependencies
        run: |
          # pip更新
          python -m pip install --upgrade pip
          # 必要なパッケージインストール
          pip install line-bot-sdk
          pip install schedule
          pip install beautifulsoup4
      - name: Run script
        run: |
          # main.pyの実行
          python main.py
```

お好きな名前をどうぞ、、
```
# ワークフロー名
name: test_chatbot
```
実行したい日時を決めましょう。 指定の方法はcronと同じです。（UTCなので注意）
```
# 発火タイミング
# 日本時刻の朝7時に定期処理(分 時 日 月 曜日)
# UTC の 02:00 は JST だと 11:00 。
# 曜日指定には [0, 6] の整数を使います。 0 が日曜日、 6 が土曜日です。
# 実はgithub actionsのcronの時刻はUTCなので、日本時間に合わせるために日本時刻から9時間マイナスで設定する必要があります。
 on:
   schedule:
     - cron: '0 0 * * *'
```
pythonで必要なパッケージをインストールするためにまずpipの更新 次にスクリプトで実行するために必要な外部パッケージをインストール
```
run: |
  # pip更新
  python -m pip install --upgrade pip
  # 必要なパッケージインストール
  pip install line-bot-sdk
  pip install schedule
  pip install beautifulsoup4
```
実行したいpythonファイル
```
run: |
    # main.pyの実行
    python main.py
```
編集が終わったら右上の「commit changes...」を押下して終了です。


---
記事を読んでくださった方は、是非弊社開発課のXもフォローしてね
毎日エンジニアに向けた情報発信を行っています
https://twitter.com/tech_cin
Webサービスが初回アクセス後数分放置して画面遷移するとエラーになる

## 概要

ブラウザから初回アクセスしたときは特段問題ないのに、少し放置すると通信が不安定になる問題があったので自身の場合の解決方法を簡単にまとめる。

## 注意

本知見は自分自身によるものではありません。先輩技術者さんから提供いただいた内容になります。本記事は自身用のメモとしての意味合いが強いです。
先輩技術者さんへこの場を借りて改めてお礼申し上げます。本件本当にありがとうございます。

## 詳細

nginxのリバプロが存在するWebサービスのインフラ構築をした。
EC2インスタンスは起動・動作実績のあるインスタンスのAMIから起動した。
ドメインの解決やネットワーク系のリソース作成も行い、いざブラウザでアクセスする。このときは問題なく画面遷移やWebサービス提供機能が使える。（画面遷移が遅い気もするが。。。）
その状態でブラウザを数分〜数十分放置すると画面ロードがめっちゃ長くなるか`ERR_CONNECTION_RESET`のエラーで画面が遷移できなくなる。
数回リロードするとエラーは解消する。

## 自身の場合の解消方法

リバプロnginxの設定で下記の内容（ipv6を許容する設定）を追記してnginxを再起動させた。

```
listen   [::]:80 default_server;
```

これにより数分〜数十分放置してもエラーが出たり、アクセスに時間がかかったりしなくなった。

## 原因は謎

まず、下記の確認ツールを使って自身がipv6を使っているのかを確認してみた。結果自身はipv4だった。

https://test-ipv6.com/

ということは「今までipv6でアクセスしてて、nginxの設定でipv6が許容されたから問題が解決した」わけではなさそう。（初回アクセス時にはそもそも通常動作してたし。。）

さらに当該のEC2に関連するロードバランサーやVPNの設定を確認したがipv6は許容されていなかった。

もしかすると`listen   [::]:80 default_server;`の追記はそこまで原因の解決につながっておらず、nginxの再起動が効果的だった可能性もある。（キャッシュが悪さしていたり、、、）（本来 `listen   [::]:80 default_server;`の設定を削除し、nginxを再起動して様子見すればココも切り分けできる気がするが、再発して直せなくなっても困るので今回は検証しない。。！）

ちなみにv4とv6療法をサポートするモードは「デュアルスタック」と呼ばれるらしい。このモードにしたことにより通信が安定化された可能性も考えられなくはない

https://www.mofu2soken.com/archives/160
【Vim】よく使うコマンド チートシート

# Vim 操作コマンド
Vimの操作コマンドについて記載する。
随時更新予定。

# カーソル
| キー | 動作                 |
|:-----|:---------------------|
| h    | 1文字左              |
| j    | 1文字下              |
| k    | 1文字上              |
| l    | 1文字右              |
| 5h   | 5文字←               |
| w    | 1単語 右へ(単語冒頭) |
| b    | 1単語 左へ(単語冒頭) |
| e    | 1単語 右へ(単語末尾) |
| ^    | 行頭                 |
| $    | 行末                 |
| G    | 最終行 行頭          |
| 1G   | 1行目行頭            |

# 入力モード
| キー | 動作                                   |
|:-----|:---------------------------------------|
| i    | カーソルの左から                       |
| a    | カーソルの右から                       |
| I    | カーソルの行頭から                     |
| A    | カーソルの行末から                     |
| o    | 1行下に空行を作ってから                |
| O    | 1行上に空行を作ってから                |
| c    | カーソル上の文字を消してから           |
| C    | カーソルから行末までの文字を消してから |
| r    | カーソル上の文字を上書き               |
| R    | escを押すまで以降の入力を全て上書き    |

# コピーペースト
| キー | 動作                    |
|:-----|:------------------------|
| yy   | 1行コピー               |
| p    | カーソル下に1行貼り付け |
| P    | カーソル上に1行貼り付け |
| 3yy  | 3行コピー               |
| 3p   | カーソル下に3行貼り付け |
| 3P   | カーソル上に3行貼り付け |

# 検索
| キー  | 動作               |
|:------|:-------------------|
| /hoge | 下方向にhogeを検索 |
| ?hoge | 上方向にhogeを検索 |
| n     | 次の検索結果       |
| N     | 前の検索結果       |

# 置換
| キー             | 動作                               |
|:-----------------|:-----------------------------------|
| s/hoge/piyo/     | hogeをpiyoに置換                   |
| :%s/hoge/piyo/gc | 全てのhogeをpiyoに確認しながら置換 |

# 削除
| キー | 動作         |
|:-----|:-------------|
| x    | 1文字削除    |
| dw   | 1単語削除    |
| d$   | 行末まで削除 |
| D    | 行末まで削除 |
| d^   | 行頭まで削除 |
| dd   | 1行削除      |

# 元に戻す
| キー | 動作        |
|:-----|:------------|
| u    | 1回元に戻す |
| 3u   | 3回元に戻す |
【PHP】 __destruct()の実行タイミングについての備忘録

業務において`__destruct()`周りの挙動を調査する過程で、実行タイミングがいまいちイメージしにくかったので備忘として記載してみました。

# __destruct()とは
公式ドキュメントでは以下のように説明されていました。
>　デストラクタメソッドは、 特定のオブジェクトを参照するリファレンスがひとつもなくなったときにコールされます。 
>あるいは、スクリプトの終了時にも順不同でコールされます。

# 実際に動かしてみた
文章だけ見てもいまいちイメージがつかなかったので簡単なコードを書いてみました。
sample.phpに`__construct()`、`__destruct()`とpublicな変数を定義しました。
```php:sample.php
<?php
class Sample
{
    function __construct()
    {
        echo "コンストラクトの実行" . PHP_EOL;
    }
    function __destruct()
    {
        echo "デストラクトの実行" . PHP_EOL;
    }
    public $value = "変数値の出力" . PHP_EOL;
}

```
action.phpでSampleクラスをインスタンス化させてます。
その後、$valueを出力させるように記載してみて動かしてみます。
```php:action.php
<?php
require("./sample.php");

$sample = new Sample();

echo $sample->value;
```

実行結果を見てみます。
```terminal:実行結果
コンストラクトの実行
変数値の出力
デストラクトの実行
```

実行結果からわかるように、デストラクトが一番最後に実行されていることがわかります。

なんとなく$valueの出力処理前に、`return`を入れたらどうなるのかなと思ったので検証してみました。

```php:action.php
<?php
require("./sample.php");

$sample = new Sample();

echo "実行終了" . PHP_EOL;

return;

echo $sample->value;
```

実行結果から分かる通り、returnで$valueの出力処理自体は実行されてないですが、デストラクトの実行はされています。
```terminal:実行結果
コンストラクトの実行
実行終了
デストラクトの実行
```

# まとめ
デストラクトの挙動はretunなどには影響されず処理が全て完了した後に実行されるとイメージができました。
ただ、あくまで簡単な検証なだけで詳細な挙動については公式ドキュメントをぜひ閲覧いただければと思います。

[コンストラクタとデストラクタ - Manual](https://www.php.net/manual/ja/language.oop5.decon.php)
VRChatでアバター改変するにあたり知っておくと便利なこと

# はじめに

この記事ではVRChatでアバター改変するときに知っていると知らないのとでは差が出る便利な機能について説明します。

<details><summary>目次</summary>

- [VCCでのバックアップと復元方法](#-VCCでのバックアップと復元方法)
- [ブループリントIDを変更し、別アバターや同じアバターとしてアップロードする](#-ブループリントIDを変更し、別アバターや同じアバターとしてアップロードする)
- [小物を合わせる時に便利な操作方法](#-小物を合わせる時に便利な操作方法)
- [Unity上でDeleteしてしまったアイテムを復元する方法](#-Unity上でDeleteしてしまったアイテムを復元する方法)

</details>

# VCCでのバックアップと復元方法

VCCを使ったバックアップと復元方法について解説します。

### バックアップ方法

プロジェクトの右上、もしくは右側の･･･を押すと[ Create Backup ]というボタンがあるのでここを押します。
ロードが完了するとバックアップは終わりです。

バックアップの場所はVCCの左下、Settingの[ Backups ]の部分にフォルダが書いてあります。
ここを変更するとバックアップが生成される場所も変わるのでわかりやすい場所に変えておくといいでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/a0209b17-4cc2-4710-a4c3-2d98eb050d40.png)

### 復元方法

VCCの右上、[ Create New Project ]の横にある▽を押すと[ Add Existing Project ]のボタンがあるのでこれを押します。
バックアップされたプロジェクトを選択するとプロジェクトを作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/d820d630-f4d3-467a-bef4-8ff1c5de1f6b.png)


# ブループリントIDを変更し、別アバターや同じアバターとしてアップロードする

まず前提知識として、ブループリントIDとはアバターを識別するためのIDになります。
アバターをアップロードする前にこれを変更することにより、別のプロジェクトでアップロードしたアバターに上書きをしたり同じプロジェクト内でコピーしたアバターを別のアバターとしてアップロードすることが可能です。

### 変更方法

アバターを選択したときのインスペクターに[ Pipeline Manager ]というスクリプトが入っていると思います。

一度アップロードしたことのあるアバターは[ BlueprintID ]のところに識別番号が書いてあります。
ブループリントIDを削除して別のアバターとしてアップロードしたい場合は下のボタン[ Detach ]を押すとBlueprintIDを削除することができます。

前アップロードしたことのあるアバターに上書きしたい場合、元のアバターのブループリントIDのところをコピーして新しいアバターに貼り付け、[ Attach ]のボタンを押すと上書きできます。
元アバターがなく、ブループリントIDがコピーできないという場合はVRChatSDKの[ Content Manager ]のタブを開き、アップロードしたいアバターの[ Copy ID ]を押すとブループリントIDがコピーできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/0a56de64-82a8-48fb-a206-12b941078a28.png)


# 小物を合わせる時に便利な操作方法

初心者でよく躓いているUnityの操作方法について解説します。

### Unityの基本操作

よく使われる操作方法はマウスホイールクリックで視点操作、右クリックで視点回転、マウスホイールで拡大縮小ですね。

Unityの画面ではそれ以外にも操作方法があります。

**右クリック+WASD**

右クリックを押しながらWASDキーを押すとUnityの中を飛ぶように移動できます。
視点移動が速い場合は右クリックを押しながらマウスホイールを回すと移動速度を変えることができます。
これを使えば少しだけ近づくことやちょっとだけ右にずれるみたいなことができるようになります。

### 近づきすぎると消えてしまう時の対処法

今説明した操作方法を使って近づきすぎると手前側が消えてしまう時があるでしょう。
そんな時はシーンウィンドウの右上のカメラマークを押して[ Clipping Planes ]の[ Near ]の値を小さくしてみましょう。(小さくしすぎると重たくなるので注意)

これをすると近くに行っても消えなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/5f8805ef-31b8-48b1-90aa-a936488e9f29.png)


### 邪魔な表示を消す

Unityの画面でライトやパーティクルの表示が邪魔になったことはないですか？
これを消す方法を教えます！

右上にあるカメラマークの横のボタンを押すだけ！
Unity2019なら[ Gizmos ]のボタン、Unity2022なら丸いボタン
これで消えます！

※ビューポイントの設定をするときの丸も消えてしまうので注意

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/ceafee9b-9735-4313-8a8f-6d2dc14d4300.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713820/75015a26-1f1f-49a7-9a95-4eea7b505202.png)

# Unity上でDeleteしてしまったアイテムを復元する方法

Unityのアセットやアバターを削除してしまった！
簡単に入れなおせるものならいいですが自作したものやプレハブ化したアバター等だと元に戻すのは大変です。
でも安心してください、簡単に元に戻す方法があります！

削除してしまったアイテムはPCのディスクトップ画面にあるゴミ箱へ移動しただけです。
ゴミ箱を開き、戻したいアイテムを右クリック → 元に戻すで戻ります！
ゴミ箱に入ったアイテムは30日経つと自動で削除されるので注意しましょう。

# おわりに

今回ご紹介した機能はほんの一部でUnityを使えば使うほどいい機能はたくさん覚えると思います！
ここに書いた方がいいんじゃないか？という機能があったら是非コメントにお書きください。
気が向いたら追記します。
Xcodeを使用してiOS simulatorを追加する方法

## 概要
MaxにiOS simulatorを追加する方法を投稿します。

既にX-codeがインストールされていることを前提としています。

## 環境
PC: Mac v 14.3
Xcode: v 15.2

## 手順1
Xcodeを開いてmacのメニューバーの`Window`をクリック。
その後、`Devices and Simulators`をクリック。
![スクリーンショット 2024-02-15 14.48.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952574/d3e8591a-0c55-517d-18ab-27eff88fd973.png)

画面左上の`Simulators`ボタンをクリックして、画面右下の`+`ボタンをクリック
![スクリーンショット 2024-02-15 14.49.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952574/c29cf0c2-7b5b-09e7-39a6-991c15bdc04d.png)

その後、下図の画面が表示されるので任意のデバイスを追加する。

![スクリーンショット 2024-02-15 18.36.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952574/c29526a4-f407-49a8-0434-fbee9df5a8c2.png)



もし上記の手順で端末が追加できない場合、XcodeにインストールしているiOSのバージョンの対象外の可能性があります。
端末の対象は以下のリンクなどで確認してください。
https://support.apple.com/ja-jp/guide/iphone/iphe3fa5df43/17.0/ios/17.0

次は、現状のiOSバージョンに対応していない場合の対応を記載します。
## 手順2
まず、Xcodeを開ききます。
その後、Macの場合「command,」でPlatforms画面を開いて、左下のプラスボタンを押します。
その後、`iOS`をクリックしてインストールしたいiOSのバージョンインストールします。
インストール後、手順1に戻ることで対象端末をインストールできるはずです。


以上、iOS simulatorを追加する方法でした。
AWS CLF入門 - コンピューティング編

## はじめに
AWS CLF (AWS Certified Cloud Practitioner) を取得した際の学習記録をアウトプットとして以下に記す。

## リンク
[ネットワーク](https://qiita.com/yashiuri/items/31b2c9c3c49c2584cd01)
[セキュリティ](https://qiita.com/yashiuri01/items/73e5310b09ff41912862)
[コンピューティング](https://qiita.com/yashiuri01/items/9eaf71a1590e7815f9ef)
[コンテナ](https://qiita.com/yashiuri01/items/708b57ee179ae29e72a9)
[スケーリング](https://qiita.com/yashiuri01/items/79050093aff66b0f0665)
[ストレージ](https://qiita.com/yashiuri01/items/6d48d81f5a1651dcc999) 
[データベース](https://qiita.com/yashiuri01/items/159ea63b197f7a836d1c)
[メッセージ](https://qiita.com/yashiuri01/items/d98772b540ef2201c4e2)
[モニタリング](https://qiita.com/yashiuri01/items/64f6dbf38603cdb4828a)
[デプロイ](https://qiita.com/yashiuri01/items/85151311897d6afd429c)
[コストとサポート](https://qiita.com/yashiuri01/items/bac23130a43d62677330)
[移行とイノーベーション](https://qiita.com/yashiuri01/items/6e327034a7c50d56ca88)
[AWS用語集](https://qiita.com/yashiuri01/items/bd36c99623ca596b76e4)


## コンピューティング
### 概要
EC2について記載する。

- 安全でサイズ変更可能なコンピューティング
 - VPCとサブネット、配置するAZを決めて作成する
- 起動し、削除するまで時間単位で課金される(アイドル状態でも課金される)

### Amazon Machine Image (AMI)
- EC2インスタンスの構築に必要な情報がまとまってる起動テンプレート
- リージョン間でのAMIのコピーをする前にソースAMIの全てのコンテンツが、異なるリージョンでの実行をサポートするように更新されていることを確認する必要がある
- インスタンスタイプ

### EC2の種類

| 汎用 | APサーバー<br>Webサーバー<br>バックエンドサーバー<br>コードリポジトリ |
| --- | --- |
| コンピューティング最適化 | ゲームサーバー<br>HPC (High Performance Computing)<br>科学モデリング<br>バッチ |
| メモリ最適化 | 高パフォーマンスDB |
| 高速コンピューティング | 浮動小数点の計算<br>グラフィック処理<br>データパターンマッチング<br>ストリーミング |
| ストレージ最適化 | ローカルに保存されたデータのハイパフォーマンス<br>分散ファイルシステム<br>データウェアハウジング |

### 料金
従量課金性
- **オンデマンドインスタンス**
    - 起動するインスタンスに対して秒単位での支払い
    - 不規則で短期的なワークロードに適している
- **Savings Plans**
    - 1年か3 年の期間、1 時間 あたり USD 単位で一定の使用量を契約することにより、Amazon EC2 にかかるコストを削減
    - 期間をコミットする事でオンデマンドに比べて最大72%料金を削減
- **リザーブドインスタンス**
    - 1年か3 年の期間、インスタンスタイプとリージョンを含むインスタンス設定を維持する契約により、Amazon EC2 にかかるコストを削減
    - コンバーティブル
       - 必要に応じてリザーブドインスタンスの属性を交換可能
    - スタンダード
- **スポットインスタンス**
    - 未使用の EC2 インスタンスをリクエストすることで、Amazon EC2にかかるコストを大幅に削減
    - 開始時刻と終了時刻が定まっていないワークロードや、中断可能なワークロードに最適
    - 最大90%料金を削減
    - 一定のコンピューティング使用量に対する契約やコミットメントを必要としない
- その他
    - EC2のAWS無料利用枠はアカウント作成日から12ヶ月
基本情報技術者試験受験体験記

:::note alert
これは大体1年前の受験記です。
:::

# 結論
- 現役エンジニアだけどアホだから普通に大変だったし難しかったよ。
- 教科書・参考書の読み込みは、「確かこのあたりにXXXの記載があったな〜」くらいが把握できるくらいでOK！
- 過去問を解いて知識・解き方を覚える。
- 間違った問題に印をつけて、ある程度時間が経ったら、再度同じ問題、もしくはその問題を含んだ試験ごと解き直してみる。
- 週に1日くらいは休んでもヘーキヘーキ（）

# 受験者の簡単なプロフィール
- 学歴：　四年制大学の文系学部卒
- 年齢：　二十代後半
- エンジニア歴：　3年程度
- エンジニア系の資格：　一切なし
- 普段使う言語：　PHP
- 好きな動物：　猫

# 勉強した期間・ペース
1. 2022年の7月〜2023年4月中旬まで
→ この間に引っ越しが2回あり、１ヶ月半〜2ヶ月程度勉強できなかった時期があった
2. （~~平日勤務しながらだったので~~ 勉強嫌いで怠惰なので）1日平均1〜2時間
3. 気分が乗らなくてやらない日もあった
4. 忘れてしまってやり直した範囲もあった
＝ 大体7, 8ヶ月くらいかかった


# 使った教材
大体Amazonで評価とコメントを見て買いました。

:::note warn
こちらに記載したものは、実際に購入した版のものになります。
参考にされる方はなるべく最新のものをご購入ください。
:::

- [令和04年 イメージ&クレバー方式でよくわかる 栢木先生の基本情報技術者教室（著：栢木 厚）](https://amzn.asia/d/eqS4n7H)
→ 主に科目A対策として使用しました。
- [うかる！ 基本情報技術者　[科目B・アルゴリズム編]　2023年版 福嶋先生の集中ゼミ（著：福嶋 宏訓）](https://amzn.asia/d/gEdhDbz)
- [うかる！ 基本情報技術者　[科目B・セキュリティ編] 2023年版（著：福嶋 宏訓）](https://amzn.asia/d/2Wfd9hr)
→ 科目B対策として購入・使用しました。
- [令和04年【下期】基本情報技術者 パーフェクトラーニング過去問題集 (情報処理技術者試験)](https://amzn.asia/d/7XsFXcD)
- [令和05年【上期】基本情報技術者 パーフェクトラーニング予想問題集](https://amzn.asia/d/2pxGtKR)
→ 過去問題として使用しました。

:::note info
問題集系は掲載されている（もしくは付録としてダウンロードできる）試験年数が多いものが良いと思います。
:::

:::note info
解説が少なくても、googleで試験年度・春秋・A/Bもしくは午前/午後、該当の問題番号で検索すれば色々出てきます。
むしろ教材の解説よりも質問サイトやYoutubeの解説の方が理解しやすいことも多かったです。
:::


# 過去問の解き方
某チェーン系学習塾のお作法に自的アレンジを加えました。

ルールとしては以下を気がすむまで繰り返す感じです。

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

記号分類：
普通に解けた／自信あるぜ！　→ ◯（緑）
凡ミスで不正解／正解してたけど勘で解いたやつ→ △（黄）
普通に不正解→ ×（赤・ピンク）

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

解き方：
1. まず普通に問題を解く
2. 丸つけする
3. 下記の分類で問題番号の横に記号をつける、もしくは色をつける
4. いつか忘れた頃（お好きな期間後）に◯（緑）以外の問題を解く
5. 間違えた問題のみを対象に解き直す
6. 全部◯（緑）になるまで5を繰り返す
7. 1に戻ってやり直し

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

:::note info
教科書を繰り返し読んで解き方を覚えるより、過去問を繰り返した方が解き方の覚えや解くスピードの向上が良い傾向にありました。
:::


# 結果+所感
合格。
A： 700点代
B： 800点代

７割8割取れればいいな、と思っていたので、その通りになってよかった。
Aの方が点数低くてびっくりした。だが、Bが例年通りだったら６割取れるかだったので、めちゃくちゃホッとした。
また、Bはプログラマーであれば、問題のソースをためつすがめつして、なんとなく流れからこれかなー？でも解ける難易度と思った。
【Python】`0001_output.log`から`1000_output.log`まで連番のファイルを瞬時に作成する方法

# 概要
空のファイル`0001_output.log`から`1000_output.log`を連番で作成する必要性が生じたので、Pythonプログラムで実装しました。

今回は、ENVファイルのようなもので作成回数や作成ファイル名を制御できるようにしたかったので、コードの中にそれらを埋め込まないように工夫しました。サンプルコードを紹介します。

# サンプルコード

```py
import os

output_logs_dir = 'output_logs'
number_of_file = 1000
log_name = '_output.log'

# outputファイルをnumber_of_file個作成する
for i in range(1, number_of_file + 1):
    filename = os.path.join(output_logs_dir, f"{i:0{len(str(number_of_file))}}{log_name}")
    open(filename, 'a').close()
```

上記コードを実行すれば、空のファイル`0001_output.log`から`1000_output.log`までの連番ファイルを一瞬で作成できました。

## コード解説
`filename = os.path.join(output_logs_dir, f"{i:0{len(str(number_of_file))}}{log_name}")`の部分では、ファイル名を作成しています。

`os.path.join`という関数を使って、ディレクトリ名とファイル名を結合、さらにゼロ埋めされた数値と指定した`log_name`を組み合わせる形にしています。

最後の`open(filename, 'a').close():`の`'a'`オプションは、ファイルを追記モードで開くという意味。ファイルが既に存在する場合はそのままで、存在しない場合は新規作成します。
`close()`によってすぐ閉じているのは、リソースを解放するため。ファイルを開いたままにするより閉じた方がパフォーマンスは良くなるため、一般的にこのような記載をするようです。

ただし、`close`を書き忘れる、というリスクもあるため`with`文を使ったコードもよく見られます。`with`ブロックが終了する時に自動的に`close()メソッド`が呼び出されるため、`close`を書く必要がありません。その場合は以下のように書きます。

```py
with open(filename, 'a'):
    pass
```

`with`文は、データベース接続とカーソルの作成・クローズを自動的に処理する際にも登場しますね。

## 補足

上述の通り、今回はPythonプログラムを利用しつつENVファイルで調整できるようにするため、上記のようなサンプルコードにしました。

単純にスクリプトファイルで作成することもできます。

```sh
#!/bin/bash

for i in {1..100}; do
  touch $(printf "%03d_output.log" "$i")
done
```

`100`個の`output_log`ファイルを作成する場合は上記の通り`touch`コマンドで可能です。
`for i in {1..100}; do`で`for`ループを始め、`done`で終了します。

`printf`コマンドは、第一引数のフォーマット文字列に対して、それに続く引数を順に代入するコマンド。`%03d`は整数を3桁のゼロ埋めで出力することを指定するフォーマット指定子です。

つまり、例えば、以下のようになります。
・`printf "%03d" 7` → 出力は`007`
・`printf "%03d" 5 2` → 出力は`005002`
・`printf "%03d"` → 出力は`000`

また、対象ファイルの中身に何か書いてあるとして、同じ内容のまま、ファイル名を連番で複製していく方法もあります。以前記事にしたのでよければご参考までに。

https://qiita.com/Ryo-0131/items/3df3a9ce2e4286e5f5af
学校コード検索APIを叩く

```python
import requests
def call_api():
    url = "https://api.edu-data.jp/api/v1/school"  # ここにAPIのエンドポイントを入力します
    headers = {"Authorization": "Bearer ここにトークンを入力"}  # 必要に応じてヘッダーを設定します

    try:
        response = requests.get(url, headers=headers)

        # ステータスコードが200 OKの場合
        if response.status_code == 200:
            # レスポンスから必要なデータを取得します
            data = response.json()
            return data
        else:
            print("API呼び出しに失敗しました。ステータスコード:", response.status_code)
            return None
    except Exception as e:
        print("エラーが発生しました:", e)
        return None

api_data = call_api()
print("APIからのデータ:", api_data)
AIが書いた小説part1

# 「微笑む子どもたち」

ある小さな村に、人々の心を温かくする特別な子どもたちがいました。彼らは皆、何かしらの困難や障害を抱えながらも、生きる喜びを失わず、微笑みを絶やさない子どもたちでした。

その中でも、特に目立つのは障害を持つ少女、ミオ。彼女は生まれつき聴覚と視覚に障害を抱えていましたが、それでも彼女の笑顔はいつも明るく、周囲の人々を勇気づけていました。

村の人々はミオを特別な存在として愛し、彼女が必要な支援を惜しまずに提供しました。ミオの両親も彼女を愛し、彼女が幸せに過ごせるように、日々尽力しました。

ある日、村に新しい家族が引っ越してきました。彼らの息子、ケンジは車椅子に乗っており、足を失ったことで自信を失っていました。最初は不安げな表情を浮かべていたケンジも、ミオの明るい笑顔に触れ、少しずつ心を開いていきました。

ミオとケンジはお互いの存在に救われるような関係を築きました。ミオはケンジに自分の経験を共有し、彼を励まし、ケンジはミオの存在を通じて新たな希望と勇気を見出しました。

その二人の姿は、村人たちに感動を与えました。彼らの微笑みは、村に新たな光をもたらし、困難を乗り越える勇気を与えました。

そして、村の人々は彼らの絆と勇気ある姿勢から多くのことを学びました。障害や困難があっても、心の豊かさと絆があれば、幸せは決して遠くないことを。

村の人々は、ミオとケンジの微笑みが彼らの心に永遠に残ることを知りました。その微笑みは、彼らが前に進む力となり、彼らの心に永遠に輝き続けるのです。
Next.js でルートパスにも headers を反映させる

# はじめに

Next.jsで、ルート以外の画面のレスポンスヘッダに正常に `headers` が設定されているのに、ルートの画面取得時のレスポンスヘッダには `headers` で設定した値が設定されていませんでした。
他にもハマっている人がいそうなので記事に残します。

# 問題

以下の記事などを参考にして `next.config.js` に セキュリティ向上のための `headers` を追加しました。

- https://zenn.dev/heavenosk/scraps/1e0b5d88b7a135
- https://dev.to/victorocna/quick-security-wins-for-your-next-js-app-3l23

```js
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
  compiler: {
    styledComponents: true
  },
  // Next.js i18n docs: https://nextjs.org/docs/advanced-features/i18n-routing
  i18n: {
    locales: ["en", "jp"],
    defaultLocale: "en"
  },
  output: "standalone",
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "xxxxx"
      }
    ]
  },
  headers: async () => {
    return [
      {
        source: "/(.*)",
        headers: [
          {
            key: "X-DNS-Prefetch-Control",
            value: "on"
          },
          {
            key: "Strict-Transport-Security",
            value: "max-age=63072000; includeSubDomains; preload"
          },
          {
            key: "X-Frame-Options",
            value: "SAMEORIGIN"
          },
          {
            key: "X-Content-Type-Options",
            value: "nosniff"
          },
          {
            key: "X-XSS-Protection",
            value: "1; mode=block"
          },
          {
            key: "Referrer-Policy",
            value: "strict-origin-when-cross-origin"
          },
          {
            key: "Content-Security-Policy",
            value: "frame-ancestors 'self';"
          }
        ]
      },
      {
        source: "/sign-in",
        headers: [
          {
            key: "Cache-Control",
            value: "no-store"
          },
          {
            key: "Pragma",
            value: "no-cache"
          }
        ]
      }
    ];
  }
};

module.exports = nextConfig;
```

しかし、ルートパスの画面取得時のレスポンスヘッダには、`headers` で設定した値が設定されていませんでした。

# 解決方法

原因は `source: '/(.*)'` でした。
これは `/(en|ja)/(.*)` に変換されるため、トップレベルの `/` や `/ja` ルートにはマッチしません。

なので、`source: '/:path*'` に変える必要があります。

これで headers が適切に設定されました👌 (ローカルで起動して、 `/` である `http://localhost:3000` にアクセスしています。)

![localhost root.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413529/142a2a12-4ab8-889f-25e0-7f6e670e662b.png)

# おわりに

しっかり確認しないと意外と気づきづらい問題だと思います。
最初に設定されているか確認すること大事ですね。

# 参考

- https://nextjs.org/docs/pages/api-reference/next-config-js/headers#headers-with-i18n-support

```js
      {
        // this gets converted to /(en|fr|de)/(.*) so will not match the top-level
        // `/` or `/fr` routes like /:path* would
        source: '/(.*)',
        headers: [
          {
            key: 'x-hello',
            value: 'world',
          },
        ],
      },
```
【HTML】Youtube動画からサムネイルを取得する方法

## 概要
今回担当した案件の中で、HTML内でYoutube動画を再生させる必要があり、
その際に動画のサムネイルを取得して表示させたかったためです。

今後のためにもメモとして残しておこうと思います！

## 参考記事
- [Youtube動画からサムネイル画像を取得する方法](https://www.billionwallet.com/goods/youtube_image.html)
- [YouTube動画をWebページに埋め込むorリンクを貼り付けるときの便利テクニック](https://4thsight.xyz/26221#YouTube%E5%8B%95%E7%94%BB%E3%81%AE%E3%82%B5%E3%83%A0%E3%83%8D%E3%82%A4%E3%83%AB%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B)
- [YouTubeの動画サムネイルの取得方法](https://logicalyze.com/get-youtube-thumbnail/)

## 作業内容

### 1. Youtubeの動画IDを確認する

動画のサムネイル画像を表示するために、先に動画のURLに含まれている動画IDを確認します。

```https://www.youtube.com/watch?v=``` 以降に書かれている文字列が動画IDになります。

![スクリーンショット 2024-02-15 17.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2669177/99ef0427-3388-d214-66f6-3100b3e20a15.png)


### 2. サムネイル画像のURLパスに動画IDを入れる

次にサムネイル画像のURLパスに、動画IDを埋め込みます。
下記のURL内の ```VIDEO_ID``` を実際の動画IDに置き換えて、HTML内に記載してください。

(※サムネイルURLのパターンとしては5種類あるようです、他にもあるかもしれないです)

- デフォルトサムネイル : (120 x 90)
    - https://img.youtube.com/vi/VIDEO_ID/default.jpg
- 中品質サムネイル : (320 x 180)
    - https://img.youtube.com/vi/VIDEO_ID/mqdefault.jpg
- 高品質サムネイル : (480 x 360)
    - https://img.youtube.com/vi/VIDEO_ID/hqdefault.jpg
- HQ動画サムネイル : (640 x 480)
    - https://img.youtube.com/vi/VIDEO_ID/sddefault.jpg
- FULLHDサムネイル : (1920 x 1080)
    - https://img.youtube.com/vi/VIDEO_ID/maxresdefault.jpg

### 3. ブラウザで確認する
指定したサムネイルがきちんとブラウザ上で表示されているかどうか確認してください。
GoogleSpreadsheetをWPにiframeで埋め込んでサイズ最適化する方法

# 1:はじめに
Google Spreadsheetで生成したグラフをWPの固定記事や投稿記事に埋め込む場合、埋め込みタグを生成するとiframeのコードが生成されるかと思います。

しかし、これをそのまま貼り付けても、デバイス別に横幅が最適化されることはなく、Spreadsheetのグラフの元のサイズがどのデバイスでもそのまま表示されるといった現象に遭遇します。

この解決方法の一つをこの記事では提示します。

# 2:Google Spreadsheetのグラフ（iframe）の準備
以下は、Googlespreadsheetで、WPに連携するグラフのiframeのコードを取得する操作です。
このあたりは本論とは異なりますが、ここで解説しておきます。

①グラフの選択:
まず、Google スプレッドシートを開き、埋め込みたいグラフが含まれるシートを表示します。グラフの任意の部分をクリックし、右上に表示される「…」マークを選択してください。

②グラフの公開:
出現するメニューから「グラフを公開」を選び、「ウェブに公開」画面が表示されたら、「埋め込む」タブを選択し、「公開」ボタンをクリックします。「公開してもよいか？」という確認メッセージが表示されたら、「OK」を選択します。

③埋め込みコードの取得:
公開操作後、表示される埋め込みコードをコピーします。この時、グラフを「インタラクティブ」形式で表示するか、「画像」形式で表示するかを選べます。「インタラクティブ」では、ユーザーがグラフにカーソルを合わせると詳細が表示されますが、「画像」形式では固定画像として表示されます。

④WordPressに貼り付け:
WordPress の編集画面で、グラフを表示したいページまたは投稿を開き、「カスタムHTML」ブロックを追加して、先ほどコピーした埋め込みコードを貼り付けます。プレビューを確認し、問題なければページを公開または更新します。

これで、たしかに、GooglespreadsheetのグラフがWPで投稿はできます。
しかし、実際にやってみると、レスポンシブ対応（スマホでの表示におけるサイズ最適化）がされないことがすぐにわかります。

解決方法は次で解説します。

# 3:サイズ最適化の方法│IMGタグで読み込む
このような現象を解決するために、iframeの元のページ側（つまり今回はGooglespreadsheet）にCSSを埋め込む方法もありますが、今回はGooglespreadsheetなので、そのような対応はできません。

そこで、読み込む側（＝WP）で、できることをします。

以下は実際に、うまくいったコードです。

```
<img width="100%" height="416" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/u/5/d/e/2PACX-1vRETVNZV27LMXp6HzfAjmc5ib76zYCZw9AbSS5S4tRNV_gJDKbR8Mt2Zn4H3foEtrI1l2zggs53qy-Q/pubchart?oid=1638100067&amp;format=image"></img>
```

<img width="100%" height="416" seamless frameborder="0" scrolling="no" src="https://docs.google.com/spreadsheets/u/5/d/e/2PACX-1vRETVNZV27LMXp6HzfAjmc5ib76zYCZw9AbSS5S4tRNV_gJDKbR8Mt2Zn4H3foEtrI1l2zggs53qy-Q/pubchart?oid=1638100067&amp;format=image"></img>

このように、標準機能で提供されるiframeタグではなく、imgタグで呼び出すようにします。
WP上で、iframeでwidth="100%"を指定しても、有効ではありませんが、imgタグでは有効なことが確認できています。


動作環境について確認したい方は、以下のサイトも参考にされると良いかと思います。

▼上記のコードでレスポンシブ対応が確認できたサイト：
https://money-no1.com/

※ギフト券買取業界の比較サイトに飛びます。サービス内容自体は、上記のコードとは無関係です
新卒エンジニアの成長記録: React学習の1カ月間（React要素ゼロ）

# はじめに
はじめまして、A.Kと申します。
現在は**1年目新卒エンジニアとして、運用保守の案件に参画**しています。
**エンジニアなのにプログラミングが全くできないという危機感・フロントへの興味をきっかけに、** JISOUというコーチングサービスに出会いました。
JISOUで先輩エンジニアからサポートを受けつつ、React学習を開始してから今日で1カ月になります。
**技術的な内容ではなく、自分の内面的な変化や感じたこと**をお話しします。
**React要素はゼロです（笑）**
# この1カ月で行ったこと
**普段の運用保守業務ではJavaのログを読むくらいで、自分でコードを書いた経験はほぼありません。「JavaScriptとはなんぞや？」状態から学習を始めました。**

先輩エンジニアに組み立ててもらった学習ロードマップを基に、以下の内容をこなしました。

・Udemy受講（JavaScript基礎＆React基礎）
・Reactで学習記録アプリ作成
・Udemy受講（React応用）
・Reactのローカル環境構築（Viteを使用）
・学習記録アプリのサービス拡張
DB作成（supabase）
Firebaseにデプロイし、スマホからでも利用可能にする
Git上でアプリを CI/CDする

学習記録アプリ作成の様子はこちらです。

https://qiita.com/AK_React/items/785329572561afef58c8

# 感じたこと
駆け出しエンジニアとして1カ月学習を継続して大きく感じたことはこちらです。

**１．課されたタスクを淡々とこなすことで、不安は薄れる**
**２．わからないことが多すぎる状況でも、最初は踏ん張って継続すること**
**３．ChatGPTが自分の欲しい答えを返してくれるとは限らない**
**４．今の時期だからこそ提供できる価値もある**
**５．結局、人である**

# １．課されたタスクを淡々とこなすことで、不安は薄れる
「エンジニアとしてどの分野でキャリアを構築していくのか」「どの言語を重点的に学習していけばよいのか」といった漠然とした不安が自分にはありました。
ですが、**いくら悩んでいても、SNSで情報を漁っていても、正解は出てきません。結局やってみるしかない。そして、やるなら早いほうがいい。**
というわけで、**私は学習すべき分野をフロントエンド、言語をReactに絞りました。**
そのほかの分野は必要に応じて肉付けをしていく予定です。
**自分のやるべきことが定まると夢中になれるので、前述したような不安を感じる隙がなくなりました。**
# ２．わからないことが多すぎる状況でも、最初は踏ん張って継続すること

暇つぶしにふと開いた本に、このようなことが書いてありました。
**「私たちは、勉強を通して自分の脳をつくり変えています。
あきらめず繰り返すことで、脳に新しい回路ができて、最初は高度に思えたことも、必ず鼻歌まじりでできるようになります。」**

https://www.amazon.co.jp/%E4%BD%95%E6%AD%B3%E3%81%8B%E3%82%89%E3%81%A7%E3%82%82%E7%B5%90%E6%9E%9C%E3%81%8C%E5%87%BA%E3%82%8B-%E6%9C%AC%E5%BD%93%E3%81%AE%E5%8B%89%E5%BC%B7%E6%B3%95-%E6%9C%9B%E6%9C%88-%E4%BF%8A%E5%AD%9D/dp/4799111205

つまり、**わからない・しんどい＝適性がないとは必ずしも限らない**ということです。
**誰だって最初がしんどいのは同じです。**
今は少し踏ん張って続けようと思えました。
# ３．ChatGPTが自分の欲しい答えを返してくれるとは限らない
ChatGPTはとても便利ですが、**聞き方によっては「それっぽい答え」を返すこともあります。**　特に、質問の内容や文脈が不明確だったりすると、適切な回答を返すことは保証されません。
**特に初学者は前提知識や経験の不足から、明確な問いを立てるのが難しかったりします。結局わかる人に聞いたほうが早かったりするので、うまく使い分けたいです。**
# ４．今の時期だからこそ提供できる価値もある
**エンジニアに技術力は必要だけど、それがすべてではない**はずです。
今自分は駆け出しエンジニアとして学習に苦戦していますが、その**様子を発信することで、同じ状況の人を勇気づけることができます。**
**技術面では貢献できないからこそ**、積極的に学習の過程で感じたことを発信したいです。

# ５．結局、人である
**もし一人で学習を始めていたら、絶対折れていた**と思います。
先輩方のサポートがあったからこそ、ここまでこれました。

ただ、**先輩方からしても、初学者の学習サポートってかなり骨の折れる作業**だと思います。そこで、先輩に対して以下の点を意識していました。

**学習過程で生まれたポジティブな変化はこまめに報告する（感謝とセットで）**

私は学習に関してあまり要領が良くないので、大きな成果が出るまでに時間を要するタイプだと思います。。なので、こういった小さな変化をこまめに伝えることで、**先輩方には「この子に教えてよかった！今後もいろいろ教えてあげたい！」って思ってもらえたらいいなって（笑）**
# おわりに
以上、React学習を一週間続けてみて感じたことでした。
**今はわからないことだらけでしんどくても、毎日頑張ってるんだからいつか大丈夫になるやろ！！くらいの精神でいきましょう。**
一緒に頑張りましょうね！！
ReactでFontAwsomeを使う @yukilulu0229

https://fontawesome.com/

## install

```
npm install --save @fortawesome/react-fontawesome
npm i --save @fortawesome/fontawesome-svg-core
npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons
npm i -S @fortawesome/free-brands-svg-icons

```
全部

```
npm i -S @fortawesome/fontawesome-svg-core @fortawesome/react-fontawesome @fortawesome/free-regular-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/free-brands-svg-icons
```

## 使う

```Sample.tsx
// import React from 'react';

import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faBars} from "@fortawesome/free-solid-svg-icons";

const Portfolio = () => {
  return (
    <div className="md:hidden">
      <FontAwesomeIcon icon={faBars} />
    </div>
  );
};

export default Portfolio;

```

コードのように使う
SequelProデータベースへSSH接続例

MySQLにはphpMyAdminと言う便利なツールがありますが、
アプリとして使えるSequelProデータベースへSSH接続を試してみました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101462/c5779366-b717-4a18-8a58-95d89667b1f8.png" width="60%">
ssh接続キーを指定して接続、GUIで使える非常に便利なツールです。
因みに、PostgreSQL用のPSequelと言うツールも使い方によっては便利のようです。
DjangoのForeignKeyで親のフィールドを参照

Djangoで外部キーを使うにはmodelに一対多のForeignKey 一対一のOneToOneFieldを指定する。
ここでは親モデルのUser, 子モデルのDataを作成することを考える。

# データベース定義

| User               |Data                |
|:------------------:|:------------------:|
| id (primaary key)  |id (primary key)    |
| user_name          |user (foreign key)  |
|                    |text                |


# 子モデルから親モデルを参照したい
例えばuser_nameがadminのDataを選択したいときdjangoでは2つの書き方がある。
```python:python
Data.objects.filter(user=User.objects.get(user_name="admin")) #1
Data.objects.filter(user__user_name="admin") #2
```

#1はuserにUserモデルのオブジェクトを代入していて#2は __ を用いてuserの親クラスのフィールドを指定している。
それぞれ以下のようなSQL文が発行される
```SQL:SQL
SELECT "data"."id", "data"."user", "data"."text", FROM "data" WHERE "data"."user_id" = 1　/* #1 */

SELECT "data"."id", "data"."user", "data"."text", FROM "data"
       INNER JOIN "user" ON ("data"."user_id" = "user"."id") 
            WHERE "user"."user_name" = "admin" /* #2 */
```

それぞれクエリを実行する時間を計測するとSQL文からも推測できるように
#1 : 0.0015730000004623435
#2 : 0.004858499999500054

となった。以上より#1を用いた方がよさそう。