OmniAICreator/Qiita-1.07M huggingface dataset:
入力項目の値を無理やり合計（移行検証用）

　社内システムの期次移行処理のうちに、
MicrosoftExcel内のワークブックから、完了になった案件のシートを削除する
というのがあった。
　数千枚になるので、プログラムを書いて処理することにする。それほど難しくもないのだが、消してはいけないシートまで削除していないことをどうやって確かめよう、が気になった。

　削除すべきものが残ってしまった場合は、作業的にはともかく、データ的にはまだましだ。リカバリが可能だからである。しかし削除してはならないものを消してしまうと、いろいろと問題が大きい。
テストを十分にやった、だから大丈夫、と言えるほど私の心臓は太くない。
　なので、データ消失が起こってないこと、を実作業の中で確認することを考えた。
「削除件数+残存件数=元の件数」
で確認でするのが基本だがもう一押しほしい。もしこれが「金額」を含むものであれば、最重要情報なので合計額をあわせれば皆さん納得してくれるのだが「ない」。
要するに、そういった「金額が絡む」システムの移行を多量に手掛けてきた、という経験があるので、どこかの値をチェックしておかないと落ち着かない体質になってしまったのである。

　ところが今回のシートは、そういうものがない。せいぜい日付である。
「よし、日付のシリアル値を合計しよう！」

作りました／動かしました／落ちました。

　日付を入れる欄なのだけど別の値を(メモなのか？)入れていたユーザーさんがいたようです。（入力インターフェースでチェックする仕組みにできない事情があったのだよね。）
　そこで考えたのが
特定セルの値を1文字ずつに分解して、文字コードを取得し、合計してやる、というもの。
　セル値のテキストプロパティを取得し、一文字ずつ切りだしてAscWで文字コードに変換する、と。
　関数自体は文字列を渡すと、文字コードを数値とみた合計を返す、というシンプルすぎるもので済みます。こんな感じ。
```
Function 文字コード値の合計(元文字列) AsLong
Dim ans AsLong
Dim n as long
Dim 元文字列長 As Long
Dim cLetter As String

	ans = 0
	元文字列長 = Len(元文字列)
	From n = 1 To 元文字列長
		cLetter=Mid(元文字列, n, 1)
		ans = ans + AscW(cLetter)
	Next n

	文字コード値の合計 = ans

End Function
```
robloxでassistant その77

# 概要
robloxでassistantやってみた。
練習問題やってみた。

# 練習問題
触れると追いかけてくるパーツを作れ。

# 手順
- ServerScriptServiceに、scriptを追加。
- スクリプトを書く。
```


local part = Instance.new("Part", workspace)
part.CFrame = CFrame.new(0, 2, 0)
part.Touched:Connect(function(hit)
	local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
	if humanoid then
		game:GetService("RunService").Stepped:Connect(function()
			part.CFrame = humanoid.RootPart.CFrame + Vector3.new(3, 1, 3)
		end)
	end
end)




```

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/24092375-908f-e561-e3b0-694f1d435890.png)


以上。
AtCoderで緑になりました

# 祝入緑
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3800972/1e20e11f-21e1-6f59-a225-67e313bdfe28.png)

次回落ちそうなので落ちる前に記事を残しておきます

## 経歴
・26歳
・大学は数学科。地方国立
・プログラミングは大学の講義でC言語を触ったくらい
・現職は物流会社の非エンジニア職です(強いて言えばExcelの基本的な関数を使うくらい)

## AtCoder始めた理由
なんとなく。一応、流れをかいつまんで言うと
業務でこんなことやりたいな→pythonのpandasって何？→難しいな→ネットサーフィン→競技プログラミングって面白そうじゃん！！！って感じです。
結果は残せていませんが、数学オリンピックの勉強を真面目にやっていたりした過去があり、このような競技は本能的に惹かれるのかもしれません。

## 使いこなせると思うアルゴリズム
・二分探索
・UnionFind
・クラスカル法
くらいです。
セグメント木など勉強しましたが使いこなせていません。というより使う問題と出会っていません。
また、尺取り法も勉強しましたが、二分探索で解いているので多分真に理解していないかと。

現状、古のMMORPGゲームで培ったタイピング速度と、平均よりは上であろう数学力を組み合わせた早見え早解きでレートを上げています。自分で言うと恥ずかしいですね。

AtCoderの名前もMisdreavusでやっているのでお時間許される方は提出コードを覗いてみてください。
基本的にACしている問題は、A,B,C+DorEです。5完達成してみたいですね。

## 今後
上記に書いた通り、アルゴリズムの勉強、データ構造の勉強はまったくやっておりません。
言い訳をすると、時間がない。勉強時間を確保出来たら業務で用いる資格だったりの優先度が高いですし。
しかし、一つの壁だと思われている緑になれたのは素直にうれしいですし、これを維持するためにも勉強を週に3時間くらいは続けていきたいですね。
こんな少ない勉強時間ですが、成長はしており、初回から3回目まではA,B,Cの3完だったのが最近は4完出来るようになりましたので成長はしていそうです。こんな勉強時間で黄色になる！！なんて傲慢なことは言いませんが、水色なら数年かけて目指してもいい夢なのでは？と思いますので水色を目指し精進いたします。
robloxでassistant その76

# 概要
robloxでassistantやってみた。
練習問題やってみた。

# 練習問題
PID制御で倒れる壁を立たせよ。

# 手順
- ServerScriptServiceに、scriptを追加。
- スクリプトを書く。
```


local PID = {}
PID.__index = PID
function PID.new(min: number, max: number, kp: number, ki: number, kd: number)
	local self = setmetatable({}, PID)
	self._min = min
	self._max = max
	self._kp = kp
	self._ki = ki
	self._kd = kd
	self._lastInput = 0
	self._outputSum = 0
	self.POnE = true
	return self
end
function PID:Reset()
	self._lastInput = 0
	self._outputSum = 0
end
function PID:Calculate(setpoint: number, input: number)
	local err = (setpoint - input)
	local dInput = (input - self._lastInput)
	self._outputSum += (self._ki * err)
	if not self.POnE then
		self._outputSum -= self._kp * dInput
	end
	self._outputSum = math.clamp(self._outputSum, self._min, self._max)
	local output = 0
	if self.POnE then
		output = self._kp * err
	end
	output += self._outputSum - self._kd * dInput
	output = math.clamp(output, self._min, self._max)
	self._lastInput = input
	return output
end
function PID:Debug(name: string, parent: Instance?)
	if self._debug then
		return
	end
	if not game:GetService("RunService"):IsStudio() then
		return
	end
	local folder = Instance.new("Folder")
	folder.Name = name
	local function Bind(attrName, propName)
		folder:SetAttribute(attrName, self[propName])
		folder:GetAttributeChangedSignal(attrName):Connect(function()
			self[propName] = folder:GetAttribute(attrName)
			self:Reset()
		end)
	end
	Bind("Min", "_min")
	Bind("Max", "_max")
	Bind("KP", "_kp")
	Bind("KI", "_ki")
	Bind("KD", "_kd")
	folder.Parent = parent or workspace
	self._debug = folder
end
function PID:Destroy()
	if self._debug then
		self._debug:Destroy()
		self._debug = nil
	end
end

local part = Instance.new("Part", workspace)
part.Size = Vector3.new(0.5, 9, 5)
part.CFrame = CFrame.new(0, 6, 0)

local pid = PID.new(-1000, 1000, 100, 10, 5)
local attachment = Instance.new("Attachment", part)
local vectorForce = Instance.new("VectorForce", part)
vectorForce.Attachment0 = attachment
local function update(deltaTime: number)
	local tilt = part.Orientation.X
	local force = pid:Calculate(0, tilt)
	vectorForce.Force = Vector3.new(force, 0, 0)
end
game:GetService("RunService").Stepped:Connect(update)





```

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/17ac7bf1-39eb-7dce-77c9-0beb957e239b.png)


以上。
robloxでassistant その75

# 概要
robloxでassistantやってみた。
練習問題やってみた。

# 練習問題
倒れる壁を作れ。

# 手順
- ServerScriptServiceに、scriptを追加。
- スクリプトを書く。
```
local part = Instance.new("Part", workspace)
part.Size = Vector3.new(0.5, 9, 5)
part.CFrame = CFrame.new(0, 6, 0)

```

# 写真


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/9299bf57-aae9-313c-b3a4-282947773e30.png)


以上。
JavaScript 〜JavaScriptの変数について〜

## JavaScriptの変数の型の一覧
```JavaScript
var myVar;            // 定義なし
var myVar = "Hello";  // 文字列
var myVar = 42;       // 数値
var myVar = true;     // boolean
var myVar = null;     // null
var myVar = {};       // オブジェクト
var myVar = [];       // オブジェクト（配列）
var myVar = function() {};  // 関数
var myVar = Symbol(); // symbol

```
## Symbolについて
一意の値を表す。
以下のソースコードのように値が同じでもifではfalseの判定がされる。
```JavaScript
let string1 = Symbol('文字列です');
let string2 = Symbol('id');
console.log(string1 === string2); // false
```

