OmniAICreator/Qiita-1.07M huggingface dataset:
Yarn Spinner 2.x のカスタムコマンドをasync/awaitで記述する方法

Yarn Spinner 2.xのカスタムコマンドでは戻り値にIEnumeratorが使用可能です。
https://docs.yarnspinner.dev/using-yarnspinner-with-unity/creating-commands-functions#making-commands-using-coroutines

そこで、UniTaskのToCoroutine()を用いることでasync/awaitの書式でコマンドを実装できます。

```cs
	public class MyCommands
	{
		[YarnCommand("MyCommand")]
		public static IEnumerator MyCommand()
		{
			yield return UniTask.Create(async () =>
			{
				// ここに処理を書く。awaitも使える。
				await UniTask.Delay(200);
			}).ToCoroutine();
		}
	}
```
AWS案件の現場初日から学んだこと

どうも、黒田です。

７月になりましたね。
本日初めて、現場に配属されている方が多くいらっしゃるでしょう。
私もその中の一人で、AWS基盤構築案件の現場に入場しました。
ワクワクしながら、バタバタ一日でしたね。:sweat_smile:
幸いなことに、とても思いやりを感じられている優しいPMの方で、良かったです。:relaxed:
![allanbm_positive_change_idea_up_technolgy_in_vector_style_illus_67d14ecc-ff34-4fe4-a86d-7895ec109024.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493593/fc68c7a6-0ec5-32e3-00c5-261317bf3eff.png)
初めてAWS基盤構築現場に配属された方にとって、初日は新しい環境に慣れるための重要なスタートです。以下に、初日に注意すべきポイントをまとめました。:point_down:

### 1. ドキュメンテーションの確認

プロジェクトには様々なドキュメントが存在します。初日には、プロジェクトのドキュメンテーションを確認しましょう。アーキテクチャ設計、ネットワーク構成、セキュリティポリシーなどの重要な情報を把握し、現場での作業に役立てましょう。

### 2. 環境の理解

AWS基盤構築において、配属された環境の理解は重要です。現場で使用されているAWSサービスやツールの機能や使い方について学び、基本的なコンセプトや用語に慣れるようにしましょう。AWSのマネージメントコンソールやCLIの操作に慣れるため、実際に手を動かして試してみることもおすすめです。

### 3. セキュリティに関するルールの遵守

AWS基盤構築においてはセキュリティが非常に重要です。セキュリティポリシーやガイドラインに従い、機密情報の取り扱いやアクセス制御に留意しましょう。IAMロールやセキュリティグループなどの設定を正確に行い、セキュリティの脆弱性を最小限に抑えるようにしましょう。

### 4. コミュニケーションの重要性

AWS基盤構築プロジェクトでは、複数のチームや関係者と連携して作業を進めることがあります。初日からコミュニケーションを積極的に取り、チームメンバーや上司とのコミュニケーションを密に保ちましょう。分からないことがあれば遠慮せずに質問し、助けを求めることも大切です。

### 5. プロジェクトの目標と役割の把握

初日には、プロジェクトの目標や配属された役割について明確に把握しましょう。プロジェクトのビジョンや期待される成果物を理解し、自身の役割をしっかりと果たすための計画を立てましょう。また、プロジェクトメンバーとの連携やタスクの優先順位を考慮しながら、効果的なスケジュール管理を行いましょう。

### まとめ
初日は新たな環境に慣れるためのスタート地点です。
上記のポイントに留意しながら、環境の理解とプロジェクトへの貢献に取り組んでください。
素晴らしい初日をお過ごしください！
![eukolos_computer_connected_to_many_external_software_1158d5ff-8120-461e-95b4-666094df9917.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493593/5794316e-c52a-18fd-8155-0f3431e65bc7.png)
AWSのEC2インスタンス内にインストールされたMySQLから指定のSELECT文結果を、CSVでS3に保存した話

# 概要
もうそのまんま、AWSのEC2インスタンス内にインストールされたMySQLから指定のSELECT文結果をS3に保存した話、です。「なぜEC2内にMYSQLをインストールしてわざわざ使っているのか」などについては触れないでほしいところです。

誰かのためにというよりは、自身のメモとして残しておきます。

# やったこと

## AWS-CLIのインストール

```bash
sudo yum install awscli
```

## SELECT文の実行結果がどこに書き出されるか見る

```sql
SHOW VARIABLES LIKE 'secure_file_priv';
```

## SELECT文を実行し書き出す

```sql
SELECT *
INTO OUTFILE '/var/lib/mysql-files/sample.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM hoge_table
WHERE ...
```

## バケット上に書き出したファイルをアップロード

```bash
aws s3 cp /var/lib/mysql-files/sample.csv s3://（バケット名）
```

# 終わりに
相変わらずちょっとしたことを知らないのをGPTに助けてもらう日々。
dbt Cloud を学んでみる - セットアップしてみました

dbtについて学習する機会があったので、まとめてみたいと思います。

# dbtとは
dbtは、データ分析におけるELT（抽出、ロード、変換）プロセスの『T』の部分、つまりデータ変換処理について、支援してくれるツールです。

データ分析開発のワークフローは、サイロ化を生むエンジニアの孤立や手作業の多さなどの問題を抱えがちで、最適とは言えない状況であることが多いそうです。一方、既にソフトウェアエンジニアリングの方ではモジュール化やCI/CD、バージョン管理、文書化といった ベストプラクティス と呼ばれるものがあります。

dbtを使用することで、それらの手法をデータ分析の現場にも取り入れることが可能になるのだそう。

今回は UI で操作できる dbt Cloud について、[dbtのクイックスタート](https://docs.getdbt.com/docs/quickstarts/dbt-cloud/snowflake)に沿って、基本の操作を一通り動かしてみました。


# セットアップしてみた
まずは[dbtのクイックスタート](https://docs.getdbt.com/docs/quickstarts/dbt-cloud/snowflake)の通りにセットアップしてみます。

初期状態で dbt Cloud にサインインすると、セットアップ画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/9dbd3865-8149-647d-32f0-ed0fdc1b3e15.png)


そのまま画面に沿って下記を設定していくことで、セットアップが完了します。

1. 接続先データプラットフォームの種類を選択
2. 接続先情報を入力
3. Gitリポジトリとの接続を作成

## 1. 接続先データプラットフォームの種類を選択
今回はSnowflakeに接続するため、Snowflakeを選択して進みます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/ab30efd5-b2b7-d1aa-c51e-a76e3c569ddc.png)

## 2. 接続先情報を入力
Snowflakeの接続情報を求められるので、入力していきます。
* コネクション名
* Snowflakeのアカウント
* 接続先のデータベース 
※dbtが生成するテーブルまたはビューの出力先となるデータベース
先にSnowflake側で「analystics_db」というデータベースを作成しています
* 使用するウェアハウス
先にSnowflake側で「dbt_learning」というウェアハウスを作成しています

以下は、開発者ごとの認証情報
* Snowflakeの認証方法
* Snowflakeのユーザー＆パスワード
* 開発環境からの接続先スキーマ 
※開発者ごとの開発環境から生成するテーブルまたはビューの出力先となるスキーマ
事前に未作成でも、dbtが実行時に作成してくれます
* その他：デフォルトのままでOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/09d4de8c-678a-a415-46c5-9eaf89230558.png)
　　↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/c401dbcd-b858-6657-6ddb-476095906454.png)
　　↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/dc93df76-3c84-183c-cd45-1787128be1ae.png)

## 3. Gitリポジトリとの接続を作成
作成したファイルを管理するリポジトリを選択します。
今回はGitHubを選択しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/b99a7e46-b2d5-7395-494c-126d855f9e0f.png)

まず、GitHub側でリポジトリを作成し、ログインした状態で↓のボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/0d7f0609-97df-7a9b-4d02-35756bc04bf0.png)
　　↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/9b749f5c-19f3-c7cb-4e40-41e6b410c9e0.png)

　　↓

すると、またセットアップ画面に戻ってきたので、もう一度リポジトリ設定のところに行きます。
ボタンが変わっているので、押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/01f4b2b2-0224-c613-f652-af89bbce8e2a.png)
　　↓

githubアカウントにdbt Cloud Appをインストールします。
アクセスを許容する範囲が選択できるようだったので、dbt用に作成したリポジトリだけを指定してみました。問題なさそうでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/f2c37852-21a6-2c8c-7cca-f802ad72c5db.png)

　　↓

セットアップ画面に戻ると、紐づけたリポジトリが選択できるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/e556cbd2-dc5b-06e0-105c-1a79326210c9.png)

　　↓

これで、githubリポジトリとの連携が完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333083/55bc04bc-3376-4b9e-f9d1-2bdc7ca32655.png)

---
ドキュメントと読み合わせながら進めましたが、基本的には画面に従っていればよく、簡単にセットアップができました。
次は、プロジェクトを開始してモデルを作成してみたいと思います。
基本情報対策：科目B「バブルソート」

<h2>バブルソートとは</h2>
隣接する値どうしの比較、入れ替えを繰り返すことで<br/>
値を大きい順または小さい順に整列させる方法。

<h2>主に以下のような状況で使用(Chat-GTP回答)</h2>
<p>
<strong>学習と教育目的：</strong>
バブルソートは非常にシンプルなアルゴリズムであり、ソートの基本的な考え方を理解するための教育的な目的で使われます。アルゴリズムとしての基本的な動作原理を理解するために、初心者にとっては有用な例となります。