## varとletの違い①
var：
関数の中で宣言された場合、その関数の中でのみ有効。
ブロック（ifやforなど）の中で宣言されても、そのブロックの外でもアクセス可能。
　
let：
ブロック（{}）内で宣言された場合、そのブロック内でのみ有効。

```JavaScript
if (true) {
    var x = 1;
    let y = 2;
}
console.log(x); // 1 (varはブロック外からもアクセス可能)
console.log(y); // ReferenceError (letはブロック外からアクセス不可)
```
## varとletの違い②
var: 同じスコープ内で再宣言が可能。
let: 同じスコープ内で再宣言ができない。同じ変数名を再び使うとエラーが発生する。

```JavaScript
var a = 1;
var a = 2; // 問題なく再宣言可能

let b = 1;
let b = 2; // SyntaxError: Identifier 'b' has already been declared
```

## varとletの違い③
var：
変数宣言がスコープのトップに巻き上げされる。
初期化は元の位置で行われる。「

let：
宣言前にアクセスするとエラーが発生する。

```JavaScript
console.log(c); // cは3→2024/05/27修正　undefinedが正しい。
var c = 3;

console.log(d); // ReferenceError
let d = 4;

```
JavaScriptのserch()メソッド

パターンマッチングする方法で、簡単なメソッドはserch()メソッドです。検索する文字列(正規表現)を引数にしているすると、最初に見つけた文字列の先頭の文字位置を表す数値を返します。マッチする文字が見つからない場合は-1を返します。


```javascript
"hello world".search() // 0
"The quick brown fox jumps over the lazy dog".search("over") // 26
"abcdefghijklmnopqrstuvwxyz".search("s") // 18
"JavaScript".search(/script/ui) // 4
"Puthon".search(/script/ui)     // -1
```


#### 参考

https://www.oreilly.co.jp/books/9784873119700/

https://proengineer.internous.co.jp/content/columnfeature/12345
久しぶりにgithubを開くとエラーが発生した話

# 久しぶりにgituhubを開くとエラーが発生した話

【記事作成の理由】
約2年もの間 github を更新していなかったので久しぶり更新をしようとすると sshkey 関連のエラーが出たので自分で見返す用として記事を作成してみる。

【経緯】
インフラ系から開発系に配属先が変わったので書いたコードをGitに上げながら学習を進めていこうと思った矢先に、sshkey関連のエラーが発生。

```
$ git push origin master

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The RSA host key for github.com has changed,
and the key for the corresponding IP address 20.27.177.113
is unknown. This could either mean that
DNS SPOOFING is happening or the IP address for the host
and its host key have changed at the same time.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:XXXXXXXXXXXXXXX（各ユーザで異なる文字列）
Please contact your system administrator.
Add correct host key in /home/user/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/user/.ssh/known_hosts:1
  remove with:
  ssh-keygen -f "/home/user/.ssh/known_hosts" -R "github.com"
RSA host key for github.com has changed and you have requested strict checking.
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```

git pushを実行すると上記のエラーが発生。
多分"~/.ssh/known_hosts"ファイルを消せ！みたいなことを言われているのだと思うので言われた通りに実行してみる。

```
$ ssh-keygen -f "/home/user/.ssh/known_hosts" -R "github.com"

# Host github.com found: line 1
/home/user/.ssh/known_hosts updated.
Original contents retained as /home/n21003/.ssh/known_hosts.old

```

updatedと返ってきたのでおそらく更新された。
本当に更新されているか確認してみる。

```
$ ssh -T git@github.com

The authenticity of host 'github.com (20.27.177.113)' can't be established.
ECDSA key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXX
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

$ ssh -T git@github.com

Hi user! You've successfully authenticated, but GitHub does not provide shell access.

```

![Screenshot from 2024-05-26 02-13-39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740507/eda4954f-cb5a-6e96-a15a-7742fa1a40f5.png)

無事草を生やすことに成功した。
【TypeScript】ユニオン型の使い方と活用法


現在、TypeScriptを用いた開発を行っています。
そこで初めて**ユニオン型**というものを知りました。

TypeScriptの知見はまだまだ浅いため、
今回知ったこの**ユニオン型**について学び、使い方を調べてみました。