<strong>小規模なデータセット：</strong>
データセットの要素数が少なく、性能の最適化が重要でない場合に、バブルソートが使用されることがあります。シンプルな実装が容易であるため、簡単なソート処理に適しています。

一方で、バブルソートは大規模なデータセットには適していません。その理由は、バブルソートの時間計算量がO(n^2)であるため、要素数が多くなると処理にかかる時間が急激に増加してしまうからです。そのため、効率的なソートアルゴリズム（例：クイックソートやマージソートなど）を使用することが一般的です。

バブルソートは実用的なソートアルゴリズムとしてはあまり使われないものの、教育や学習の目的で理解する価値はあります。アルゴリズムとしての基本的な概念を理解し、他のソートアルゴリズムとの比較を通じてその効率性や利点を学ぶことが重要です。
</p>

<h2>ソースコード</h2>

```java:Main.java

import java.util.Arrays;

public class Main {
    public static int[] bubbleSortMachine(int[] array){
        int tmp;
        int length = array.length;

//  外側のループは要素を1つずつ減少させていく
        for(int i=0; i<length-1; i++){
            for(int j = length-1; i<j; j-- ){
                //前後の要素の比較を行い入れ替えるシンプルな構造
                if(array[j]<array[j-1]){ 
                    tmp = array[j-1];
                    array[j-1] = array[j];
                    array[j] = tmp;
                }
            }
        }
        System.out.println(Arrays.toString(array));
        return array;
    }
}

```


```java:MainTest.java

import org.junit.Test;


import static org.junit.Assert.assertArrayEquals;

public class MainTest {
    @Test
    public void testBubbleSortMachineForArray() {
        // テスト用のデータ
        int[] array = {10,9,8,7,6,5,4,3,2,1,0};

        // 期待される結果
        int[] expected ={0,1,2,3,4,5,6,7,8,9,10};

        // テスト対象のメソッドを実行
        int[] sortedArray = Main.bubbleSortMachine(array);

        // 期待される結果と実際の結果を比較
        assertArrayEquals(expected, sortedArray);
    }
}
```
コンピュータが小数点数の計算を間違える理由

小数点数の計算を行った場合、プログラムの実行結果として正しい値が得られないことがある。
その理由と回避する方法について学習

## コンピュータが計算を間違う理由
例として、1011.0011という小数点を持った2進数を10進数に変換する。

整数と同じように、小数点のお部分にも重みをかけて結果を加えれば求まる。
よって、11.1875となる。
2進数で表された小数点を10進数に変換する方法が分かれば、コンピュータが計算を間違える理由を理解できる。
その答えは「10進数の小数点数の中には、2進数に正確に変換できないものがある」から。
小数点以下4桁の2進数で表せる数値の範囲は、0.0000~0.1111となる。
だから、2進数の小数点数以下の重みである0.5、0.25、0.125、0.0625という4つの値の組み合わせの小数点数でしか表せない。
最終的には循環小数となり、プログラムで正しい計算結果が得られなくなる。
## 正規表現とイクセス表現
**正規表現**: 様々な形式で表せる浮動小数点数を統一的な表現にするための工夫。例えば10進数の0.75という小数点数なら下記のいずれの方法でも表現できるが、
0.75 = 0.75 * 10^0 
0.75 = 75 * 10^-2
0.75 = 0.075 * 10^1
同じ数値を表すのに様々な表現方法があったのでは、コンピュータで処理するのが面倒になるので、ルールが必要。
例として、10進数の浮動小数点数なら「小数点以上は0として、小数点以下の1桁目は0でない値にする」というルールを決める。このルールに従えば、0.75は「0.75*10^0」という方法だけでしか表現できなくなる。
このようなルールに従って小数点数を表すのが正規表現。
**イクセス表現**: 指数部で表せる範囲の中央の値を示す工夫。例として1~13にトランプのカードを使ってマイナスの数を必要とするゲームを考案する。
この場合、中央の7というカードをゼロとみなすルールを適用する。7がゼロなので、10は＋3、3は-4を表すことになる。
セッションスコープ

## セッション
セッションは、Webアプリケーションにおいてユーザーの情報を保持する仕組みです。各ユーザーに一意のセッションIDが割り当てられ、セッションIDを使用してユーザーを識別します。

セッションの仕組みは次のような手順で行われます
１.サーバー側がクライアントに対して、セッションIDを含んだクッキーを送信します
２.クライアント（ブラウザ）は受け取ったクッキーを保持します
３.その後、クライアントがサーバーにリクエストを送信する際には、クッキーの情報が自動的にHTTPリクエストに付与されます。

上記の手順により、セッションIDを使用してサーバーはユーザーを識別し、ユーザーごとの情報を保持することができます。

セッションスコープはjavax.servlet.http.HttpSessionインスタンスであり、インスタンスメソッドを使用して、セッションスコープを操作することができます。
```java
Animal cat = new Animal();
cat.setName("kokoa");

//セッションスコープをセット
HttpSession session = request.getSession();
session.setAttribute("animal",cat);

//セッションスコープの取得
Animal animal = (Animal)session.getAttribute("animal");

//セッションスコープからインスタンス削除
session.removeAttribute("animal");
```
JavaScriptでオブジェクト初期化時に特定のプロパティを条件によって省略する

## 要旨
オブジェクト初期化で条件によってプロパティを省略したい場合は以下のように書ける。

``` js
function sample(foo, bar, baz) {
    return {
        ...foo ? { foo } : {},
        ...bar ? { bar } : {},
        ...baz ? { baz } : {},
    };
}

console.log(sample('hoge')) // { foo: 'hoge' }
console.log(sample('hoge', 'fuga', 'piyo')) // { foo: 'hoge', bar: 'fuga', baz: 'piyo' }
```
三項演算子の結果をスプレット構文で戻り値となるオブジェクトに展開している。

## 補遺
なお、以下の実装と得られる結果は同じである。

``` js
function sample(foo, bar, baz) {
    const obj = new Object();
    if (foo) obj.foo = foo;
    if (bar) obj.bar = bar;
    if (baz) obj.baz = baz;
    return obj;
}
```

だが、この実装には個人的に次のような不満があった。

- ワンライナーではない
- オブジェクト初期化後にプロパティを書き換えている
アクセスカウンタの作成

ダイアルアップ、プロバイダで日本にインターネットが普及し始めた頃、
ホームページを作って最初に組み込むプログラムがアクセスカウンタでした。

outsystemsで適当にアクセスカウンタを作成していたら、
Service StudioからData Actionにしなさいと波線を付けられてしまいましたので、
Data Actionを作成してみました。

まずカウンタ値を格納するEntityを作成します。

Databaseを右クリックし、Add Entity、名前をCount、
Countを右クリックし、Add Entity Attributeを選択、名前をCount、Data TypeはIntegerにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/b894fe31-b522-1583-ce62-20567ffcad16.png)

Entityの方のCountを右クリックし、View or Edit Dataを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/7ddf7940-4bd7-a8a6-e0f0-9a1e2b46f9fb.png)

Add rowを押して、Countに0、Applyを押します。
（この時のAuto Numberは後で使うので覚えておいてください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/758c8562-943f-3bb8-57d1-d31978532443.png)

カウントしたいScreenを右クリックし、Fetch Data from Other Sourcesを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/64bc8c6c-8c6e-9b4f-b166-7df01cd038c0.png)

作成されたData Actionを右クリックし、Add Output Parameterを選択。
数値を返すのでData TypeはIntegerにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/12000163-9970-70e3-ced6-b8636feb4e76.png)

DataAction1をダブルクリックします。

DataタブからGetCountForUpdateを配置し、IdをIntegerToIdentifier(1)にします。
（注：カッコ内の数値はAdd rowした時にAuto Numberで振られた番号にしてください。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/a2d6c70e-4afd-3bc7-783e-accf91abaa72.png)

GetCountForUpdateの下に、Assignを配置、
GetCountForUpdate.Record.Count.Count
に
GetCountForUpdate.Record.Count.Count + 1
でカウントアップするようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/f4f3b137-d687-d194-d652-2074b73dc6e9.png)

もう一つAssingを配置し、このData Actionの戻り値、Out1にカウントアップされたGetCountForUpdate.Record.Count.Countをセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/19570538-4d92-5502-1198-8803ef48ad1e.png)

DataタブからUpdateCountを配置、Entityを更新します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/355179cf-f7da-12dc-925a-2c2b5872e614.png)

最後、Screenにカウンタ値を表示するExpressionを配置し、Valueを
If(DataAction1.Out1=0,"",DataAction1.Out1)
にします。
DataActionが値を返すまでの間、0が表示されるのが気になったので、
DataAction1.Out1=0の時は表示されないようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254659/0650154a-6f16-1831-de1f-5d49a7af21a9.png)
セキュリティゲートウェイとは？その仕組み・機能について解説

セキュリティ、ネットワークの信頼性を評価する際に重要視された項目の一つです。技術革新に伴って、サイバー攻撃やネットワーク侵害も日々深刻かつ頻繁となっています。規模や業種によらず、起業したばかりの会社でも大手企業でも、情報漏えいや機密データ流出のような危機に晒されています。それを防ぐために、安全性かつ安定性の高いセキュリティ対策を打つ必要があります。新しい**次世代多機能型セキュリティゲートウェイ** の完備したセキュリティにより、ネットワークの安全性が確保されます。