# ユニオン型とは？
まず、ユニオン型とは一体なんなのでしょうか。
[サバイバルTypeScript](https://typescriptbook.jp/reference/values-types-variables/union)には以下のように書かれています。

> TypeScriptのユニオン型(union type)は「いずれかの型」を表現するものです。
> > サバイバルTypeScript ユニオン型（union type）

これだけではいまいちピンとこないので具体例を見てみましょう。

<br>

ユニオン型の型注釈は、2つ以上の型を` | `で繋げて記載します。
下記の例では、変数`favoriteFood`が`string型`または`undefined型`であることを表しています。

```typescript
let favoriteFood: string | undefined;
```

<br>

また、下記のように記載することもできます。
```typescript
type FavoriteFoods = string | undefined;

let favoriteFood: FavoriteFoods; //favoriteFood: string | undefined
favoriteFood = 'sushi'; // OK
favoriteFood = 343;     // NG
```
この場合、変数`favoriteFood`の型は`string型`または`undefined型`であるため、数値である`343`を代入するとエラーが発生します。

<br>

つまりユニオン型は、**変数が持つことのできる型を指定することができる**のです。

# 使い方の例
ユニオン型がどういったものかは理解することができました。
では実際どのように使用できるか考えてみましょう。

## ①代入する値の指定
変数に特定の値のみ代入可能にしたい場合、
ユニオン型を使用することで実現することが可能です。

```typescript
const JapaneseCars = ['TOYOTA', 'HONDA', 'NISSAN', 'MAZDA', 'SUZUKI'] as const;
type JapaneseCar = typeof JapaneseCars[number];
// 'TOYOTA'|'HONDA'|'NISSAN'|'MAZDA'|'SUZUKI'

let myCar: JapaneseCar;
myCar = 'HONDA';   //OK
myCar = 'Mercedes' //NG
```
1行目のように、配列の後ろに`as const`をつけることで
要素を`read-only`な配列であると定義することができます。

2行目の`typeof JapaneseCars[number]`で、なぜ配列の要素をメンバーとするユニオン型を生成できるのかについては[こちらの記事](https://qiita.com/suin/items/25588b2beba7a3fcce4f)が参考になるのでぜひご覧ください。

## ②型ガード
ユニオン型を使用することで、実行時に型をチェックし、誤った操作を防ぐことができます。
下記の例では引数`id`が`number`型だった場合に`string`型へ変更する処理を実装しています。

```typescript
function numberToString(id: number | string) {
    if(typeof id === 'number') {
        return id.toString();
    }
    return id;
}
```

## ③レスポンスの処理
下記のようにレスポンスのハンドリングにも使用できそうです。

```typescript
type SuccessResponse = {success: true; data: any;}
type ErrorResponse = {success: false; error: string;}

function handleResponse(res: SuccessResponse | ErrorResponse) {
    if(res.success) {
        console.log('Data: ', res.data);
    } else {
        console.log('Error: ', res.error)
    }
}
```

# 最後に
ユニオン型は柔軟性があり、様々な条件や処理に応じて使うことができそうです。
こんな風に使っているよなど意見等あればぜひコメントしてください。

それでは。

# 参考文献
https://typescriptbook.jp/reference/values-types-variables/union

https://zenn.dev/sorye/articles/typescript-array-to-union

https://qiita.com/suin/items/25588b2beba7a3fcce4f
cloud sqlでレプリケーションをTerraformで作りたい

cloudsqlでレプリケーションをつくってみました。

下記コード
```main.tf
resource "google_sql_database_instance" "primary" {
  name             = "mysql-primary-instance-name"
  region           = "asia-northeast1"
  database_version = "MYSQL_8_0"
  settings {
    tier = "db-f1-micro"
    backup_configuration {
      enabled            = "true"
      binary_log_enabled = "true"
    }
  }
  
  deletion_protection = false
}

resource "google_sql_database_instance" "read_replica" {
  name                 = "mysql-replica-instance-name"
  master_instance_name = google_sql_database_instance.primary.name
  region               = "asia-northeast1"
  database_version     = "MYSQL_8_0"

  replica_configuration {
    failover_target = false
  }

  settings {
    tier              = "db-f1-micro"
    availability_type = "ZONAL"
    disk_size         = "10"
  }
  # set `deletion_protection` to true, will ensure that one cannot accidentally delete this instance by
  # use of Terraform whereas `deletion_protection_enabled` flag protects this instance at the GCP level.
  deletion_protection = false
}

```

 Terraformでユーザーを追加しようとするとエラーがでます。
 ユーザーはGUIで登録するしかないです。

参考資料
https://zenn.dev/tomoyuki_kato/scraps/9d7e6b1633e921
wsl2にTerraformをインストールする

自分の環境は
WSL2 Ubuntu 24.04 LTSです。

```
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list

sudo apt update && sudo apt install terraform
```

確認
```
terraform --version
```
wsl2にgcloudをインストールする。

自分の環境は
WSL2 Ubuntu 24.04 LTSです。

```
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates gnupg curl sudo
```

```
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
```
```
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
```

```
sudo apt-get update && sudo apt-get install google-cloud-cli
```


下記2つをやっておくこと
```
gcloud init

gcloud auth application-default login
```
Swiftでunsigned shortからshortへ変換する

組込み機器から送られてくるデータ、例えばBluetoothやUSBなどで通信するような場合です。

`short`、`unsigned short`が16bitのシステムでの例です。

通信で数値が送られてくる場合、それが`(signed)short`なのか、`unsigned short`なのかはわかりません。

送られてくる数値が`0x7fff`までならいいですが、`0x8000`からが危険です。

Swiftに用意されている16bit値を扱う`Int16`は、文字どおり符号付きの数値を扱うため、例えば組込み機器から受け取った`0xffff`をそのまま`Int16(0xffff)`としてオブジェクトを作るとエラーになります。
Swiftでの整数`0xffff`は`0x0000_0000_0000_ffff`と同意で、10進数で65,535です。符号付き16bitの最大値である32,767を超えています。
だけど、0xffffは-1として扱って欲しい...

その時は、こうやって初期化します。
```swift
Int16(truncatingIfNeeded: 0xffff)
```
これでオブジェクトの値は-1になります。
最強のデジタル名刺を作ろう！~概要編~

## こんな人に読んでもらいたい
* 初対面の相手に強いインパクトを与えたい人
* VRを気軽に楽しんでみたい人
* 名刺をすぐ切らすほどたくさん使う人
* ビジネスシーンに面白さを取り入れたい人
* 名刺入れをよく忘れる人(笑

## 完成図

![2024052501.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3357738/8eecaa3a-4012-2030-ce22-c93330ff75fd.jpeg)

NFCカードに名刺のシールを貼ったものです。

![2024052502.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3357738/32ca042c-dcb4-2e07-25fd-139c40d1b2e9.jpeg)

カードをNFC対応のスマホに近づけるor右上のQRコードを読み込ますと↑のような画面に飛び、、、

![2024052503.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3357738/9826c5aa-815f-dbff-ba7c-c5d0aa992544.jpeg)

「カメラの使用を許可」するとロゴの上にこのようなVRアバターが出現して手を振ってくれます！

さらに上のタップイメージを押すと、[litlinkで作った自己紹介ページ](https://lit.link/terisuke)にリダイレクトするシステムです。

**litlinkページの「WebVR」項目からもVR体験ができます♪画像の名刺をカメラで写してみてね！**

## 「最強のデジタル名刺」の強いところ
* ロゴからVRが飛び出すという忘れられない強烈なインパクトを与えることができる
* 自分の連絡先を仮想ビジネスカード(.vcf)形式でlilinkに登録することで、渡した人の連絡先に気軽に保存してもらえる
* 厚みもインパクトもある名刺なので紛失しにくい&紛失されにくい
* 連絡先交換はNFCでタッチorQRを読み込んで貰えばいいので、数枚持っているだけで充分足りる　**(重要)**
* 実話：名刺を渡したら相手の会社でプチ話題になって、勝手に連絡先を登録してくれた

## 使ったもの
* NFCカード(Amazonで1枚80~100円くらいで買えます)
* マーカーを印刷するシール(今回は名刺サイズのシールを買って表を印刷しています)
* [Canva](https://www.canva.com/)(ブラウザでおしゃれなロゴ・デザインを作れます！)
* [VRoidStudio](https://vroid.com/studio)(着せ替え人形感覚でVRアバターを作れます！おすすめ！)
* [Blender](https://www.blender.jp/)(オープンソースの３DCG制作ソフト！**完全無料!**)
* [litlink](https://lit.link/)(いろんなリンクを貼ってスマホ向け自己紹介ページを作れます！**無料！**)
* [Glitch](https://glitch.com/)(ブラウザだけ、環境構築なしでコードが書けるオンラインエディタ！プロジェクトごとに勝手にドメインがもらえるのでデプロイ作業不要😳！**無料でがっつり使えます!**)

、、、改めて見るとほとんどカードとシール代しかかかってない笑

## 作成手順
(それぞれの技術記事を隔週で作っていきます！お楽しみに！)
1. NFCカードやマーカー用シールを買って、litlinkで自己紹介ページを作る
2. VRoidStudioでモデルを作る
3. 作ったvrmモデルをBlenderに取り込んで、モーションをつけてglbファイルでエクスポートする←**詰みポイントその１**
4. GlitchでA-Frameというフレームワークを使ってWebVRを作る
5. WebVRlitlinkへのリンクを加えて完成！←**詰みポイントその2**

## 終わりに
「これおもしれぇ！でも自分で作れる気がしない、、、」
と思ったそこのあなた！

### あなたのオリジナルデジタル名刺作成します。

正式サービスリリースは6月下旬予定！
もしそれまでに気になる方は[弊社お問い合わせフォーム](https://cor-jp.com/contact/)から問い合わせてみてね♪

*「Chat with AI Cloudia」から自作AIチャットボットともお話しできます！試してみてね！*

### See you again!
【BizRobo!・DA】操作対象を曖昧（部分）一致で選択したい場合【基本】

## はじめに
今回は、DAでロボ作成している時に必ず発生するであろう内容について記載していきます。

DAでシステムやサイトを操作する際、ループ処理で何度も同じようなウィンドウを操作するシーンが出てくると思います。

その際に、同じようなウィンドウを操作しているが、ウィンドウ名の一部分が違う、という事がよくあります。

その場合には**ウィンドウ名の部分一致**が出来るので、そちらを活用しましょう。

## 方法
１．DAステップ内で、Click Step等を作成する。

２．ApplicationやComponent欄が[name="○○_<font color="Red">可変になる部分</font>"]のようになっている部分を[name<font color="Red">*</font>="○○"]のように変更する。

以上で曖昧(部分)一致でウィンドウなどが選択できるようになります。

## 補足
Design StudioはJavaを用いています。

演算子や正規表現(上記のようなもの)をどのように設定するのか知りたいときは、**「Java 演算子」「Java 正規表現」** などで検索すると、役に立つ情報が出てくると思います。
AppStore Connectアップロード後のLSMinimumSystemVersionによるリジェクト

iOSアプリをAppStore Connectにアップロードした後、以下のようなメッセージを含むメールがAppleから送られてきました。

![スクリーンショット 2024-05-26 0.47.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/03e047ad-d8dd-1a65-6883-265b0037b73a.png)

Apple silliconを搭載したMacでの対応OSに関するもののようです。

メールの記載にあるように`LSMinimumSystemVersion`を14.0って感じに設定すればいいみたいです。

![スクリーンショット 2024-05-26 0.50.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/9504ac8b-b96f-7560-a7cf-33dffef9f57a.png)

これでアップロードしたところ、リジェクトのメールは来ませんでした。
多分iOSアプリの対応OSバージョンとの関係でしょう。

こちらのサイトが参考になりました。
https://utro-develop.com/minimumsystemversion/
https://zenn.dev/samekard_dev/articles/29ccf2339ab347
iOSデバイスのBluetooth許可アラートメッセージをローカライズする

iOSデバイスでBluetoothを使用する際、初めてアプリを起動すると接続許可アラートメッセージが表示されます。

![Bluetooth Alert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/b169c277-27b1-2036-4eef-e21feb91a1af.png)

このメッセージはXcodeのInfoの項目で設定しています。

![スクリーンショット 2024-05-26 0.22.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/7bbce496-34ef-0ea2-72e7-33229837b560.png)

このメッセージのローカライズするには、アプリ名のローカライズと同じ「InfoPlist.strings」の中に記載します。

:::note info
「InfoPlist.strings」のローカライズ設定が必要です
![スクリーンショット 2024-05-26 0.42.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/d27e29a2-84e6-34ce-4bc0-7f1a3bde52ba.png)

:::

```
NSBluetoothAlwaysUsageDescription = "ハンディ水検知チェッカーに接続します";
```

無事、日本語にローカライズできました。

![Bluetoothアラート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202602/c9faac69-9030-4383-3ddf-6d4db8fd8758.png)


`NSBluetoothAlwaysUsageDescription`キーの情報はAppleのサイトに記載されています。
Bluetooth以外にもトラッキングに関するキーなども記載されているので、メッセージのローカライズに役立ちそうです。

[NSBluetoothAlwaysUsageDescription](https://developer.apple.com/documentation/bundleresources/information_property_list/nsbluetoothalwaysusagedescription)
【Rust】mockallでnewをモックする

new関数を含む以下のようなトレイトをmockallでモックするのに手間取ったので、その手順をメモする

```rust
trait Foo {
    fn new(x: u32) -> Self;
}
```

# 手順

コード全体としては以下のようになる。

```rust
#[automock]
trait Foo {
    fn new(x: u32) -> Self;
}

#[test]
fn mock_new() {
    let foo = MockFoo::default();
    let foo_ctx = MockFoo::new_context();

    foo_ctx.expect()
        .return_once(|_| foo);
}
```

手順を分解して説明すると、以下の1. ~ 3.の通りとなる

### 1. モックの実装

モックの実装手順はいつもの手順と同じ

```rust
#[automock]
trait Foo {
    fn new(x: u32) -> Self;
}
```

### 2. モックの実体化

モックの実体化は以下のどちらかで行う

```rust
// デフォルト値で実体化
let foo = MockFoo::default();
// 引数を与えて実体化
let foo = MockFoo::new(1);
```
- 2番目の方法について、いつもの手順で使われる引数なしのnew関数(=`MockFoo::new()`)は実装されず、以下のようなエラーが出るので注意する

    ```rust
      |
    2 |     let foo = MockFoo::new();
      |               ^^^^^^^^^^^^-- an argument of type `u32` is missing
      |
    ```

### 3. new関数の期待する呼び出しを定義する

new関数に対応するコンテキスト?を作成して、そのコンテキストで期待する呼び出しを実装するようにする

```rust
let foo_ctx = MockFoo::new_context();

// expect()以降のreturn_onceなどについては、return_constなど他のいつものやつも使えるはず
foo_ctx.expect()
    .return_once(|_| foo);
```

- いつものように`expect_new()`とするのではなく、単に`expect()`とする
- コンテキスト作成の際に、newを指定している理解(`new_context()`)

# 一般的な話 : 静的メソッドのモック

上記手順は、静的メソッド(=`self`を含まないメソッド)をモックする場合も同様となる

```rust
#[automock]
trait Foo {
    fn bar(x: u32) -> u32;
}

#[test]
fn mock_bar() {
    // [関数名]_context()で静的メソッドのコンテキストを作成できる
    let foo_bar_ctx = MockFoo::bar_context();

    foo_bar_ctx.expect()
        .return_const(1u32);
}
```

# +ε : いつものモックと静的メソッドのモックを組み合わせる

いつものモック=静的じゃないメソッドのモック(と思っている)

```rust
#[automock]
trait Foo {
    // new関数=静的メソッド
    fn new(x: u32) -> Self;
    // いつものほう
    fn bar(&self) -> u32;
}

#[test]
fn mock_new_bar() {
    // MockFoo::new(1)でもよいと思われる
    let mut foo = MockFoo::default();
    let foo_ctx = MockFoo::new_context();

    // 二つ目の文でfooの所有権が移動するので先にbarの期待する呼び出しを定義する
    // (他にいい方法があるかもしれない、、、)
    foo.expect_bar()
        .return_const(1u32);
    foo_ctx.expect()
        .return_once(|_| foo);
}
```

# 参考

- ドキュメント

https://docs.rs/mockall/latest/mockall/index.html#static-methods

- 関連してそうなソースコード

https://github.com/asomers/mockall/blob/master/mockall/tests/automock_constructor_in_trait_with_args.rs

- いつもの`MockFoo::new()`が実装されない理由

https://github.com/asomers/mockall/issues/334

https://github.com/asomers/mockall/issues/3
GPT-4 Omniがアップデートした件について

この記事を投稿した約2日前にGPT-4 Omniのアップデートが行われました。特に、データ分析機能とPowerPointプレゼンテーション生成機能について更新があったので、今回はそれら2つのことについて紹介させていただきます。

## アップデート内容

### データ分析機能
- **GoogleドライブとMicrosoft OneDriveアカウントを接続**して、ChatGPTで直接データにアクセスできるようになりました。ユーザーはグラフやチャートをカスタマイズしたり、リアルタイムで操作したりすることができます。
たとえば、ユーザーはGPT-4 Omniに特定のグラフを生成するように指示したり、データに関する質問をしてさらに洞察を得たりできます。（今まではGoogle Geminiがそれに近い機能を果たしていたので、OpenAIのGPTを愛用している人にはかなりの朗報です！！）

### PowerPoint生成
- **ユーザーの指示に基づいてデータを要約したり、PowerPointプレゼンテーションを作成**できるようになりました。（今まではPowerPoint作成用のAIツールを活用しないとできなかったので、これが一元化できたことはかなり素晴らしいです！！）ユーザーは、GPT-4 Omniに特定の分析を実行するように指示したり、特定の方法でデータを可視化するように指示したりできます。また、データを提示するためにさまざまなグラフを生成することもできます。（棒や円、折れ線など様々な形式に対応できますが、まだ私自身も全て試したことはないので、調査中です。）

## まとめ
全体として、このアップデートにより、GPT-4 Omniはデータ分析機能を大幅に向上させ、さまざまなビジネスニーズに役立つ実用的なツールに近づきつつあると分かります。現時点では、ChatGPT Plusユーザーにのみこれらの機能にアクセスできていますので、無料でもできるようになったら是非皆さんも試してみてください。
ruby練習

今回は簡単なプログラムを作ってメソッドや配列を使う練習をしたものを言語化して整理します。
#### 仕様
アプリケーションを起動すると 
##### [1] : 書籍を登録する, [2] : 書籍情報を確認する, [3] : アプリケーションを終了する
という選択肢を表示し、ユーザーに入力を求め、その入力に従い以下の処理を行う。
アプリケーションを終了するまで、処理を繰り返す。

#### [1]の処理
名前、著者名、ジャンル、ページ数を入力させ、保存する
#### [2]の処理
登録された書籍情報から番号と名前で一覧を表示し（例 [1]ハリーポッター）、見たい書籍の番号の入力を求める。
入力された書籍の名前、著者名、ジャンル、ページ数を表示する。
#### [3]の処理
アプリケーションを終了する

```ruby:雛形
def registration_book
  # 書籍の名前、著者名、ジャンル、ページ数を登録できるようにしなさい
  book = {}
  puts '書籍名を入力してください'
  puts '著者名を入力してください'
  puts 'ジャンルを入力してください'
  puts 'ページ数を入力してください'
end

def show_book_info
  # 登録された書籍の名前を番号を振って表示しなさい
  puts '見たい書籍の番号を入力してください'

  # 選択された書籍の名前、著者名、ジャンル、ページ数を表示できるようにしなさい
  puts "書籍名:"
  puts "著者名:"
  puts "ジャンル:"
  puts "ページ数:"
end

books = []

while true
  puts '行いたい項目を選択してください'
  puts '[1]書籍を登録する'
  puts '[2]書籍情報を確認する'
  puts '[3]アプリケーションを終了する'
  input = gets.to_i
  if input == 1
    registration_book
  elsif input == 2
    show_book_info
  elsif input == 3
    exit
  else
    puts '無効な値です'
  end
end
```
```ruby:[1]の処理
1 def registration_book(books)
2  # 書籍の名前、著者名、ジャンル、ページ数を登録できるようにしなさい
3  book = {}
4  puts '書籍名を入力してください'
5    book[:name] = gets.chomp
6  puts '著者名を入力してください'
7    book[:author] = gets.chomp
8  puts 'ジャンルを入力してください'
9    book[:genre] = gets.chomp
10  puts 'ページ数を入力してください'
11    book[:page] = gets.to_i
12  books << book
13 end
```
引数(books)を指定してメソッドの外部からデータを受け取る。
booksには書籍情報が格納されている。

bookハッシュにname,author,genre,pageをそれぞれ入力させる。

books << bookでbooksという配列にbookハッシュを格納する。
最終的に以下のようになる。
```ruby:books配列
books = [{name: (入力された値1), author: (入力された値2), genre: (入力された値3), page: (入力された値4)}, {name: (入力された値1), author: (入力された値2), genre: (入力された値3), page: (入力された値4)}, ......]
```
```ruby:[2]の処理
1  def show_book_info(books)
2    i = 0
3   # 登録された書籍の名前を番号を振って表示しなさい
4    books.each do |book|
5      puts "#[{i}:#{book[:name]}"
6      i += 1
7    end
8    puts '見たい書籍の番号を入力してください'
9      num = gets.to_i
10   book = books[num]
11
12# 選択された書籍の情報を表示できるようにしなさい
13  puts "書籍名:#{book[:name]}"
14  puts "著者名:#{book[:author]}"
15  puts "ジャンル:#{book[:genre]}"
16  puts "ページ数:#{book[:page]}ページ"
17 end
18
19 books = []
```
4～6行目で登録された書籍を**番号：書籍名**という形で表示する。
6行目は番号が1ずつ増えていくようにしている。
10行目は配列booksから特定の要素（書籍）を選択するためのコード。
　　選択された書籍情報がbookに代入される。

```ruby:
1 while true
2  puts '行いたい項目を選択してください'
3  puts '[1]書籍を登録する'
4  puts '[2]書籍情報を確認する'
5  puts '[3]アプリケーションを終了する'
6  input = gets.to_i
7  if input == 1
8    registration_book(books)
9  elsif input == 2
10    show_book_info(books)
11  elsif input == 3
12    exit
13  else
14    puts '無効な値です'
15  end
16 end
```
ループ処理で繰り返し選択できるようにする。
5行目の指定で無限ループから抜け出す処理を入れている。
7～12行目でプログラムを実行。

配列とハッシュが組み合わさる点が複雑に感じてまだまだ苦手な部分です。
引数の指定も忘れがちなのでもっとたくさん問題を解いてマスターします！！
textlintを使ってカスタム文章チェック

# 概要
マークダウンで記事を書くことがあったので、文章の内容をチェックするtextlintの導入方法についてまとめる。
今回は独自のルールを指定したかったので、設定方法について記載。


https://github.com/textlint/textlint

## ローカル環境

* macOS：Ventura(13.6)
* npmとnodeのバージョン

```
$ npm --version
10.2.4

$ node --version
v21.6.2
```

## 設定後のディレクトリ

```
├── .textlintrc.json
├── custom_rule.yml
├── node_modules
│  ├── xxxxxxxxx
:  :  :
│  │  └── yyyyyy
│  └── zzzzzzzzz
:  
├── package-lock.json
├── package.json
└── text
    └── sample.md

```

# 手順

## text-lintパッケージをローカルインストール

```
$ npm install --save-dev textlint-rule-preset-ja-technical-writing
```

・node_modulesディレクトリ
・package-lock.jsonファイル
・package.jsonファイル
が作成される。

## textlint 初期化

```
$ npx textlint --init
```

.textlintrc.jsonファイルが作成される。


## 独自ルール設定

custom_rule.ymlファイル作成し、ルールを設定していく。
ここではWebという文字の揺らぎをチェックする。

```custom_rule.yml
version: 1
rules:
  - expected: Web
    pattern: WEB
  - expected: Web
    pattern: web
  - expected: Web
    pattern: ウェブ
```

## 設定ファイル更新

.textlintrc.jsonファイルを更新し、独自ルールを設定したファイルをみるようにする。

```.textlintrc.json
{
  "rules": {
    "preset-ja-technical-writing": true,
    "prh": {
        "rulePaths": [
            "./custom_rule.yml"
        ]
    }
  }
}
```


## ファイル作成

textフォルダ配下にsample.mdファイルを作成し、文章を記載する。

```/text/sample.md
私はWEBエンジニアです。
私はWebエンジニアです。
私はwebエンジニアです。
```

## 文章チェック

```
$ npx textlint ./text/sample.md

/{プロジェクト}/text/sample.md
  1:3  ✓ error  WEB => Web  prh
  3:3  ✓ error  web => Web  prh

✖ 2 problems (2 errors, 0 warnings)
✓ 2 fixable problems.
Try to run: $ textlint --fix [file]
```

# まとめ

技術文書向けのルールリポジトリはこちら
https://github.com/textlint-ja/textlint-rule-preset-ja-technical-writing
【ABC338】FortranでA,B,C問題

## 目次
  - [A - Capitalized?](#a---capitalized?)
  - [B - Frequency](#b---frequency)
  - [C - Leftover Recipes](#c---leftover-recipes)


## A - Scoreboard
### 問題

https://atcoder.jp/contests/abc338/tasks/abc338_a


### 解説
文字列 S を１文字ずつ**ASCIIコード**へ変換し、大文字と小文字の判定を行います。

ASCIIコードが９１以上なら小文字、９1未満なら大文字になります。
ASCIIコードへの変換は`ichar関数`を用います。

http://www3.nit.ac.jp/~tamura/ex2/ascii.html


### プログラム例
```Fortran
program abc338a
    !S：文字列
    implicit none
    character(100) S
    integer i

    !入力
    read (*, *) S

    !１文字目が大文字かどうか
    if (ichar(S(1:1)) > 90) then
        write (*, '(a)') 'No'
        stop
    end if

    !2〜100文字目が小文字かどうか
    do i = 2, len_trim(S)
        if (ichar(S(i:i)) < 91) then
            write (*, '(a)') 'No'
            stop
        end if
    end do
    write (*, '(a)') 'Yes'
end program abc338a
```



##  B - Frequency
### 問題

https://atcoder.jp/contests/abc338/tasks/abc338_b

### 解説
文字列 S を１文字ずつ**ASCIIコード**へ変換し、`変数char_cnt`に出現回数を記録します。

最頻出の回数の取り出しは、`maxval関数`を`変数char_cnt`に対して行うことで求めます。
再頻出回数は`変数char_max`に格納します。

結果の出力は、`変数char_cnt`を１から順に調べ、`変数char_max`と値が一致するアルファベットを出力します。
`変数char_cnt`を１から順に調べることで、再頻出のアルファベットが複数個ある場合にも、辞書順の若い順番の答えとして出力が可能です。

### プログラム例
```Fortran
program abc338b
    !S       ：文字列
    !char_num：変換後のASCIIコード
    !char_cnt：各アルファベットの出現回数を記録
    !char_max：再頻出の文字の出現回数
    implicit none
    character(1000) S
    integer i, char_num, char_cnt(127), char_max

    !入力
    read (*, *) S

    !各アルファベットの出現回数を記録
    char_cnt = 0
    do i = 1, len_trim(S)
        char_num = ichar(S(i:i))
        char_cnt(char_num) = char_cnt(char_num) + 1
    end do

    !再頻出の文字の出力
    char_max = maxval(char_cnt)
    do i = 1, 127
        if (char_max == char_cnt(i)) then
            write (*, '(a)') char(i)
            stop
        end if
    end do
end program abc338b
```



## C - Leftover Recipes
### 問題

https://atcoder.jp/contests/abc338/tasks/abc338_c

### 解説 
$1≤N≤10$であるため、料理Aと料理Bの作成数の組み合わせを2重の`for文`で**総当たりするとTLE**になります。
料理Aの作成数を先に決めておき、その後に余った材料数から料理Bの作成できる最大個数を求めることで、**計算量を削減**できます。

初期の材料在庫を`変数Q(i)`、料理Aに必要な材料数を`変数A(i)`、料理Aの作成個数を`変数x`、とする時、余った材料数`変数Qr(i)`は、`Qr(i)=Q(i)-x ×A(i)`で求められます。

料理Bに必要な材料数を`変数B(i)`とした時、料理Bの作成個数`変数y`は、`y=Qr(i)/B(i)`により求められます。


### プログラム例

```Fortran
program abc338c
    !x     ：料理Aの作成個数
    !y     ：材料B(i)のみ作成できる料理Bの数
    !min_y ： 料理Bの作成個数
    !max_xy：料理ABの最大個数
    !Q     ：初期の材料在庫
    !A,B   ：料理A,Bを作成するのに必要な材料個数
    !Qr    ：料理A作成後に余ったの材料在庫
    implicit none
    integer i, j, x, y, min_y, max_xy
    integer N
    integer, allocatable::Q(:), A(:), B(:), Qr(:)

    !入力
    read (*, *) N
    allocate (Q(N), A(N), B(N), Qr(N))
    read (*, *) (Q(i), i=1, N)
    read (*, *) (A(i), i=1, N)
    read (*, *) (B(i), i=1, N)

    !料理作成数の組み合わせ検証
    min_y = 1000000000
    max_xy = 0; x = 0
    L1: do
        L2: do i = 1, N
            Qr(i) = Q(i) - A(i)*x
            if (Qr(i) < 0) exit L1
            if (B(i) == 0) cycle
            y = Qr(i)/B(i)
            min_y = min(y, min_y)
        end do L2
        max_xy = max(max_xy, min_y + x)
        x = x + 1
    end do L1

    !結果の出力
    write (*, *) max_xy
end program abc338c
```
JavaScriptに関するdelete演算子

この記事はJavaScriptのdelete演算子について学習し疑問に思ったことをまとめて書いてみました。

delete演算子の前に配列について簡単に説明します。
## 配列とは？
- **配列**：順番に並んだデータの集まり。
- **インデックス**：配列の各要素には番号がついていて、最初の要素は0番、次は1番というように番号が続きます。

## delete演算子とは？
- **delete演算子**：配列の特定の要素を削除するために使います。

### deleteを使う例
```javascript
var animals = ["dog", "cat", "rabbit"];

delete animals[1];
console.log(animals); // ["dog", undefined, "rabbit"]
```
`animals[1]`（"cat"）を削除しました。削除された場所は`undefined`になり、他の要素（"dog" と "rabbit"）はそのままです。

## なぜ削除しても他の要素は動かないのか？
- **配列の要素は特定の場所にある**：配列の要素は、それぞれ特定の場所（インデックス）にあります。`delete`はその場所を空っぽにするだけで、他の要素を動かしません。
- **動かすと時間がかかる**：もし削除した場所を詰める（繰り上げる）とすると、残りのすべての要素を動かす必要があります。大きな配列になればなるほどもっと時間がかかります。

## 動かすと時間がかかる具体例
**配列の大きさと時間**：要素の削除と繰り上げ処理にかかる時間は、要素数に比例します。

#### 小さな配列（10要素）
```javascript
var smallArray = Array.from({ length: 10 }, (_, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.time('smallArrayDelete');
smallArray.splice(5, 1); // 5番目の要素を削除
console.timeEnd('smallArrayDelete');
```
結果：繰り上げ処理にかかる時間は非常に短く、ミリ秒未満です（ほぼ瞬間的）。

#### 中くらいの配列（1,000要素）
```javascript
var mediumArray = Array.from({ length: 1000 }, (_, i) => i); // [0, 1, 2, ..., 999]
console.time('mediumArrayDelete');
mediumArray.splice(500, 1); // 500番目の要素を削除
console.timeEnd('mediumArrayDelete');
```
結果：繰り上げ処理にかかる時間は数ミリ秒です。

#### 大きな配列（1,000,000要素）
```javascript
var largeArray = Array.from({ length: 1000000 }, (_, i) => i); // [0, 1, 2, ..., 999999]
console.time('largeArrayDelete');
largeArray.splice(500000, 1); // 500,000番目の要素を削除
console.timeEnd('largeArrayDelete');
```
結果：繰り上げ処理にかかる時間は数百ミリ秒から数秒です。

具体例は分かったけど、どうすればよいのか・・・？

## 解決策
要素を削除して他の要素を詰めたい場合は、`splice`メソッドを使います。

#### spliceを使う例
```javascript
var animals = ["dog", "cat", "rabbit"];

animals.splice(1, 1); // 1番目の要素を1つ削除
console.log(animals); // ["dog", "rabbit"]
```
"cat"が削除され、"rabbit"が1番目に詰められます。

## まとめ
- **delete演算子**：要素を削除するとその場所が`undefined`になり、他の要素は動かない。
- **なぜ動かないのか**：配列の要素は特定の場所にあり、動かすのに時間がかかるから。
- **具体例**：小さな配列では瞬間的、中くらいの配列では数ミリ秒、大きな配列では数百ミリ秒から数秒かかる。
- **spliceメソッド**：要素を削除して他の要素を詰めたいときに使う。

このように、`delete`演算子は効率を保つために要素を削除しても他の要素を動かさず、`splice`メソッドを使うことで必要に応じて詰めることで解決できます。
決定木の異常検知アルゴリズム

機械学習というと「教師あり学習」「教師なし学習」(と「強化学習」)がフィーチャーされますが、その中でやり方が「教師なし学習」と似ている「異常検知」があります。
異常検知では正常値が1となり、異常値が-1という風にクラスタリングと似たようになります。
ではその中でIsolationForestという異常検知アルゴリズムを使って見ましょう。
# 分かりやすく賃金指数で
昔のデータで今は違う値になっていますが、分かりやすいデータとして賃金データを使います。
なぜ分かりやすいかというと、賃金データにはボーナス月は異常に高くなるという性質があります。それを考慮して見てみましょう。
```Python3
from sklearn.ensemble import IsolationForest
from sklearn.decomposition import PCA
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("sarrary.csv", encoding="shift-jis")
df.head()

x = df["賃金指数"]
model = IsolationForest()
model.fit(x.values.reshape(-1, 1))
y_pred = model.predict(x.values.reshape(-1, 1))
plt.scatter(df["年月"], df["賃金指数"], c=y_pred, cmap="brg")
plt.colorbar()
plt.show()
```
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/7e2e80ec-b2df-870c-0c6d-39f8db00bdc4.png)
ボーナス月に一部正常データがありますが、おおむね異常値は当たっています。ただそれでも一部正常値にも異常値が混じっています。

# アヤメデータでやってみる
さっきのプログラムの続きです。
```Python3
df = pd.read_csv("iris.csv")
x = df.drop("category", axis=1)

model = IsolationForest()
model.fit(x)
y_pred = model.predict(x)
print(y_pred)
pca = PCA()
for col in x.columns:
    x[col] = (x[col] - x[col].mean()) / x[col].std()
pca.fit(x)
tx = pca.transform(x)
plt.scatter(tx[:, 0], tx[:, 1], c=y_pred, cmap="brg")
plt.colorbar()
plt.show()
```
```
[ 1  1  1  1  1  1  1  1 -1  1  1  1 -1 -1 -1 -1 -1  1 -1  1  1  1 -1 -1
 -1  1  1  1  1  1  1  1 -1 -1  1  1  1  1  1  1  1 -1 -1 -1 -1  1  1  1
  1  1  1  1  1  1  1  1  1 -1  1  1 -1  1 -1  1  1  1  1  1  1  1  1  1
  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1 -1  1  1
  1  1 -1  1 -1  1  1  1  1 -1 -1 -1 -1 -1  1  1  1  1 -1  1  1 -1 -1 -1
  1  1 -1  1  1 -1  1  1  1 -1 -1 -1  1  1  1 -1 -1  1  1  1  1  1  1  1
 -1  1  1  1  1  1]
```
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/f69d8a83-16e9-4dcb-6bd2-ad2099e402ed.png)
```Python3
plt.scatter(tx[:, 0], tx[:, 1], c=df["category"], cmap="brg")
plt.colorbar()
plt.show()
```
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/de54cd0a-056c-bb2b-986b-d7eae168dae2.png)

こうやってみると各クラスまたはクラスタの外れ値が異常値になっているような感じに見えますね。
ちなみに各変数ごとにプロットするとこんな感じらしいです。
```Python3
for col in x.columns:
    df[col] = (df[col] - df[col].mean()) / df[col].std()
sns.pairplot(df, hue="category", palette="Pastel1")
plt.show()
```
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/07bb86ce-db76-c9b4-93ed-b483c5df0b71.png)

これを異常検知の予測結果で散布図を見てみると
```Python3
df_if = pd.DataFrame(y_pred)
df_if.columns = ["pred"]
df = pd.concat([df, df_if], axis=1)

sns.pairplot(df.drop("category", axis=1), hue="pred",  palette="Pastel1")
plt.show()
```
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2782889/fa1ad8f2-7ec6-22a4-5f62-342d0821ec92.png)
こんな形で異常検知をしているみたいです。


# まとめ
色々使えそうです。
【WinActor】メール送信ステップでファイル添付をする場合の注意

## はじめに
RPAでロボットを実行する時に、必ず発生するであろう処理の一つである、**「ロボット実行完了の通知メール送信」**。

今回は、**WinActor**で完了メールを送信する際に、一緒にファイル添付も行いたい場合の手順と注意点について記載していきます。

## 手順
１．メール送信ライブラリを設置します。

２．プロパティを開き、スクリプトタブを選択します。

３．ファイル添付がコメントアウトされているので、コメントアウトを削除します。

４．設定タブに戻り、ファイル添付の欄に**添付ファイルのフルパス**が入った変数の情報を設定します。

以上になります。

## 注意点
WinActorでファイルの場所を指定する時は相対パスと絶対パスが使用できます。

しかし、メール送信ライブラリで添付するファイルを指定する場合は、**必ず絶対パスの情報を指定しないとファイル添付が出来ない**ので気をつけてください。
[Vue3]同じRouteの中でパラメータだけ変わる時、スクロールの位置が初期化できない

## 起きたこと

基本スクロールが`top: 0`じゃないことはないので、routerが変わった時はスクロール位置を0に初期化したい。

そのため書いた下記のコード

```ts:router.ts
export const routes = [
  {
    path: '/',
    name: 'home',
    component: MainPage
  },
  {
    path: '/gallery/:id(\\d+)',
    component: GalleryPage,
    scrollBehavior(to, from, savedPosition) {
      return { top: 0 }
    },
  },
]

export const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    return { y: 0 }
  }
})
```

![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/5b936e4e-181c-fd23-8c75-57c94c6bfed6.gif)

## 開発環境
```json:package.json
{
    "vue": "^3.4.21",
    "vue-router": "^4.3.0",
}
```

## 解消方法

似たようなコードを書くたびにvueの開発思想に合っているか自信はない。
とりあえずDOM要素を触ったら動いた。

```ts:router.ts
export const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior(to, from, savedPosition) {
    document.getElementById("app").scrollTop = 0; // #appを直接触る
  }
})
```
Redhat Enterprise Linux Server 6.3をVirtualboxへインストールしてPython3.7.17をビルドする手順

# はじめに

古いOSでPython3.7をインストール必要があり、環境構築からPython3.7.17をビルドするまでの手順をまとめます。

# 環境構築

Windows10にVirtualboxをインストールし、仮想環境にRedhat Enterprise Linux Server 6.3をインストールします。

Redhat Enterprise Linux Server 6.3のインストールメディア(ISOファイル)はRedhatのサポートページへログインしてダウンロードします。

Virtualbox 7.0.18ではインストールDVDが起動しませんでした。
Virtualbox 6.1.50は起動できました。

RedhatのインストールはSoftware Developmentを選択します。
デフォルトのBasicではビルドに必要なソフトウェア(gccなど)がインストールされません。

# RedhatへSSHログインする

macOSからRedhatへSSHでアクセスするとエラーが表示されました。

```bash
$ ssh root@192.168.3.128
Unable to negotiate with 192.168.3.128 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss
```

オプションを付けてsshコマンドを実行します。

```bash
ssh -oHostKeyAlgorithms=+ssh-rsa -oPubkeyAcceptedAlgorithms=ssh-dss,ssh-rsa root@192.168.3.128
```

# Python3.7.17をビルドする

Python3.7.17のビルドに必要なソフトウェアをインストールします。
rootユーザで行う前提で手順を記載します。

- openssl 1.1.1

https://www.openssl.org/source/old/1.1.1/openssl-1.1.1w.tar.gz

```bash
cd
tar xvzf openssl-1.1.1w.tar.gz
mv openssl-1.1.1w /usr/local/openssl
cd /usr/local/openssl
./config --shared --prefix=/usr/local/
make
make install
echo "/usr/local/openssl" > /etc/ld.so.conf.d/openssl.conf
ldconfig
```

- tcl8.6.14

https://www.tcl.tk/software/tcltk/download.html

```bash
cd
tar xvzf tcl8.6.14.tar.gz
cd tcl8.6.14/unix
./configure --prefix=/usr/local
make
make install
```

- tk8.6.14

https://www.tcl.tk/software/tcltk/download.html

```bash
cd
tar xvzf tk8.6.14.tar.gz
cd tk8.6.14/unix
./configure --prefix=/usr/local
make
make install
echo "/usr/local/lib" > /etc/ld.so.conf.d/tcltk.conf
ldconfig
```

- libffi

https://github.com/libffi/libffi/releases/download/v3.4.6/libffi-3.4.6.tar.gz

```bash
cd
tar xvzf libffi-3.4.6.tar.gz
cd libffi-3.4.6
./configure --prefix=/usr/local
make
make install
echo "/usr/local/lib64" > /etc/ld.so.conf.d/libffi.conf
ldconfig
```

-- xz

https://github.com/tukaani-project/xz/releases/download/v5.4.6/xz-5.4.6.tar.gz

```bash
cd
tar xvzf xz-5.4.6.tar.gz
cd xz-5.4.6
./configure
make
make install
```

- Python3.7.17

https://www.python.org/ftp/python/3.7.17/Python-3.7.17.tgz

```bash
cd
tar xvzf Python-3.7.17.tgz
cd Python-3.7.17
export CFLAGS="-I/usr/local/include -I/usr/local/openssl/include"
export LDFLAGS="-L/usr/local/lib64 -L/usr/local/lib -L/usr/local/openssl/lib"
export LD_RUN_PATH=/usr/local/lib64:/usr/local/lib:/usr/local/openssl/lib:/usr/lib:/usr/lib64
export LD_LIBRARY_PATH=/usr/local/openssl/lib:/usr/local/lib64:/usr/local/lib:/usr/lib:/usr/lib64
export PKG_CONFIG_PATH=/usr/local/ssl/pkgconfig:/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib:/usr/lib64/pkgconfig
./configure --with-openssl=/usr/local/openssl --enable-optimizations
make
make altinstall
```

# 参考情報

Python3.7をビルドする場合、OpenSSL 1.0.2、または、1.1.1以上が必要だが、1.0.2をインストールすると、urllib3でエラーが出る。

```bash
ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2u  20 Dec 2019'. See: https://github.com/urllib3/urllib3/issues/2168
```
ファイルシステムとは

# ファイルシステムを介してアクセスする
各種デバイスはデバイスファイルを介してアクセスできると述べました。しかしストレージデバイスは、ほとんどの場合は、本章で述べる```ファイルシステムを介してアクセス```します。
ファイルシステムが存在しない場合、データをディスク上のどの位置に保存するかを```自分で決めなければなりません```。その時に、他のデータを壊さないために、```空き領域の管理もしなくてはいけません```。
...
ファイルシステムはこのような管理を代行してくれます。ファイルシステムは```ユーザにとって意味のある一塊のデータをファイルという単位で管理```します。

# ファイルシステムの意味
「```ファイル形式でデータを管理しているストレージ上の領域```」と、「```そのストレージ領域を扱う処理```」のどちらもファイルシステムと呼びます。

# ファイルシステムの扱うデータ
### データ
ユーザが作成した文章や画像、プログラムなど
### メタデータ
```ファイルを管理する```ためにファイルシステム上に存在する付加的な情報です。
# メタデータの種類
### ファイルの名前
### ストレージデバイス上の位置やサイズ
### ファイルの種類
通常のファイルか、ディレクトリか、デバイスファイルか、など
### ファイルの時刻情報
作成した日時、最後にアクセスした日時、最後に内容を変更した日時
### ファイルの権限情報
どのユーザかがファイルにアクセスできるか
### ディレクトリのデータ
ディレクトリの中にどのような名前のファイルが入っているか、など

# 出典
https://gihyo.jp/book/2022/978-4-297-13148-7

# 感想
ストレージデバイスにアクセスするときはファイルシステムが代わりにアクセスしてくれてストレージデバイスを管理してくれるからありがたい。
CSS アニメーションでネオンサインの点灯の雰囲気を出してみる

SVGのロゴをネオンサインが点灯するようなイメージのアニメーションにできないかと試行錯誤した結果を記事に残しておく。
## 最終完成バージョン
まずは出来上がりを、Codepen で確認してほしい。Start ボタンを押すと、ロゴが点灯する。
ネオンサインが点灯する雰囲気にみえるとうれしい。
（と言っても、今となってはネオン管っぽいLEDチューブが主流なので、本物のネオン管を見たことがない世代が多いかもしれないが...）
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="PovGOdR" data-pen-title="Neon Sign" data-user="scqspcfg-the-solid" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/scqspcfg-the-solid/pen/PovGOdR">
  Neon Sign</a> by hiroatsu (<a href="https://codepen.io/scqspcfg-the-solid">@scqspcfg-the-solid</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

なお、このアニメーションは、地図共有サイト [Toboggar](https://toboggar.com) のスプラッシュ画面で使用しているものである。

## どうやったらネオンサインの点灯のように見えるか
今回の方法がベストだとは思えないし、ベターですらないような気がするが、とりあえず私はこれで納得した。

- うっすら明るくなったあと短く3回点滅する
- その後、数度パチっと一瞬消える
- 点滅の間隔はランダム
- 時間は全体で2秒前後
- 影とぼかしで光ってる雰囲気を出す

## テクニック
### 影とぼかし
これは簡単、SVG全体にフィルターを掛ければよい。
[drop-shadow](https://developer.mozilla.org/ja/docs/Web/CSS/filter-function/drop-shadow) CSS関数は、Xのオフセット、Yのオフセット、ぼかし半径、色を指定して影を作る。
```css
svg {
    filter: drop-shadow(0px 3px 10px #71f7ed);
}
 ```
 ### アニメーション
点滅はCSSアニメーションで実現する。ロゴ自体はSVGで作成されているが、CSSで操作している。
SVG内でいくつかに分割されたpathに対して、点滅させるためのランダム値を用いて動的に生成される別々の kayframe を設定する。
```javascript
const title = document.querySelectorAll('svg path');
title.forEach((path) => {
    const keyframe = [];
    /* -----
    ランダムに点滅させるkeyframeを動的に生成する
    ----- */
    animate(path, keyframe);
});
```
### 点滅
点滅は単純に opacity の設定で実現できる。
0 から 1 にし、すぐに0に戻せば一瞬光る。アニメーションで0.05秒でoacityを増減させる。
```css
path {
    opacity: 0;
}
```
よって、アニメーションの keyframe は opacity を0か1に設定しているだけである。
```javascript
keyframe.push({ opacity: 1, offset: timming });
keyframe.push({ opacity: 0, offset: timming + 0.01 });
keyframe.push({ opacity: 1, offset: timming + 0.02 });
```
ただ、単純にアニメーションするだけでなく、それっぽくするためにいくつか処理を追加している
冒頭、点滅する前に opacity が 0.2 になるようにうっすら光らせている。
アニメーション全体は2500ミリ秒に設定しているので、平均125ミリ秒かけてうっすら光る。
```javascript
keyframe.push({ opacity: 0 });
let timming = Math.random() * 0.5;
keyframe.push({ opacity: 0.2, offset: timming });
```
最初に3回点滅するときには、0.8、0.9、1と段々明るくなるように設定している。
```javascript
keyframe.push({ opacity: 0.8, offset: timming});
keyframe.push({ opacity: 0, offset: timming + 0.01 });
keyframe.push({ opacity: 0.9, offset: timming + 0.02 });
keyframe.push({ opacity: 0, offset: timming + 0.03 });
keyframe.push({ opacity: 1, offset: timming + 0.04 });
```
3回点滅したあとランダムに、ときどき点滅するようにする。
offset が 1を超えない範囲でランダムに点滅させるのだが、適当な増分(`increase`)を設定しているので、段々点滅間隔が長くなるようになっている。
概ね1回か2回点滅するはずである。
```javascript
let increase = 0;
timming = timming + 0.05 + Math.random() * 0.8;
while (timming < 0.97) {
    keyframe.push({ opacity: 1, offset: timming });
    keyframe.push({ opacity: 0, offset: timming + 0.01 });
    keyframe.push({ opacity: 1, offset: timming + 0.02 });
    timming = timming + 0.05 + Math.random() * (1.1 + increase * 0.1);
    increase = increase + 3;
}
```
完成ソースはCodepenで確認してほしい。
機械学習のデータの集め方 2024年版

大規模言語モデル（LLM）が普及しているので、機械学習用のデータの集め方もだいぶ変わってきている。

そのため、2024年の時点での私の見解をもとに機械学習データの集め方について加筆する。

# そもそも、機械学習を自力でさせる必要があるのか
## 大規模言語モデル(LLM)、視覚言語モデル(VLM)の進展は、従来ではできなかった画像認識タスクをこなすようになってきている。
- かつては、特定の対象物もしくはCOCOデータセットの特定のカテゴリしか検出できなかった。
- 今は、特定の対象物にかぎらない（Open Vocabulary）物体検出ができている。
- 例：GroundingDINO　https://github.com/IDEA-Research/GroundingDINO
- かつては、検出物に対して検出枠しか返せなかった。
- 今は、検出対象物の画素ベースのセグメンテーションを返すのがある。
- しかも、特定の対象物に限らず、セグメンテーションを返す実装も存在している。
- 例： Grounded-SAM　https://github.com/IDEA-Research/Grounded-Segment-Anything

## 既存のVLMのモデルに利用可能なものがないか。
- もしかすると、既存の実装の既存の学習済みモデルで、そのタスクを実現できるかもしれない。
- HuggingFaceなどで、用意されている実装例を試してみよう。
- もし、それでうまくいくならば、成功の可能性は高まる。

## 従来型の学習・推論の枠組みはそのままでも恩恵を受けることはできるだろう。
### 人検出・人pose推定の学習データへの加工
- 従来の人検出に対して、セグメンテーションできるツールがあれば、それで人を前景として、人以外を背景としよう。
- それを使えば、背景差し替え画像を使って人検出の増強ができるだろう。

## 解決したい課題（タスク）に対しての論文を調査しよう。
- 論文を見つけたら、必ずと言っていいほど、その課題のために用いたデータベースが書かれている。
- 最近は、そのタスクに対するアルゴリズムの評価ができるためのデータセットの構築に対して学会が重要性を認識している。
- だからbest paper に選ばれることもある。
- その分野レビューの論文でも、データベースを記載していることが多い。
- だから、そのような調査が、あなたの仕事の役に立つことも多いだろう。

## 解決したいタスクの変化が、作られるデータセットの変化を生じている。
- 例：魚眼画像での画像データセット
- 例：一人称画像（自己視点画像）でのデータセット
- 例：工場や建築現場、農作業でのデータセット
- 例：極端な暗がりでのデータセット
- 解決したいタスクが変わってくるのに応じて新しいデータセットが作られてきます。
- こういったデータセットは、人検出という目的でも追加する価値があるかもしれません。

# 既存検出器を利用したポジティブサンプルの追加
- 物体検出の性能が上がっているので、既存の検出器での検出結果を拾い上げて、学習用の追加画像に加えても、誤検出は増加しないことが多い。
- ポジティブサンプルに対して、未アノテーションのまま学習データに追加しないかぎり、検出器の検出率を向上させる側に学習が改善する可能性が高い。

## 背景差し替え画像の作成が簡単になっている。
- 3Dカメラがとても安い。
- 3Dカメラで撮影した画像で、RGB画像で検出対象を検出したうえで、近距離の領域を前景画像として残す。それ以外の領域を背景として差し替えるのが簡単にできる。
- 他にも別な方法での背景除去はいろいろあるので、背景差し替え画像は簡単に作れる。

## 弱点になりがちな特定の状況での画像を集中的に集める。
- 例：アフリカ系の人での顔検出・顔ランドマーク検出
- そのようなデータがなければ、作るしかない。
- [CASIA-Face-Africa: A Large-scale African Face Image Database](https://paperswithcode.com/paper/casia-face-africa-a-large-scale-african-face)

- [Facial Recognition Software Struggles To Detect Dark Skin — Here’s Why & How](https://foundation.mozilla.org/en/blog/facial-recognition-bias/)
 この記事は、濃い顔色の顔検出・顔照合が記事の書かれた2023年時点においても十分なレベルに達していないことが述べられている。

- 車椅子での歩行者検出用の画像
[MobilityAids](https://paperswithcode.com/dataset/mobilityaids)
he annotated dataset contains five classes: pedestrian, person in wheelchair, pedestrian pushing a person in a wheelchair, person using crutches and person using a walking frame.

## そのVLM(視覚言語モデル)は、十分な検出能力を持っていますか？
- 大規模に学習されたVLM(視覚言語モデル)は高い汎用性のある認識性能を持つと期待されている。
- しかし、あなたの想定しているタスクに対して十分な性能を持つかどうかは、検証してみないとわからない。
- 例：ドアノブを見つけることができますか？
- それは、どれだけ多様なドアノブに対してもドアノブとして視認することができますか？
- 検証することの必要性はなくならないのです。

## それでも残る自作データセットの必要性
- あなたの想定する利用環境で性能がほんとうにでますか？
- カメラの視点が変われば、見え方は変わる。特に腕や手に対する見え方。手で扱っている対象物の見え方は、自己視点画像とそれ以外では大きく変わる。
- 人の見る視点と、掃除ロボットの視点では、部屋の中の見え方が異なる。
- カメラの画角、解像度が異なれば、見え方が異なる。
- 暗い環境では、カメラのノイズ特性によって、見え方が大きく異なる。
- あなたが解決しようとするタスクにそったデータを用意しないと、あなたの開発したアルゴリズム・機械学習結果が、十分に実用になるのかどうかを検証できない。
#### だから、自作のデータセットが不要になることはない。

---
## 関連記事
- [機械学習のデータの集め方](https://qiita.com/nonbiri15/items/f5c5c4357458bfeb03bb)
- [機械学習データの集め方２](https://qiita.com/nonbiri15/items/23448516c4e24e8ded8b)
- [機械学習のためのデータを収集する各段階の考え方](https://qiita.com/nonbiri15/items/df6b4df6a977636626c4)
Pythonでファイル(text,PDF,docx)の文字情報を取得してみる

## はじめに
今回は、Pythonでアプリにアップロードしたファイル(textやPDF、wordデータ)の文字情報を読み込むことに関するアウトプットをしていきます。


## 本編
#### 考え方
フロントでアップロードしてサーバーに送られるファイルデータは、バイナリーデータであることを頭に入れておくことが大事です。
バイナリーデータとは、コンピュータがわかる形式のデータのことです。
https://wa3.i-3-i.info/word1146.html

#### text形式データの文字情報を取得
text形式データは、decodeメソッドを使えば取れちゃいます。
ワー、チョー簡単ナンデスケド
```python
# binary_dataは、フロントからきたデータ
text = binary_data.decode('utf-8')
```

[公式](https://docs.python.org/ja/3/library/stdtypes.html#bytes.decode)も見てみると、デコードされた情報返してくれると書いておる。


#### PDF形式データの文字情報を取得
- ioを利用して、バイナリデータからBytesIOオブジェクトを作成
ioについては[こちら](https://docs.python.org/ja/3/library/io.html#binary-i-o)のように、バイナリーデータをファイルオブジェクトのように扱うことできます。
BytesIOオブジェクトを使う主な目的は、ディスク上のファイルを開かずにバイナリデータを処理することです。これにより、一時ファイルの作成や削除の手間が省け、メモリ上で完結できるので高速に処理できます

- PyPDF2というものを利用して、簡単にPDFの文字情報を取得できます
https://pypi.org/project/PyPDF2/
```python
import io
from PyPDF2 import PdfReader

file = io.BytesIO(binary_data)
reader = PdfReader(file)
# PDFのページ数が見れる
number_of_pages = len(reader.pages)

# 全ページのテキストを抽出
text_list = []
for page in reader.pages:
    text = page.extract_text()
    text_list.append(text)

# テキストを連結して1つの文字列に変換
text = "\n".join(text_list)
```

#### word形式(docx)データの文字情報を取得
- PDFの時と同様にioを利用して、バイナリデータからBytesIOオブジェクトを作成
- docxというものを利用して、簡単にPDFの文字情報を取得できます
https://python-docx.readthedocs.io/en/latest/
[チートシート](https://qiita.com/Maruchin/items/373274b7ba18670fae99)
```python
import io
from docx import Document

file = io.BytesIO(binary_data)
doc = Document(file)


# 全段落のテキストを取得
text_list = []
for para in doc.paragraphs:
    para_text = para.text
    text_list.append(para_text)

# テキストを連結して1つの文字列に変換
text = "\n".join(text_list)
```

## 雑感
Pythonも奥が深く、まだまだ知らなことだらけですが少しづつ知れると楽しい！
docxに関しては、段落、表、図も取得できたりと自分の引き出しを増やしていきたい、、！