https://www.fs.com/jp/products/108710.html

https://www.fs.com/jp/products/90594.html

https://www.fs.com/jp/products/11552.html

https://www.fs.com/jp/products/40180.html
## 企業が直面している「セキュリティ脅威」
企業及び機関組織から収集したアンケートの結果によると、「ネットワークセキュリティに関係する研究・対策・情報は年々複雑になっている」と思われる割合は高いです。モバイルデバイス、ユーザー数、クラウドアプリ、ネットワークトラフィックなど、通信時代の産物の爆発的増加に伴って、ネットワークが攻撃を受ける可能性も増え続けています。特に新型コロナウィルスの収束がまだ見当のつかない現在では、ネットワークセキュリティの防御力が試されます。大手企業だけではなく、中小・中堅企業も同じチャレンジに直面しています。むしろもっと厳しい状況に向き合わなければなりません。**セキュリティ意識に欠けるため、ハッカーのサイバー攻撃に襲われ、機密が流出したり、個人情報が漏洩したりすることは大勢存在します。** ITセキュリティ部門を持たない企業はなおさらです。不正サイトを識別できないせいで、知らずのうちに情報が盗まれる可能性は格段と高いです。ネットワークセキュリティを強化するために、意図的な不正トラフィックを拒絶・監視したり、従業員のインターネット利用・サイトアクセス行為を管理したりすることは企業がよくとるセキュリティ対策です。では、いかに効率的に不正トラフィックを識別・監視し、ネットワーク全体のセキュリティを把握するのかについて、FS セキュリティゲートウェイの紹介を兼ねて説明します。
### ステートフルファイアウォール機能——ネットワークの第１防衛ライン
企業データベース・機密情報は会社の命だといっても過言ではありません。データベースに基づいて運営する企業にとってはなおさらです。ステートレスファイアウォールに比べて、ステートフルファイアウォールは随時に接続状態をチェック・追跡し、トラフィックやデータパケットを検査しています。ファイアウォールを強化したことで、FS SG-5110多機能型セキュリティゲートウェイはお客様の情報・データを守ることができます。また、**ARPスプーフィング（ARPプロトコルの応答を偽装することにより、LAN上で通信機器のなりすましを行う技法）への完備した対策を備えたFS SG-5110セキュリティゲートウェイはDDoS攻撃（Distributed Denial of Service Attack、サービス拒否攻撃）を有効に防御するため、高い安全性・安定性をもつネットワークの構築を実現しました。** 
![_1605003725_xyWbj38IMt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512227/f073ec6f-18eb-551c-f753-d2d8863a0a75.jpeg)
企業の規模によらず、次世代のステートフルファイアウォールにはユーザーの個人情報・企業の機密データを守り、アイデンティティの識別・状態確認を行うという責任があります。ネットワークの安全性・安定性・効率を高め、ネットワークセキュリティを強化するために、高いコストを費やし、品質の良い製品に切り替えることが必要だと考えられます。
### オンライン不正行為の検出と防止——イントラネットへのアクセスを管理
ネットワーク管理者はFS セキュリティゲートウェイで接続したユーザーのオンライン行為を監視・管理することができます。トラフィックやアクセスの不正行為があるのか、データの受送信に異常があるのかについて詳しく調べることで、不正行為を阻止したり、デマ拡散を防止したりすることが可能です。 SG-5110セキュリティゲートウェイでは2600個以上のアプリケーションかつ3000万のURLアドレスを識別することができます。不正アクセス、ウィルス感染、不正ソフトウェアによる被害などといったセキュリティ事故・事件を防ぐために、セキュリティゲートウェイは最適なソリューションだと考えられます。
### IPsec VPN & SSL VPN、拠点間リモートインターコネクション（相互接続）
リモートワーク（遠隔勤務、在宅勤務）を導入した企業が増える一方、いかに情報・データの安全を確保し、漏えい・流出などのような不祥事を避けることは問題視されています。IPsecあるいはSSL VPNを利用することで、本社にあるデータセンターにアクセスして、必要なデータや情報を参考しながら、仕事を滞りなくこなすことが可能です。
![_1605003757_blbbe7pSVw.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512227/311bc299-e0ba-a386-3c5e-d3af478ea165.jpeg)
PFS（Perfect Forward Secrecy、完全転送秘密）セキュリティゲートウェイはIPsec VPNとSSL VPNの特性を用い、改ざん検知・暗号化機能を生かし、遠隔通信の秘密性・安全性を強化しました。複数の支社や事務所を持つ企業にとってFS セキュリティゲートウェイはなくてはならない安全保障だと言えるでしょう。
## 企業におけるFS セキュリティゲートウェイの活用
![_1605003784_p6Apm9z5Bx.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512227/c56d7b2e-c51a-1b2e-095c-d67003a8340d.jpeg)
次世代多機能型セキュリティゲートウェイとして、FS SG-5110はステートフルファイアウォール、オンライン不正行為検知・監視、IPsec VPN、SSL VPNなどの機能を備えています。それを活かして、FS SG-5110セキュリティゲートウェイは学校のような教育現場、ホテル、スーパー、ショッピングセンター、オフィスなど、様々な場所で活用されています。例えば、法人向け無線LANにおけるセキュリティゲートウェイの活用は上記の図のように描かれました。また、FS 次世代スイッチ及びWi-Fi6対応製品とセットで利用すれば、セキュリティを一層強化することが可能です。
ココナラでスクレイピングツールを作成する依頼を通じて、今後意識したほうが良いと感じたこと

# はじめに
この度、ココナラでスクレイピングツールを作成する依頼を数件いただきました。
その依頼を通じて今後意識したほうが良いと感じたことを備忘録を兼ねて記事に残します。
なお、この記事では具体的なソースコードは記述していません。ご了承願います。

# 前提
- Pythonでツールを作成していました。
- 「selenium」「requests + BeautifulSoup4」のいずれかを使用してツールを作成していました。
- 問い合わせがあってから、以下の流れで正式な納品まで進めていました。
    - ヒアリング(対象となるWebページ・出力するファイル形式などを質問を通して聞き取る)
    - 対応できるか調査
    - 見積もり(価格・期間)
    - 成約
    - 実装
    - 納品・リテイクを繰り返す
    - 正式な納品(お互い合意の上で)

# 1. 納期は長めに取っておく
納期は長めに取っておくといいと思います。リテイクや納品時のトラブルに備えるためです。実装そのものは1～2日に終わるものであったとしてもそこからプラス1週間ぐらい取るぐらいがいいかと思います。

# 2. ツールが納品先で使用できないトラブルに備えておく
実装したツールが納品先では使用できなかったということもあります。特にPythonをexe化して納品したときなどはセキュリティの問題から起こりやすいです。未然に防ぐことができないか、あるいはトラブルが起きたときはどうするか、可能な限り考えておくといいと思います。

ちなみに、exe化にこだわらなければ、Pythonをインストールすることなくツールを納品する手段があります。詳しくは以前に投稿した私の記事が参考になります。

https://qiita.com/76r6qo698/items/46d04691b3ab896dd08e

# 3. エラーハンドリングについて考えておく
ツールを作成するときにエラーは発生しないように実装をしていきたいところではあります。
が、納品先で想定していないエラーが起こったりするものです。
もし、エラーが起きたときでも親切な作りにできないか。エラーハンドリングを考えて実装していけるようになるといいかと思います。
以下がその一例です。
- タイムアウトなどの起こりやすく、かつ原因がはっきりしているエラーではツールが止まらない実装ができないか考える。
- エラーログをファイル出力させることができないか考える。
- ファイルへ出力するツールでは、こまめにファイル書き込みする実装にできないか考える。そうすることでエラーが発生してもリカバリしやすいようにする。

ありとあらゆるケースに対してエラーハンドリングを作り込むのは大変です。だからこそ、どこを重要視するか考える材料として依頼者へヒアリングをしていくのも良いかと思います。「1度に何ページ分のデータを抽出することになるか？」これを知るだけでもエラーハンドリングを考えるヒントになるのではないかと思います。

# 4. できる限りrequests + BeautifulSoup4で済まないか考える
使用するモジュールについてですが、seleniumで大体のことはカバーできます。ですが、seleniumで実装すると考えないといけないことが多い印象でした。(Webドライバー・読み込み戦略・オプション・・・etc)
そこで「requests + BeautifulSoup4」でできそうかをまずは考えたほうが良さそうです。
「requests + BeautifulSoup4」だけでは難しいページも確かにありますが、一見できなさそうだけど実はできるページもあります。
実際、ツール作成の調査時にこのようなことがありました。
- requestsでhttps通信する。レスポンスを取得する。
- レスポンスから取り出したい要素を確認するが、その要素内にテキストは何もない。
- レスポンスをよく見るとHTMLコンテンツの中にjsonが含まれていて、それを各要素に描画している処理があった。
- そこでjsonから取り出したい値を取得することにした。

こういうように、調べてみると「requests + BeautifulSoup4」でできそうだと分かることもあります。
調査のときに「requests + BeautifulSoup4」でできそうかどうかも合わせて確認するのもいいかと思います。

# あとがき
あくまで私が数件頂いた依頼の中で意識したほうが良いと感じたことをまとめました。
それでも参考になったと言っていただける方が1人でもいらっしゃると嬉しい限りです。

以上です。
DB設計の時、不用意に「delete_flag」を使うのはやめよう！

# この記事の執筆理由
システムチームでのMTGの際に、論理削除としてどのようなカラムが適切かという話になった際に、不用意なdelete_flagは使用すべきでないという結論になりました。
個人的にはなるほどな！と思う点があったので今回ご紹介できればと思っています。


# 結論
先に結論を書いておくと、**delete_flagはカラムには0か1の情報しか持てないから** です
なので、論理削除したいカラムでは、**「deleted_at」** を使いましょう。


# delete_flagを使うデメリット
###### ①履歴管理が難しい
delete_flagを使用すると、カラムには0か1の情報しか持てません。
そのためいつレコードが論理削除されたのかという履歴情報を保持することができません。
このレコードがいつ削除されたのかという情報は非常に重要です。

**～例～** 
あなたは、注文サイトのECを管理しています。
あるとき上司から、顧客Aさんの注文を削除した日を調べて欲しいと言われました。
この時、delete_flagで削除を管理していた場合だと、Aさんの注文状態は分かりますが、**いつ** 削除されたのかという情報はカラムからは判断することはできません。
アクセスログをたどっていけば分かるかもしれませんが、非常に時間がかかるし正確な情報にたどり着けない可能性があります。
もし、delete_atで削除を管理していればDBでSQLを叩けば一発で調べることができます。


###### ②クエリが不自然になりやすい
削除されていないレコードを抽出する際にはSQLのクエリですべてのSELECT文で **delete_flag=0**  を含める必要があります。これは、delete_atを使用しても同じではありますが、**deleted_at IS NULL** といったクエリのほうが自然に感じます。
また、Laravelを使っている場合は標準機能として、delete_atが備わっているので、わざわざdelete_flagを用いるメリットはないでしょう。



# まとめ
DB設計時には、実務でどのような抽出データが必要なのか考えてカラムを作成することが大事です。
また、カラムに持たせる意味についてもきちんと考えて設計を行いましょう。
そして！基本的には「deleted_at」を用いて論理削除を行いましょう！
**いつ削除されたレコードかという情報はとても大事！**
React Nativeのキャッシュクリアってこれでいい？

React Nativeのアップデートする時にキャッシュで悩まされる事が多いのでクリアするコマンドのまとめです。

## watchman

```bash
watchman watch-del-all
watchman shutdown-server
```

## node_modules周り

```bash
rm -rf node_modules yarn.lock
yarn cache clean
```

## ネイティブ周り

```bash
rm -rf ios/Pods ios/Podfile.lock ios/build
rm -rf ~/Library/Developer/Xcode/DerivedData
rm -rf android/build
```

## react-native start

```bash
react-native start --reset-cache
```


他にもクリアするものがあれば追記したいと思います。（Androidってこれだけでよかったっけ？）
Device or resource busy

"Device or resource busy" typically occurs when a computer or device is being used by another process or application.

If this problem occurs, you can identify the process causing the issue by using the following command:

```
fuser [filename] 
```

This command will display the following output:

```
[/path to filename]: ???(PID)
```

Here, you can terminate the process by using the following command:

```
kill -9 ???(PID)                  (ex.)kill -9 12345
```

Please note that the "kill -9" command forcefully terminates the process with the specified PID. Use this command with caution, as it may result in data loss or other unforeseen consequences.
WASIX を用いた WebAssembly の実行

WASI の拡張である [WASIX](https://github.com/wasix-org/) を使った WebAssembly を実行してみました。

実行には WASIX に対応した WebAssembly ランタイムが必要で、現状では [Wasmer](https://github.com/wasmerio/wasmer) を使う事になります。

# WASIX サンプル

[wasix-abi-rust](https://github.com/wasix-org/wasix-abi-rust) を参考にサンプルを作成しました。

```step1``` の文字列を標準出力した後、5秒スリープして ```step2``` の文字列を出力するだけの処理内容となっています。

注意点として ```thread_sleep``` のスリープ時間はナノ秒で指定する必要がありました。

```rust:src/main.rs
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

fn main() -> Result<()> {
    print_message("step1\n")?;

    unsafe {
        // 5秒スリープ
        wasix::thread_sleep(5 * 1000000000)?;
    }

    print_message("step2\n")?;

    Ok(())
}

fn print_message(msg: &str) -> Result<()> {
    let d = [wasix::Ciovec {
        buf: msg.as_ptr(),
        buf_len: msg.len(),
    }];

    unsafe {
        wasix::fd_write(wasix::FD_STDOUT, &d)?;
    }

    Ok(())
}
```

Cargo.toml の内容は以下のようになります。

```toml:Cargo.toml
[package]
name = "sample"
version = "0.1.0"
edition = "2021"

[dependencies]
wasix = "0.11"
```

# 実行

今回はビルド & 実行に下記を使用しました。

* [cargo-wasix](https://github.com/wasix-org/cargo-wasix)

arm64 環境（Apple シリコン）や GLIBC のバージョン関係で上手くいかなかったりと、割と苦労する事になりました。

最終的に x86_64 環境で Ubuntu 23.04 をコンテナ実行して成功しました。

## 環境構築

### Ubuntu をコンテナ実行

ubuntu:22.04 のコンテナイメージを使った場合、ビルド時に```GLIBC_2.36 not found``` エラーが発生して上手くいかなかったので 23.04 を実行します。

```sh:ubuntu実行例
$ podman run -it --rm -v $(pwd):/work ubuntu:23.04 bash
```

以後の操作はこのコンテナで実行します。

### build-essential インストール

このままだと cargo-wasix インストール時に ```error: linker `cc` not found``` エラーで失敗するので、```build-essential``` をインストールしておきます。

```sh:build-essentialインストール例
$ apt install -y build-essential
```

また、Rust や Wasmer のインストールで使う場合は curl もインストールしておきます。

```sh:curlインストール例
$ apt install -y curl
```

### Rust インストール

Rust をインストールしておきます。

```sh:Rustインストール例
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ . ~/.cargo/env
```

### cargo-wasix インストール

cargo-wasix をインストールする事で ```cargo wasix``` コマンドを利用できるようになります。

```sh:cargo-wasixインストール例
$ cargo install cargo-wasix
```

### Wasmer インストール

```cargo wasix run``` 時に wasmer コマンドが見つからないと ```failed to find `wasmer` in $PATH``` というエラーで失敗するので wasmer をインストールしておきます。

```sh:wasmerインストール例
$ curl https://get.wasmer.io -sSfL | sh
$ . ~/.wasmer/wasmer.sh
```

## サンプルの実行

これで環境が整いましたので、```cargo wasix run``` でビルド & 実行します。

step1 の出力後、5秒程度経ってから step2 が出力されたので、とりあえずは正常に動作したようです。

```sh:実行例
$ cd /work
$ cargo wasix run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `/root/.cargo/bin/cargo-wasix target/wasm32-wasmer-wasi/debug/sample.wasm`
info: Post-processing WebAssembly files
     Running `target/wasm32-wasmer-wasi/debug/sample.wasm`
step1
step2
```
手早くRailsアプリでUserAgentがbot判定するには

## やりたいこと

素直にBot判定をしようとすると、UserAgentかIPアドレスから判定することになると思います。

愚直にbot判定しようとするとUserAgentから判定することになると思いますが、世の中には無数のBotがあり困難です。

できるだけ、簡単にBot判定をしようとしてみた。

## どうするの？

Bot判定を自力で頑張らずともUserAgentで様々な判定をしてくれるgemのbrowserを使うだけです

https://github.com/fnando/browser


以下、の2行だけで完結しちゃいます！
```ruby
browser = Browser.new(user_agent)
browser.bot?
```

でも、どうせそんなに多くのBotには対応してないでしょっと思うかもしれないですが、
300近くの有名なBotに対応しているので、自力で対応していく労力に比べ圧倒的に楽に判定できます！
対応Botリスト: https://github.com/fnando/browser/blob/main/bots.yml

## まとめ

browserを使うと簡単にUserAgentからBot判定ができてしまいます。
この他にもbrowserにはデバイスの判定を簡単にできたりします。

しかし、この方法だけでは１００％確実にBotの判定ができるわけではありません。
Botの中には一般のユーザーっぽいUserAgentに偽装してアクセスしてくるBotなどもいるので、UserAgentの判定だけを過信するのは良くない場合もあります。
Slackの次世代プラットフォームが正式リリースになったので、GASで稼働しているBOTを移植してみる。その2(特徴)

[前回の記事](https://qiita.com/LBnozaki/items/4323013e0e2f819761da)から期間が空き、その間にSlackの次世代プラットフォームはオープンベータから段階的な正式リリースのステータスとなり、多くのアップデートがありました。
そのため、前回の記事と繋がらない部分はあるかと思いますが、ご容赦ください。

さて、今回は次世代プラットフォームの特徴をまとめていきます。
まとめるカテゴリーとしては、公式サイトのアピールポイントごとに行い、
これまでの方式との差異などを交えて記述していきます。

## 1. 統合された開発環境が提供される
以前のSlackアプリは、APIリファレンスを基に直接コードを書いたり、PythonのSDKを用いて開発を行っていました。
次世代プラットフォームでは、Slack CLIが提供され、これ一つで簡単にアプリのプロジェクト作成からテスト、デプロイまで行えるようになりました。
これにより、開発効率が向上することはもちろんですが、組織やチーム間でのプロジェクトの共有も容易となり、メンテナンスやアップデートについても多くの恩恵を受けることができるようになりました。
[Slack CLI - Install & authorize](https://api.slack.com/automation/cli/install)

## 2. アプリのホスティング環境が提供される
次世代プラットフォームでは、コンピューティングリソースについても、Slackから提供されます。
これまでは、GASで書かれたものや、Pythonでイベント駆動型のサーバレス環境に設置されたものなど、動作環境が分散したり、それぞれの環境をメンテナンスする必要がありました。
それが、次世代プラットフォームでは、Slackが提供する環境で動作するため、環境の把握や管理も不要となり、ロジックの開発に集中することができるようになりました。

## 3. Slackというプラットフォームに特化したSDK
CLIでセットアップされるプロジェクトは、denoというJS/TSランタイム用にベースが作成されます。
そのため、プログラムの実装については、TypeScriptで行います。

プロジェクトは、Functions,Workflows,Triggersという、Slackプラットフォームに対する開発に特化した要素で構成されます。
なので、作りたいものをプログラムコードの文脈で分解しやすく、スムーズに開発に入っていけるように感じました。
各要素については、概要を以下にまとめます。

### 3-1. Functions
Workflowsを構成する機能の単位です。いわゆる関数です。
SDKがSlackを操作するための便利なメソッドを多数用意してくれているので、
データの加工や判定処理など、アプリに必要な部分を自分たちで実装していきます。
[Slack functions](https://api.slack.com/automation/functions)

### 3-2. Workflows
実現したい処理の流れです。アプリのメイン処理部分です。
Functionsの組み合わせで構成されているので、可読性が高く、入れ替えや追加、編集も容易に行えます。
[What are workflows?](https://api.slack.com/automation/workflows)

### 3-3. Triggers
Workflowsの起動される条件にあたります。
定義できる条件は、
- Link (チャンネルから特定のURLクリックで実行)
- Scheduled (特定日時、毎時、毎日などの定期実行)
- Event (チャンネルへの参加、メンション、リアクションの追加などSlack上のイベントを起点に実行)
- Webhook(外部サービスなどから、特定のURLに対してのデータ送信で実行)

と、充実しており、それぞれの条件でアプリに渡される情報が異なるので、アプリで実現したいことをもとに、最適なものを選択します。
[Triggering workflows](https://api.slack.com/automation/triggers)

### 3-x. Datastores
処理からは少し離れますが、
Slackインフラ上に、NoSQLのデータベース(DynamoDB)も構築することが可能です。
感想になりますが、今までGAS(with スプレッドシート)でSlackアプリを構築していた場合などには、使いやすそうです。

[Datastores](https://api.slack.com/automation/datastores)

## まとめ
今回は、Slackの次世代プラットフォームの特徴について、まとめました。
前回から時間も空いているので、予定通り次回に移植の記事を書くかは悩んでおりますが、
何かしらSlackの次世代プラットフォームで作ってみるところまで、記事の完走を目指したいと思います。
PHPのFiberを使用してコールバックをTraversableに変換する

PHP の Fibers で何か面白いことができないかという試みです。

```php
function subscribe(callable $onNext): void
{
    for ($n = 0; $n < 10; ++$n) {
        $onNext($n);
    }
}

foreach (fromObservable(subscribe(...)) as $n) {
    echo $n, PHP_EOL;
}
```

上記コードのようにコールバックで値が供給される構造を `Fiber` で `Traversable` に変換することで、`foreach` でループすることができます。

`Traversable` 自体が汎用的なパーツなので、`map` のような汎用ユーティリティがある前提下でさまざまな応用が考えられます。

```php
/**
 * @template TKey
 * @template TSource
 * @template TResult
 * @param Traversable<TKey,TSource> $source
 * @param callable(TSource,TKey):TResult
 * @return Traversable<TKey,TResult>
 */
function map(Traversable $source, callable $selector): Traversable;

map(fromObservable(subscribe(...)), fn($n) => $n * $n);
```

---

<https://www.tehplayground.com/Idh8iJE8SAsx0p5j>

```php
<?php

/**
 * @template TSource
 * @param callable(callable(TSource):void):void $observable
 * @return Traversable<TSource>
 */
function fromObservable(callable $observable): Traversable
{
    $fiber = new Fiber(static function () use ($observable) {
        $observable(static function ($element) {
            Fiber::suspend($element);
        });
    });

    return new class ($fiber) implements Iterator {
        private int $_key = 0;
        /** @var ?TSource */
        private mixed $_current;

        public function __construct(private readonly Fiber $fiber)
        {
        }

        public function current(): mixed
        {
            return $this->_current;
        }

        public function key(): mixed
        {
            return $this->_key;
        }

        public function next(): void
        {
            $this->_current = $this->fiber->resume();
            ++$this->_key;
        }

        public function rewind(): void
        {
            $this->_current = $this->fiber->start();
        }

        public function valid(): bool
        {
            return $this->_current !== null;
        }
    };
}

/**
 * @param callable(int):void $onNext
 */
function subscribe(callable $onNext): void
{
    for ($n = 0; $n < 10; ++$n) {
        $onNext($n);
    }
}

foreach (fromObservable(subscribe(...)) as $n) {
    echo $n, PHP_EOL;
}
```
マージ済みブランチの整理整頓

## はじめに
基本、マージしたブランチを手動でdeleteしているが、毎回消しているわけではないので、マージ済みブランチが溜まっていく。時々不要ブランチをコマンドで一気に消すことがあるが、ときどきしかしないので毎回どうやったか思い出しているので、やり方をメモする

## ブランチ整理の時使うコマンド
```
git branch --merged
# マージ済みブランチ一覧を表示

git branch -d "ブランチ名"
# "ブランチ名"のブランチを削除する
```
`--merged`　のオプションで、すでにマージ済みのブランチ一覧を取得し、`-d` で削除する。小文字のdの場合、マージされていないなら削除しないが、大文字`-D`にすると強制で削除できる。ちなみに"--merged"はデフォルトだとカレントブランチにマージされたものが表示される。
数が少なければ`git branch -d`で消せばいいが、数が多くなってくると-dの後ろにブランチ名をコピーして貼り付ける操作がだるい。

## xargsコマンド
そういう時、
「標準入力やファイルからリストを読み込み、コマンドラインを作成して実行する」xargsコマンドを使う。これは他コマンドと組み合わせて使うことが前提になっているコマンドで、パイプ(|)で繋いで使用する
```
git branch --merged|xargs git branch -d
```
こうすると、`--merged`で出力されたブランチ名を一つ一つ`git branch -d`に渡してブランチを削除していることになる。
ただ、大抵`git branch --merged`した際に消したくないブランチが含まれることが多い。今回だと`develop`とカレントブランチくらいは消したくない

### 最終系
```
git branch --merged|grep -Ev '\*|develop'|xargs git branch -d

> Deleted branch xxx (was xxxxxxxxx).
> ...
```
最終的に、指定した文言の含まれない結果を返す`-v`オプションを付けてgrepで`git branch --merged`の結果を整理する。
カレントブランチには先頭に`*`が付くので、\でエスケープして指定している。複数指定のために|で`develop`を指定し、環境によっては`main`だったり`staging`を指定することがあるかもしれないので、環境によって調節する必要がありそう。

## おわりに
コマンドは時々しか使わないからどうも忘れがちになるけど、やっぱり便利。細かいところで使っていって、やり方を忘れない様にしたい。
Azure OpenAI on your dataのAPIでハマった点

# はじめに

先日衝撃的に登場した[Azure OpenAI on your data](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/concepts/use-your-data)ですが
ChatGPT プレイグラウンドに加えてAPIでも利用できるようになっています。
APIがうまく動かせなくてものすごくハマったので注意点をメモしていきます。

# やってみた

ChatGPT プレイグラウンドにてon your dataの設定を済ませてさあAPIで動かしてみるぞ！って張り切っていました。

```json
{"error":{"code":"InternalServerError","message":"Backend returned unexpected response. Please contact Microsoft for help."}}
```
何度試してもこのエラーが返されてしまい設定したパラメータを何度も確認したのですが
このエラーメッセージからは正直言いますとエラーの原因が分からず途方に暮れていました・・・

# 考察

[APIの詳細](https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/reference#completions-extensions)を見ていきましょう。

まずはエンドポイントから
```
POST {your-resource-name}/openai/deployments/{deployment-id}/extensions/chat/completions?api-version={api-version}
```

`your-resource-name`はお馴染みの`https://xxxxx.openai.azure.com`のようなエンドポイントが発行されますのでそれを設定します。

`deployment-id`はデプロイしたモデル名を指定します。私は`gpt-4`をデプロイしました。

`api-version`は`2023-06-01-preview`を設定しました。

このエンドポイントには`/extensions/`というパスが含まれています。
**入力候補の拡張機能**とありこれは通常のchat completionsと異なるものになるかなと思います。

ではいよいよパラメータ仕様を見ていきます。
ここからが超重要ですw
最低限動作させるのに必要なパラメータは以下になります。
これはドキュメント通りだったと思いますね。
一つ一つ見ていきます。

```sh
curl -i -X POST YOUR_RESOURCE_NAME/openai/deployments/YOUR_DEPLOYMENT_NAME/extensions/chat/completions?api-version=2023-06-01-preview \
-H "Content-Type: application/json" \
-H "api-key: YOUR_API_KEY" \
-H "chatgpt_url: YOUR_RESOURCE_URL" \
-H "chatgpt_key: YOUR_API_KEY" \
-d \
'
{
    "dataSources": [
        {
            "type": "AzureCognitiveSearch",
            "parameters": {
                "endpoint": "'YOUR_AZURE_COGNITIVE_SEARCH_ENDPOINT'",
                "key": "'YOUR_AZURE_COGNITIVE_SEARCH_KEY'",
                "indexName": "'YOUR_AZURE_COGNITIVE_SEARCH_INDEX_NAME'"
            }
        }
    ],
    "messages": [
        {
            "role": "user",
            "content": "What are the differences between Azure Machine Learning and Azure Cognitive Services?"
        }
    ]
}
'

```

`api-key`は発行されたAzure OpenAI用のAPIキーを設定しましょう。これはいつもchat completionsなど利用しているものと同じですね。


そして`chatgpt_url`がとてもトリッキーなポイントですw

```
https://xxxxx.openai.azure.com/openai/deployments/xxxxx/chat/completions?api-version=2023-03-15-preview"
```

これはextensions用ではなくていつも皆さんが使っている**chat completionsのエンドポイントが正解**だったのです。
ここにextensions用のエンドポイントを設定していたため先ほどのエラーが出ていました。

`chatgpt_key`は`api-key`と同じもので問題ありません。

`dataSources`部分を見ていきましょう。

`endpoint`はCognitive Searchの設定で発行されたエンドポイントを設定しましょう。
おそらく`https://xxxxx.search.windows.net`のような感じになっていると思います。

`key`もちょっと注意が必要ですね。
Cognitive Searchの設定で発行された**管理者キー**を設定してください。
**クエリ キー**ではエラーが返却されて動作しませんでした・・・

`indexName`は作成したインデックス名を指定すればOKです。

`messages`パラメータはいつもと変わらない感じで大丈夫です。

# さいごに

この問題が解決するまで１週間くらいネットを彷徨ってました。
解決できたときはとても嬉しかったですね。
エンジニアの皆様としてはやはりAPIでちゃんと動いてくれないと辛いものがありますよね。
同じように困った人に参考になる情報であれば幸いです。
新人時代もらった未だに覚えているお言葉

## 遥か彼方の会計系独立系Slerで…

　恥ずかしながらエンジニアとして働きだしてもうすぐ9年ほどになろうとしています。今、私は某出版会社にてシステム開発部（的なポジションの部署）で働いていますが、新卒では某大手奉行系SIerに就職しました。半年ほどの手厚い新人研修期間を終え現場に配属され、メンターとして先輩社員が私についてくれました。その先輩社員がなかな癖の強い人、同じ大学の先輩だったこともあり、よく頭をこつかれたり、よく叱られたりしていました。そんな先輩ですが、仕事はバリバリできる人で、私がヘマをして叱られるたびに、ためになるアドバイスをいくつももらっていました（全部は覚えていませんが笑）
　そんな新人時代から紆余曲折あり、何回か転職して今に至るのですが、未だに覚えていて今の私の仕事観の基盤となっている言葉をご紹介したいと思います。

## 「おれたちはソリューションを提供してお金をもらっている。ソリューションは決してシステムを作ることではない、5分でエクセル関数組んですぐに解決できるなら（顧客の利益に繋がるなら）それに越したことはない」

Slerとして、顧客企業から受注を受けシステムを開発して納品するビジネスモデルだったので、顧客からの要望に答えようとしてシステムを作り込む方向に考えてしまいがちだった私に対する言葉です。
システム開発はあくまで手段であり、本当に顧客が叶えたい状態にするにはどうしたらいいか根本から考える必要があるという気づきを与えてもらいました。この言葉をもらったことで、私の中で「本当に顧客のためになることはなにか」と「システムは問題を解決するための手段」という意識が根付きました。

## 「いいか、おれたちはプロだ。プロの活動にはすべて時給が発生している。知らないことを学び、力を溜めているときだとしてもそのことは忘れてはならない」

私が勉強としてコードリーディングや参考書を読んでいるときに言われた言葉です。
直接的に見える成果に繋がらない活動であっても会社員である以上、そこには時給が発生しコストになっています。学ぶときであっても、学んだことをどう活用して、どう貢献することができるかを考えるか考えないかでは、学んだことのアウトプットも大きく変わります。この言葉をもらったことで、ただ自分が成長するためだけではなく、どうアウトプットして一緒に働いている人たちに還元するかも大事なことだと気づくことができました。

## 最後に
### 「新人に100の金言を与えても5年後に残っているのはせいぜい1つか2つ」

これは私が新人研修の担当だったときに、毎年、他の先輩社員の方々に向けて新人研修についての報告をする際に言っていた言葉です。

新品のスポンジのような新人は多くのことを吸収しますが、先輩のアドバイスの本当の大切さを理解してしっかりと定着させることは本当に稀なことだと思います。ですが、先輩として「1つくらいしか残らないのか」と悲観するのではなく **「なら1000も2000も良いアドバイスをして1つでも多くのことを残してやろう」** と考えて接することが良き先輩なのではと私は思っています。後輩社員にはいろんなアドバイスを滝のようにしてあげましょう！

※ 事実を元にしていますが多少のドラマティックなアレンジが含まれています。
データを2進数でイメージしてみる

プログラムが動作する仕組みを頭の中でイメージできるようになるためには、コンピュータの内部でデータがどのような形式で表され、どのような方法で演算されているのかを知ることが重要。2進数でデータを演算する仕組みをマスターできれば、プログラムが動作する仕組みも見えてくるはず。

## コンピュータが情報を2進数で取り扱う理由
コンピュータの内部がIC（集積回路）と呼ばれる電子部品で構成されており、ICの特性により、コンピュータでは必然的に情報を2進数で取り扱う必要が出てくる。
**ビット**: コンピュータを取り扱う情報の最小単位。2進数の１桁に該当。
**バイト**: コンピュータを取り扱う情報の基本単位。8桁の2進数。
メモリやハードディスクなどにはバイト単位でデータが格納され、バイト単位でデータが読み書きされる。
## 2進数とは
「00100111」という2進数の値を10進数の値に変換してみる。
この時になぜこのような変換方法になるのか、説明できることが重要。
**重み**: 10進数で表された39という数値の各桁の数値にかけている10や1などの値のこと桁が違えば重みも違うし、桁が上がればだんだん重くなる。
そもそも数値は、その数値を構成する核桁の数値に重みをかけた値を合計した結果を表している。
例えば39という10進数なら30+9を表しており、核桁の数値に重みを掛けて合計した値を表していることになる。
この考え方は2進数でも同じ。
## 「2の補数」はコンピュータ処理に都合がいい
コンピュータは、引き算を行う場合に、内部的には足し算として演算するようになっている。そのため、マイナスの値を表すときに2の補数を使用するという工夫がなされている。
**2の補数**: 2進数において、プラスの値でマイナスの値を表すという不思議な数。2の補数を得るためには、2進数で表された各桁の数値を全て反転し、その結果に1を加える。
例えば、-1を8桁の2進数で表すなら、1すなわち「00000001」の2の補数を求める。「00000001」の2の補数は、各桁を全て反転して1を加えるので「11111111」となる。
もう一つ例。3-5を考える。5=00000101の2の補数は反転して+1で「11111011」なので、「00000011 + 11111011」 = 11111110となり最上位桁が1となっているのでマイナスの値を表している。ここでもう一度2の補数を求める。全ビット反転して1を加えて、00000010となり、-2が導き出される。
## 論理右シフトと算術右シフトの違い
**論理右シフト**: 2進数の値が数値ではなく、グラフィックのパターンなどの場合には、シフト後の上位桁に「0」を入れること。
**算術右シフト**: 2進数の値を符号がある数値として演算する場合には、シフト前の符号ビットの値（0または1）を入れること。
2の補数で表されたマイナスの値の場合には、右シフトして空いた上位桁に「1」を入れることで、符号つきで正しく、1/2倍、1/4倍、1/8倍などの数値演算が実現できる。
【React×TypeScript×Firebase】Redux Toolkit

# Redux Toolkitの使用方法
* Redux Toolkitの導入にあたり、流れや使用方法、実際の作成について備忘録を残します。
* 備忘録はカウンターアプリを例にします。

## 導入
* Reduxを簡単に使うためのkit
```sh
npm install @reduxjs/toolkit
```
* useDispatch等が用意されているhooks
```sh
npm install react-redux
```

## 概要
* Reduxの全体像
![7402c7f4-a7b2-d4a2-e30d-d4ab5816487c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920579/c536cfe3-3a0d-7cf8-d8db-9587ce7f1167.png)
引用（https://qiita.com/kitagawamac/items/49a1f03445b19cf407b7）

* 自分なりに解釈した内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2920579/e1a3c67f-59c5-3070-a0a7-0097df8793b3.png)

## Redux toolkitで必要なファイル
1. store.ts
2. コンポーネント.tsx（ここではCounter.tsx） 
3. Slice（ここではconterSlice.tsx） 

### store.ts
* 状態を管理するファイル
```ts:store.ts
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counterSlice';

export const store = configureStore({
    reducer:{
        counter: counterReducer,
    },
});
```

### Counter.tsx
* コンポーネントにあたる
```tsx:Counter.tsx
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { decrement, increment } from '../../features/counterSlice';
import { RootState } from '../../app/store';

const Counter = () => {
    const count = useSelector((state: RootState) => state.counter.value)
    const dispatch = useDispatch(); // 通知を出すことができるhooksがreact reduxに用意されている
  return (
    <div>
        <h1>count: {count}</h1>
        <button onClick={() => dispatch(increment())}>+</button>
        <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  )
}

export default Counter
```

### conterSlice.tsx
* スライス（ActionCreater, State, Reducer）をまとめたもの
```tsx:conterSlice.tsx
import { createSlice } from "@reduxjs/toolkit";

interface CounterState {
    value: number;
  }

  const initialState: CounterState = {
    value: 0,
  };

// creatSlice actionCreater, dithpach, reducerを一度に作ることができる関数（用意されている）
export const counterSlice = createSlice({
    name: "counter",    // sliceの名前
    initialState,
    reducers:{          // 状態を更新するための関数みたいなもの actionCreaterはreducerを作成すると同じ名称で自動的に裏で作成される
        increment: (state) => {     // カウントアップ stateはその時々の状態
            state.value += 1;
        },
        decrement: (state) => {
            state.value -= 1;
        },
    }
})

export const { increment, decrement } = counterSlice.actions; //actionCreaterはコンポーネントに受け渡さないといけないのでexport
export default counterSlice.reducer; // redusersではなくreducersを渡す
```
mysql基本

設計やチューニングで注意したこと。
```bash

実装で起こりがちなこと
mysqlでの処理が遅い場合のあるある

1.スロークエリログの調査

2.nullが多すぎる

3.テーブルにindexをはれていない

4.クエリの数が多い
    ⇨join or EagarLoadを行う。

5.nullを極力使用しない
　⇨is not null

6.パラメータチューニング
　⇨MySQL診断ツール (MySQL Tuner) を使う

7.EXPLAINを使って、JOIN時にどちらのテーブルが駆動表でどちらのテーブルが内部表になっているか調べる
joinを正しく使用する

上記のチューニングとは別の内容にはなりますが・・・

※基本的にフォームが空のままデータを投入することはよろしくないと思っております。
実装時にプルダウンメニューで最低限入力保管をすることでバリデーションをかける等でカバーすることなども必要かもしれません。

もしくは設計段階できちんとした設計であるかなど。
チューニング自体も大切ですが、その前に渡された側が苦労しないようなスケジュールや実運用になったときに現実的に無理がないのか？
等も考慮する必要があるかと思います。

```

トランザクションに関する知識
```
トランザクション開始～終了までの一連の処理のこと。

トランザクションを終了するときは、一連の処理を確定させる（コミット）」
破棄する（ロールバック）」

```

SQLインジェクション
```
アプリケーションの脆弱性により本来の意図ではない不当な「SQL」文が作成されてしまい、「注入(injection)」されることによって、データベースのデータを不正に操作される攻撃
```
axumのexamplesが現行版でそのまま参考にできない話(2023/07/03時点)

## 注意

これは書き散らしなので案外すぐにこの記事は使い物にならなくなります｡多分｡

## TL;DR

- 結局ドキュメントの`docs.rs`でお出しされていないコードは参考にしてはならない
- 何が変更された追跡してそこだけ現行版にあわせたら別に問題ない話→なのでやっぱりドキュメントが正義


## axumのexampleとは

> The axum repo contains a number of examples that show how to put all the pieces together.

[axum -Rust](https://docs.rs/axum/0.6.18/axum/#examples)より

ここの`a number of examples`からGitHubのコードに飛ぶことができ､[WebSocket](https://github.com/tokio-rs/axum/tree/main/examples/websockets)だったり[データベースコネクション](https://github.com/tokio-rs/axum/tree/main/examples/sqlx-postgres)だったりいろんなコードの実装例が書かれている素敵なリンクです｡


## 実際にコードを動かしてみる

脳死でコピペして動かしてみましょう 今回は[hello-world](https://github.com/tokio-rs/axum/tree/main/examples/hello-world)を使っています

```toml:Cargo.toml
#...
[dependencies]
axum = "0.6.18"
tokio = { version = "1.15.0", features = ["full"] }
```

```rust:src/main.rs
//! Run with
//!
//! ```not_rust
//! cargo run -p example-hello-world
//! ```

use axum::{response::Html, routing::get, Router};

#[tokio::main]
async fn main() {
    // build our application with a route
    let app = Router::new().route("/", get(handler));

    // run it
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    println!("listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}

async fn handler() -> Html<&'static str> {
    Html("<h1>Hello, World!</h1>")
}
```

実行結果がこちら

```
dev\rust-play on  main [?] is  v0.1.0 via  v1.68.2
❯ cargo run
   Compiling rust-play v0.1.0 (C:\Users\raiga\dev\rust-play)
error[E0425]: cannot find function `serve` in crate `axum`
  --> src\main.rs:13:11
   |
13 |     axum::serve(listener, app).await.unwrap();
   |           ^^^^^ not found in `axum`

For more information about this error, try `rustc --explain E0425`.
error: could not compile `rust-play` due to previous error
```

というように､存在しないよ~という風に言われます｡


## Exampleなのになぜ動かないのか

そんなのExampleの意味ないじゃないか!って思ったりもしますが､おちついて色々調べてみる

- `axum`のバージョンは`0.6.18`
- 最新の`0.6.18`のドキュメントにはまず`axum::serve`の項目はない

というわけでまぁあたりまえの話なんですけど､｢そのバージョンにそんな機能はないよ!｣という話でした

そしてGitHubの最終のファイルのコミットを追ってみると､あるPRのコミットにより現行のバージョンにない機能を使っているようです｡

https://github.com/tokio-rs/axum/pull/1868

このPRを見る限り､新しい`v0.7`への機能改善だと思われるのですが､何故かマージしている先のブランチが404なので､これの機能が結局次のリリースなどで実装されるのかは不明なのに､そのままにされているみたい･･････?

## 結局どうするのか

今回注目した部分での改変は現行版が

```rust
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
```

を

```rust
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
```

と変更されているようなので､そこは変更すればいいでしょう｡
まだ全部のExampleを見ていないので､もしかしたらまだ他にも実装が宙ぶらりんでエラーになる機能もあるかもしれないので､他のところも一度実装例をそのまま動かしてみて､そこから動く部分を参考にするようにすればいいと思います｡

なのでまずはきちんと**ドキュメント､読もう!**


### 追記 ブランチ選択

今自分の使いたいバージョンに合わせてブランチを切り替えればその時のバージョンの使用例にできます(~~でもドキュメントのリンクもできればバージョンにあわせてほしいよなーなんて思っていません~~)

https://github.com/tokio-rs/axum/tree/v0.6.x
【Python】スネークケースの文字列をキャメルケースの文字列にする方法を2種類紹介 (+モデルクラスにアクセスする方法)

# 概要
`company_code`と言うオブジェクトを`CompanyCode`と言うモデル名にする（スネークケースをキャメルケースにする）方法を2つ紹介します。また単にモデル名を生成するだけではなく、モデルクラスにアクセスする方法も記載します。

# サンプルコード解説
- `str.title()`メソッドを使用する方法
- `capitalize()`メソッドを使用する方法

## `str.title()`メソッドを使用する方法

```py
object_name = 'company_code'

def get_model_from_object_name(object_name):
    model_name = object_name.title().replace('_', '')
    return model_name

model = get_model_from_object_name(object_name)
print(model)

# 出力結果
# CompanyCode
```

`str.title()`メソッドは、文字列内の単語の先頭文字を大文字に変換するメソッド。その後、`replace`で`_`を取り除くという方法です。
`str.title()`メソッドは、スペースやアンダースコアが単語の区切りと判断されます。それ以外にもハイフン、ピリオド、スラッシュなども認識されます。


## `capitalize()`メソッドを使用する方法
```py
object_name = 'company_code'

def get_model_from_object_name(object_name):
    model_name = ''.join(word.capitalize() for word in object_name.split('_'))
    return model_name

model = get_model_from_object_name(object_name)
print(model)

# 出力結果
# CompanyCode
```

`capitalize()`メソッドは、文字列内の最初の文字を大文字に変換するメソッド。

`split('_')`によって文字列がアンダースコアで分割され、各単語がリストとして取得された後、`for`ループでリスト内の各単語をキャピタライズし、`join()`メソッドで結合する、という方法です。

## モデルクラスを取得したい場合
文字列として与えられたモデル名をもとに、対応するモデルクラスを取得したい場合は、関数の返り値を`return globals()[model_name]`とします。これにより、データベース内のモデルとの対応や動的なオブジェクト作成などの処理に利用することができます。

 `globals()`はグローバルスコープ内の変数やオブジェクトを格納している辞書です。

該当するキーが見つからなかった場合、`KeyError`例外が発生します。

取り急ぎローカル環境で触るために使いましたが、グローバルスコープ内の全ての変数を辞書して返すので可読性や保守性に欠ける可能性もあります（というよりあまり推奨されない）。変数をまとめて辞書で管理するとか、引数に渡す関数にするとかした方が良いかもですね。
ChatGPで月の満ち欠けプログラム

# ChatGPTを使ったプログラム作成にチャレンジ

　ChatGPT4に

```
年月日より、月の満ち欠けを表示するプログラムコードは？
```
と尋ねたところ1回のエラーだけで作り上げてきました。
　恐るべし。


```moon_age.py:
import ephem

def get_moon_phase(year, month, day):
    date = ephem.Date(f"{year}/{month}/{day}")
    moon = ephem.Moon(date)
    age = ephem.previous_new_moon(date) - ephem.previous_new_moon(moon.previous_new())

    if age < 1.0:
        phase = "New moon"
    elif age < 7.4:
        phase = "First quarter - Waxing crescent"
    elif age < 14.8:
        phase = "Full moon"
    elif age < 22.1:
        phase = "Last quarter - Waning gibbous"
    else:
        phase = "New moon"
    
    return phase

year = 2023
month = 7
day = 3
print(get_moon_phase(year, month, day))
```

こんな感じでした。
ゆめみの春の1dayインターンシップ（サーバサイド）に参加してきた件！！

# 目次
- [ゆめみの1dayインターンシップとは](#ゆめみの1dayインターンシップとは)
- [参加した理由](#参加した理由)
- [インターンシップの流れ](#インターンシップの流れ)
- [設計グループワークで行ったこと](#設計グループワークで行ったこと)
- [参加しての筆者の感想](#参加しての筆者の感想)

# ゆめみの1dayインターンシップとは
> サーバーサイドエンジニア必須の知識や設計手法を学べる1dayワークショップ

※👆[ゆめみ公式のページ](https://hrmos.co/pages/yumemi/jobs/1791024548071079941)から引用。リンク先のページがいつ削除されるかわからないです😅

この記事では2023年6月28日に開催された「春のゆめみ1dayインターン」についての概要と感想について書きます。



# 参加した理由
私がこの１dayインターンに参加した理由は下記の3点です。
- サーバサイドに興味があった。
- ゆめみという企業に興味があった。
- インターンシップの内容が面白そうだったから

詳しい背景は割愛するのですが、the 普通の就活生的な感じで参加しました（笑）

# 参加資格
参加資格は下記の3点でした。

- 高等学校・高等専門学校・大学・大学院・専門学校生（学部・学科不問）
- ES & アンケートの提出
- Webテスト（開発知識を問うクイズ30問）80点以上

この中だと、皆さんが一番気になるのは、Webテストですよね？（笑）
詳しい内容は言えないのですが、Webについての基本的なことを問う選択式の問題でした。
なので、そこまでハードルの高い選考はないので、気軽に参加できるインターンシップだと思います。

# インターンシップの流れ
1. 会社の紹介
1. グループに分かれての自己紹介&アイスブレーク
1. 昼食休憩
1. 技術LT（グループワークで扱うこと）
1. 設計グループワーク
1. チーム発表 & メンターによるフィードバック
1. 質問会

# 設計グループワークで行ったこと
グループでは、下記の3つの設計＆設計書の作成をしました。
1. ER図の作成
1. API設計
1. シーケンス図の作成

この課題では、初めにシナリオと仕様書が渡されました。📝<br>
シナリオについては具体的なことは言えないのですが、普段の生活で使ったことがあるようなシステムがテーマで、それの設計を途中で引き継ぐという内容のシナリオでした。<br>
また、仕様書の方には、基本的な仕様から、少し考えさせられるような仕様までが約15個くらいの仕様が定義されていました。<br>
そして、シナリオと仕様書を基に、ER図→API設計→シーケンス図の順番でシステムを設計していくのがグループワークのタスクでした。<br>
そして、そのグループワークには1グループに1人のゆめみの社員のメンターさんがついてくれ、困ったり、質問したいタイミングに助けてもらえる状態でした。<br>


# 参加しての筆者の感想
このインターンを通じて、サーバサイドの設計について多くのことを学ぶことができました。<br>
具体的には、上記で述べた設計書を作成するにあたっての考え方や、（mermaid.jsを使った）設計書の書き方について学ぶことができました。<br>
加えて、設計をグループでするという経験は、今まで経験したことがなかったので、とても新鮮でした。<br>
また、グループワークの前にLTがあり、そこでサーバサイドの設計についての基本的な知識を学び、グループワークで実践してみるという形だったので、このインターンで扱った設計についての知識は、普段の座学に比べて、より身についたと感じました。<br>

なので参加してとても良かったと思っています😁<br>

なので、皆さんももし機会があったら、このような1dayインターンシップなどに参加してみるのをおススメします👍<br>
Pythonを用いたPDFファイルのOCR処理

# はじめに
初めまして，大学院でコンピュータサイエンスを専攻しているchanjagaです．

友人がPDFファイルのOCR化を必要としていたため，試しにPythonを使って実装してみました．

OCRとは，簡単に言うと画像データのテキスト部分を認識し，文字データに変換する機能のことです．

# 実行環境
今回はGoogle Colaboratoryを使ってPythonを実行します．
 https://colab.research.google.com/?hl=ja

# 準備
Pythonを実行する前に以下をGoogle Colaboratoryで実行し，必要なパッケージをインストールします．
```txt:prepare.txt
!apt-get install -qq tesseract-ocr
!apt-get install -qq libtesseract-dev
!apt-get install -qq poppler-utils
!apt-get install -qq tesseract-ocr-jpn
!pip install -q pytesseract
!pip install -q pdf2image
```

# サンプルコード
以下は今回作成した，OCR化のサンプルコードです．

```python:OCR.py
import pytesseract
from pdf2image import convert_from_path
from PIL import Image

# PDFファイルのパス
pdf_path = "日本語.pdf"

# PDFを画像に変換
images = convert_from_path(pdf_path)

text = ""

# 各ページの画像に対してOCRを実行
for image in images:
    # 画像を一時的に保存してOCRを実行
    image_path = "temp_image.jpg"
    image.save(image_path)

    # OCRを実行してテキストを取得
    page_text = pytesseract.image_to_string(Image.open(image_path), lang='jpn')  # 言語データに合わせて指定

    # テキストを結合
    text += page_text

# テキストを表示
print(text)

```
このコードは与えられたPDFファイルを画像データに変換してOCR化し，結果の文字データを表示するプログラムです．

上から１０行目のpdf_pathにプログラムに与えるPDFファイルのパスを代入します．
```python:OCR.py
pdf_path = "日本語.pdf"
```
また，このプログラムは日本語のテキストが書かれた画像データをOCR化対象としています．

## テキストが日本語以外の場合
OCR化する画像データのテキストが日本語以外の場合，準備で挙げたprepare.txtの４行目末尾に記述されたjpn(言語コード)を変更し，パッケージを再インストールします．


言語コード：
https://tesseract-ocr.github.io/tessdoc/Data-Files-in-different-versions.html

```txt:prepare.txt(テキストが英語の場合)
!apt-get install -qq tesseract-ocr-eng
```


併せて，OCR.pyの下から７行目の変数langに代入する言語コードを変更します．
```python:OCR.py(テキストが英語の場合)
page_text = pytesseract.image_to_string(Image.open(image_path), lang='eng') 
```




# 実行結果
プログラムに与えるPDFファイル↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3187264/fbc93bc4-dfa8-ccee-12f9-828e2c12bed0.png)
画像引用元：https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E8%AA%9E




OCR実行結果↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3187264/84e2293f-64ff-4027-bfe8-3a54c6aa1121.png)

文字の大きさや配置が統一されていればもっと精度が上がるのかもしれません．

# おわりに
今回は，PDFファイルを画像データに変換してOCR化するPythonプログラムを作成しました．今後もPythonや他の言語を使って遊んでみたいと思います．
JavaでINIファイルの読み書き（セクション非対応）

# javaでINIファイルを読み書きする
JavaでINIファイルの読み書きをします、セクション非対応です
諸事情により古いJavaで作ったので Files.writeString, Files.readString を使用していません


``` java
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

class IniFile {
    public static void Write(String fileName, HashMap<String, String> x) throws Exception {
        String y = "";
        for(String k : x.keySet()) {
            y += k + "=" + x.get(k) + "\n";
        }
        //Files.writeString(Paths.get(fileName), y, StandardCharsets.UTF_8);
        List<String> yline = new ArrayList<String>();
        yline.add(y);
        Files.write(Paths.get(fileName), yline, StandardCharsets.UTF_8);
    }
    
    public static HashMap<String, String> Read(String fileName) throws Exception {
        HashMap<String, String> y = new HashMap<>();
        Path file = Paths.get(fileName);
        //String x = Files.readString(file, Charset.forName("UTF-8"));
        byte[] bytes = Files.readAllBytes(file);
        String x = new String(bytes, Charset.forName("UTF-8"));
        String[] lines = x.split("[\r\n]");
        for(String l: lines) {
            String[] a = l.split("=", 2);
            if(a.length == 2) {
                y.put(a[0].trim(), a[1].trim());
            }
        }
        return y;
    }
};
```