OmniAICreator/Qiita-1.07M huggingface dataset:
Excelで使える回路図シンボルを簡単に Python + schemdraw で用意する

説明資料などをExcelやWordで作るときに、ちょっとした回路図を描く場合がありますが、回路図シンボルを図形で作るのは面倒です。
また、最近のExcelやWordではグループ化した図形でも構成要素を選択できてしまうので、移動するときにうっかり形を崩してしまうことがあって、余計な注意を払う必要があります。

schemdrawは回路図を描くためのPythonパッケージですが、多くの回路図シンボルが用意されているので、生成されたsvgファイルから各シンボルをExcelやWordにペーストすれば、ばらばらに分解されない回路図シンボルを簡単に用意することができます。

```python
import schemdraw
import schemdraw.elements as elm

with schemdraw.Drawing(unit=1.5, inches_per_unit=0.5, file='schem_sym.svg') as d:
    elm.Dot()
    elm.Resistor()
    elm.Ground()
    elm.Capacitor(unit=1.0)
    elm.GroundSignal()
    elm.Inductor2(loops=2)
    elm.Diode(unit=1.0)
    elm.LED(unit=1.0)
    elm.BjtNpn(unit=1.0)
    elm.BjtPnp(unit=1.0)
```

Inkscapeで開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/dcf77b82-2f4c-2e5c-bd98-891c1bee41f4.png)

シンボル一つずつ選択してコピー、ExcelやWordにペースト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/d0ef9de7-92c4-b414-2fd0-fdadc95457ee.png)

LEDやトランジスターは複数のパーツで構成されているので、必要なものを全て選択してExcelやWordにコピーすれば一つのグラフィックになります。

## matplotlibの有無による違い
matplotlibの有無によってschemdrawの生成するsvgファイルに違いがあるようです。
今回確認したバージョンは下記のとおりです。
> Python 3.11.5
> matplotlib      3.8.2
> schemdraw       0.18

### matplotlib無し
上記の例はmatplotlib無しの場合です。パーツはグループ化されていません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/6eb0a35f-7a99-1df7-f2c9-125d301d3759.png)

### matplotlib有り
matplotlibがインストールされている場合、figure->axes->パーツ群 のようにグループ化されています。上記例のようにパーツを取り出すには、Inkscapeでグループ解除を2回すればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1923193/ba1f89f0-f23d-265b-9831-dcda69cc5f5b.png)

# 参考文献
https://schemdraw.readthedocs.io/en/stable/index.html
【WordPress】プラグインなしでヘッダーメニューにサブタイトルをつける方法

## やりたいこと
今回やりたいことは、WordPressでプラグインなしで以下のようなヘッダーメニューの下にサブタイトルをつけることです。

![スクリーンショット 2024-01-28 23.54.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/7c9a67bf-e9a9-e57f-63d8-90b1dff2dfcb.png)


### 以下のコードをfunctions.phpに挿入
`add_filter('walker_nav_menu_start_el', 'description_in_nav_menu', 10, 4);
function description_in_nav_menu($item_output, $item){
  return preg_replace('/(<a.*?>[^<]*?)</', '$1' . "<br /><span>{$item->attr_title}</span><", $item_output);
}`

## ワードプレスの管理画面

### WordPressの管理画面から「外観」→「メニュー」を選択

<img width="400px" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/3d506b77-39bb-d5db-8d37-7bced79d4b6c.png">

### 各メニューの「タイトル属性」にサブタイトル文字を入すると完了です！！
<img width="400px" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524155/6eafa02d-1400-55c5-ddf2-99599b84f073.png">


## 最後に
以上となります！
意外と簡単にできますね。。
ワードプレスのヘッダーのデザイン修正は結構難しくてつまずきやすいので自由に色々デザインできるようになりたいです、、！
Sassのミックスイン

1.ミックスインの基本
ミックスインの機能はスタイルの集まりを定義しておき、それを他の場所で呼び出して使うことができるというものです。

Sass
//ミックスインを定義
@mixin main{
  padding:15px;
  background:#999;
  color:white;
}
ミックスインは、@mixinの後に半角スペースを空けて任意のミックスイン名を定義します。そして、｛～｝（波括弧）内にスタイルを書いていきます。

定義したミックスインを呼び出す際は、「@include ミックスイン名；」と書きます。
Sass
//定義したミックスインを呼び出し
.button{
  @include main;
}

CSS(コンパイル後)
.button{
  padding:15px;
  background:#999;
  color:white;
}
ミックスインはこのように「@mixin」と「@include」をセットで使います。

2．引数を使ったミックスイン
Sass
//引数を使ったミックスインを定義
@mixin card($value){
 border-radius: $value;
}

引数を使う場合、ミックスイン名の直後に（）（丸括弧）を書き、括弧内に引数（変数）を書きます。

Sass
.item{
 @include card(2px);
 background:#eee;
}

CSS(コンパイル後)
.item{
  border-radius:2px;
  background:#eee;
}

ミックスインはこのように、値の一部を変えて使い回すことができます。
macOSでChromeがアップデートできなくなったので対処した

macOS（Sonoma）の Chrome にて、下記のエラーが突然発生するようになりアップデートができなくなりました。
アップデートに失敗するだけで Chrome 本体に問題は見られません。

```sh:アップデート失敗時のエラー内容
An error occurred. (Error code: 9) (Extra code: 0)
```

Chrome を再インストールしても効果は無く、アップデート処理の初期段階ですぐにエラーが発生しているように見える事から下記を疑いました。

* アップデートで使用するファイルが壊れたか、ロックファイルのような一時ファイル等が消えていない

そこで、アップデート処理のログを探したところ、```~/Library/Application Support/Google/GoogleUpdater/updater.log``` ファイルを見つけました。

このファイルには次のような ERROR ログが出力されており、明らかにおかしな事になっているように見えます。

|source|エラーメッセージ|
|---|---|
|prefs.cc|Failed to acquire GlobalPrefs|
|update_service_internal_proxy_posix.cc|Failed to connect to UpdateServiceInternal remote. Connection timed out.|
|app_install.cc|FetchPolicies failed: 35|

更に、この GoogleUpdater ディレクトリには ```crx_cache``` という怪しげなディレクトリもあり、ここに原因があるような気がしました。

そこで、試しに以下を実施したところ、GoogleUpdater ディレクトリが新規に作られ、問題はあっさり解決しました。

* ```~/Library/Application Support/Google/GoogleUpdater``` を名前変更（mvコマンド）して Chrome を起動

その後、アップデートが何度か適用されましたが、特に問題は起きていません。

また、問題の起きていない GoogleUpdater ディレクトリに crx_cache は残っていない事を確認しています。
開発言語未経験のインフラ技術者が Azure AI で PDF 翻訳 にチャレンジしてみた話

# はじめに
初めて Azure の AI サービスに触れてみました。
私は、インフラ系の技術者なので 開発系の知見は疎いので AI サービスは縁遠いと思っていましたが、たまたま 以下の記事で紹介したように Google 翻訳を使って、PDF 翻訳 を経験したことから、これを Microsoft のサービスで実施できないだろうか？・・・と考えたことから、興味が湧きました。

https://qiita.com/carol0226/items/cac35e570f79b51082eb

:::note info
（筆者のレベル）
Azure のインフラ系技術は、5年前から触っていて、各種資格も取得しているレベル。
IaaS , Network , Windows Server , 仮想化技術 , VDI などには強みがある方。
しかし、開発言語は 何もできません。20年以上前に COBOL と BASIC をやった程度。
Python とか C#、Java などは全く手も足も出ません。
最近、会社の資格取得イベントがあったので、AI-900 の資格を丸暗記レベルで取得したばかり。
Azure Open AI も そんな関係からか 距離を置いていた感じです。
:::

# 実現方法の検討
Microsoft が Google の PDF 翻訳 と同じようなサービスを提供しているのかを探してみたのですが、無料で一般利用者が使えるものとしては、提供されていないことが分かりました。

https://www.microsoft.com/ja-jp/translator/business/

どうやら、Azure AI サービス を使って、自分で手配するしか無い様子であることが分かりました。

https://www.microsoft.com/ja-jp/translator/business/document-translation/

ちょっと大変そうですが、これを扱えるようになると、クローズドなので、社内の 機密文書 であっても、機械翻訳 を実施できる可能性が出てくるんじゃないかと期待が膨らみます（それでも、会社に許可を取る必要ありそうですが・・・）
※さすがに、ネット上の Google 翻訳 に、ビジネス文書 を放り込むのは ナンセンスです。

## ① REST API を使う（見送り）
まず、Azure AI サービスの中から 見つかったのは 以下の記事です。
これを使うと、翻訳できそうなのですが、API の提供のみで フロントエンド の UI は自分で開発しなければなりません。これは、ハードルが高い！
ストレージアカウント に 英文 PDF を保存しておき、REST API を叩けば 翻訳してくれそうなことは理解できました。もっと良い方法がないかを探してみることに。

https://learn.microsoft.com/ja-jp/azure/ai-services/translator/document-translation/overview?wt.mc_id=mvp_407731

https://learn.microsoft.com/ja-jp/azure/ai-services/translator/document-translation/quickstarts/document-translation-rest-api?wt.mc_id=mvp_407731&pivots=programming-language-rest-api

## ② Power Automate Translator V3 コネクタ を使う（見送り）
次に 以下の記事を見つけました。
これだと、Power Automate を使って、トリガーさせられそうです。
私でも、ギリギリ それっぽいものを作れそうな気もしますが、ドキュメントを BLOB ストレージにアップロードしなければならないのが、操作性に欠ける気がして、もっと良い方法が無いか、探してみることにしました。見つからなかったら これでやるしかないか・・・という感覚です。

https://learn.microsoft.com/ja-jp/azure/ai-services/translator/connector/document-translation-flow?wt.mc_id=mvp_407731

## ③ Language Studio でのドキュメント翻訳 を使う（採用！）
最後に、これを見つけました。
良く読むと、Language Studio が ドキュメントの受け渡しのフロントエンドの役目を担ってくれそうであり、これなら 何も開発しなくても UI を使って、翻訳まで実施ができそうです。
これでやってみよう！ と決断しました。

https://learn.microsoft.com/ja-jp/azure/ai-services/translator/document-translation/language-studio?wt.mc_id=mvp_407731&tabs=local-env

:::note warn
上記の記事を 頭から順に進めてみたら 結構ハマりました。
公開情報の記事には、必要な情報は 盛り込まれているのですが、内容を理解して、各コンポーネントの関係性を整理した結果、効率的な手順にまとめることができました。手順は、以下の記事を参照ください。
:::

https://qiita.com/carol0226/items/e10fe04cf59c3dbd734e

# Language Studio を使った ドキュメント翻訳 の利用シーン
実際に Language Studio を使って、ドキュメント翻訳を実施した際の 操作の流れは、以下の記事にまとめてあります。

https://qiita.com/carol0226/items/09821bca4f0946ec8c51
EVPN/VXLAN Anycast Gateway、からのVRF-Liteで外部L3接続する

## 概要
GNS3でNexus9000vを使ってEVPN/VXLAN Anycast Gatewayを設定した上で、Border Leafとファブリック外ルータをVRF-Liteで接続する。
> VXLAN BGP EVPN ファブリックは、外部接続を実現するために VRF 単位の IP ルーティングを使用して拡張できます。レイヤ 3 拡張に使用されるアプローチは一般に VRF Lite と呼ばれ、機能自体はより正確に Inter-AS オプション A またはバックツーバック VRF 接続として定義されます。
[Cisco Nexus 9000 シリーズ NX-OS VXLAN 構成ガイド、リリース 9.3(x)](https://www.cisco.com/c/ja_jp/td/docs/switches/datacenter/nexus9000/sw/93x/vxlan/configuration/guide/b-cisco-nexus-9000-series-nx-os-vxlan-configuration-guide-93x/b-cisco-nexus-9000-series-nx-os-vxlan-configuration-guide-93x_chapter_011111.html)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359374/7dbfa717-7772-73fb-f759-375c7873740a.png)

物理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359374/bb75a2ab-f01c-c0cd-e71f-f5eafdfc8857.png)

論理
- Border LeafにVRF-Red、VRF-Blueを作成
- VRF-Redは外部ルータとOSPFで、VRF-Blueは外部ルータとeBGPで接続するように構成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359374/7d1ea2ac-24ce-a143-1ff9-a78ef18c4ded.png)

## 設定
VRF-Red @BorderLeaf
- IGPを使用した場合には再配布が必要となる

https://gist.github.com/tadashota212/fabb606344eda62248a445f6f85cde88

VRF-Blue @BorderLeaf
- BGPを使用したほうがconfigはシンプル

https://gist.github.com/tadashota212/8912176bb99eda8f1ec198e1c560bc42

## 動作確認
BorderLeaf

https://gist.github.com/tadashota212/bf9b831a6f749f2c2208404dcbac2538

Leaf01

https://gist.github.com/tadashota212/4831d99409a7e1ca71f782389a33d270

vm01→BorderRouterへエンドtoエンドのping疎通もOK
```
[root@vm01 ~]# ping 10.110.0.1 -c 5
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
64 bytes from 10.110.0.1: icmp_seq=1 ttl=62 time=7.91 ms
64 bytes from 10.110.0.1: icmp_seq=2 ttl=62 time=6.72 ms
64 bytes from 10.110.0.1: icmp_seq=3 ttl=62 time=16.6 ms
64 bytes from 10.110.0.1: icmp_seq=4 ttl=62 time=60.4 ms
64 bytes from 10.110.0.1: icmp_seq=5 ttl=62 time=88.4 ms

--- 10.110.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 6.725/36.043/88.442/32.764 ms
```

参考：外部ルータconfig（vJunos-switch 23.2R1）

https://gist.github.com/tadashota212/5936f3fb6a549073f13fd08ac6684811
PHPStan どうしても解消できない静的解析エラーをPHPStanの設定で見なかったことにする

## 概要

PHPStanを使っていてどうやっても解消できない静的解析エラーをエラーとして扱わないようにする方法を簡単にまとめる。

## 注意

本方法は多用してはいけません。多用するとPHPStanを入れている意味がなくなります。
コード上でどうにかして対処し、それでもどうしても解消できない場合に使いましょう。

## お願い

なるべくエラーを許容する方法で解決したくありません。もっと良い解決方法がございましたらぜひ教えてほしいです。。！

## 方法

phpstan.neonというファイルに見過ごしたいエラーを指定することでそのエラーをなかったことにする事ができる。
具体的には`ignoreErrors:`のあとに正規表現を使って見過ごしたいエラーを指定する。

エラーとは下記のように行数とエラー内容で表示されるヤツのことである。

```
------ ------------------------------------------------------------------------------------- 
 Line   ファイルパス  
------ ------------------------------------------------------------------------------------- 
 107    Access to an undefined property Stripe\StripeObject::$last4.                         
------ -------------------------------------------------------------------------------------
```

今回のエラー内容は`$last4 = $this->paymentMethod?->card?->last4;`という処理でlast4のプロパティが見つからないというエラーである。
それもそのはず、対象のプロパティが含まれるクラスは動的クラスだ。last4プロパティは動的クラスに存在しない。

phpstan.neonを探して下記のように書いてみよう。（おそらくこのファイルはPHPStanのチェックレベルを指定するものなのでPHPStanを運用しているならほぼ確実に存在しているはずである。）

```phpstan.neon
includes:
    - ./vendor/nunomaduro/larastan/extension.neon

parameters:

    paths:
        - app/
        - config/
        - database/
        - tests/
        - lang/

    # Level 9 is the highest level
    level: 6

    ignoreErrors:
        # Stripe PHP SDK系の動的メソッド回避
        - '#^Access to an undefined property Stripe\\StripeObject::\$last4\.#'
```

上記で注目すべきは`parameters:`のなかの`ignoreErrors:`の中の`- '#^Access to an undefined property Stripe\\StripeObject::\$last4\.#'`の部分である。
配列的に`ignoreErrors:`の中に許容したいエラーを正規表現で記載するらしい。

上記の設定を行い、再びPHPStanを実行したところ当該のエラーは出なかった。
【メモ】Vim

## はじめに
最近、Vimについて学習したのですが初学者の私にとっては理解に時間がかかったため、整理していきます。

:::note warn
私の環境と同じMac OSを前提に話を進めていきます。
:::

## 前提として必要な知識

早速Vimについて整理していきたいところですが、Vimを理解するにはCUI操作について知っておく必要があります。

コンピュータの操作画面はCUI、GUIの2種類があります。それぞれの頭字語は以下の通りです。

- GUI：Graphical User Interface
- CUI：Character User Interface

多くの人に馴染みがあるGUIは、以下のように画像やアイコンのインタフェースでユーザーはマウスのみで操作します。

![gui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/e001bd16-b462-41e1-c37c-31fb60b89ec5.png)

対して、CUIは文字のみのインタフェースです。ユーザーはキーボードで、コマンド入力し操作します。

![cui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/579a8f86-9ca2-29b6-fcb9-4970c95b77e5.png)

MacでCUI操作するには、標準でインストールされているターミナルというアプリを使用します。

ターミナルは上記画像のような黒い画面のCLI（Command Line Interface）を提供しており、CLI上でコマンドを入力しコンピュータを操作していきます。

補足となりますが、コマンド実行後から実行結果が表示されるまでの基本的な流れは以下のようです。

1. ユーザーがコマンドを入力する
2. シェルがコマンドを解釈し、カーネルに実行要求を出す
3. コマンドはカーネルから出力された実行結果を受け取り、画面に表示する

参考 - [初学者のための正しいシェルとカーネルの概念 ～ 大学も技術者認定機関も間違いだらけ](https://qiita.com/ko1nksm/items/935be63e940f00e4c228#%E3%82%B7%E3%82%A7%E3%83%AB%E3%81%AF%E4%BE%9D%E9%A0%BC%E3%81%97%E3%81%9F%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E5%AE%9F%E8%A1%8C%E3%81%8C%E7%B5%82%E3%82%8F%E3%82%8B%E3%81%BE%E3%81%A7%E5%81%9C%E6%AD%A2%E3%81%99%E3%82%8B)

## CUI操作してみる

では実際に、ディレクトリの内容をターミナルに表示する```ls```コマンドを実行してみます。

![ls.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/2e31fb31-e96c-79bd-cb50-9c5880c2304d.png)

実行が完了すると、添付画像の赤枠箇所にルートディレクトリのフォルダの一覧が表示されているのが分かります。

また、```ls```コマンド以外に```cd```、```mkdir```など様々なコマンドがあるので使用頻度が高いものから覚えていくと良いかもしれません。

CUI操作について整理できたので、次はいよいよVimについてです。

## Vimとは

Vimとは、簡潔に言うと「前述のCLI上で動作する高機能なテキストエディタ」です。高機能な理由は、エディタ上でのコマンドの数の多さにあると思われます。

エディタ上のかなり線密な操作に対してでも、コマンドが割り当てられており、コマンドを覚えて適切に入力できればファイルの編集スピードは格段に上がると思います。（ただ、コマンドの数はかなり多いです。）

## Vimでテキストファイルを編集する

では、```vim```コマンドでテキストファイルを指定してVimを起動します。

``` plaintext:
    $ vim hoge.txt;
```

ただ、以下の状態ではテキストファイルの中身は編集できず、キーボードの```i```キーでINSERTモードに切り替える必要があります。現在は、NORMALモードです。

![vim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/7e757856-a1e2-61e1-e3c4-69db0172bcf2.png)

INSERTモードで、```hoge```と入力できました。

![insert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/d7af2f6c-9bb0-cc97-a657-ad0c05ee4f03.png)

```esc```キーでNORMALモードに戻り、保存してVimを閉じるために```:wq```と入力します。（```:```を入力するとCOMMANDモードに切り替わります。）

Vimの簡単な操作は以上となります。

## おわりに

Vimのコマンドは、インターネット上にまとまっているものが沢山あるので見てみるとよいかもしれません。コマンドの多さに驚かされます。

ただ、全て覚えるのは効率が悪いので基本が学べるVimtutorをおすすめします。

CLI上で```vimtutor```コマンドを実行すると、Vimが起動しテキストに沿って実際に手を動かしながらVimが学べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2290540/9e5f42f7-f086-9ea6-a0e8-ce65bbf35fa2.png)

どなたかの参考になれば、幸いです。
jupyter notebook　チートシート

# jupyter notebook　チートシート

メモです。これから追記していきます。


## jsonデータを開く

```py
with open("predict.json") as f:
    data = json.load(fp=f)
```

## jsonデータを整形して表示する

```py
sample=data[0]
print(json.dumps(sample, indent=2))
```

pprint使えばdumpしなくても整形してくれるよー、って教えてもらった。

## 時間はdatetime.fromisoformatで変換する

```py
timestamp = datetime.fromisoformat(d["jsonPayload"]["time"])
```
CSP設定下でGoogle広告を入れる時の注意

コンテンツ セキュリティ ポリシー（CSP）を設定しているサイトでGTMを使ってGoogle広告のコンバージョンタグを導入した際に困った事があったのでその対処をした時のメモ。

[GTMの公式ドキュメント](https://developers.google.com/tag-manager/web/csp?hl=ja)にCSPを設定しているサイトについての案内があり、
ここを読む限りGoogle広告のタグを利用する際は以下のディレクティブの追記が必要となっています。

```
script-src: https://www.googleadservices.com https://www.google.com
img-src: https://googleads.g.doubleclick.net https://www.google.com
```

しかしこの２つをディレクティブに含めて動作確認をすると以下のCSP違反のエラーが出ます。

- `https://td.doubleclick.net/` が `frame-src` で違反
- `https://www.google.co.jp/pagead/1p-co…` が `img-src` で違反

上記の状態だとコンバージョンが計上されずGoogle広告の管理画面でもコンバージョンのステータスが無効のままです。
GTMのプレビューだけで確認するとタグの発火自体は問題ないためコンソールログを見ていないとなかなか気づかきにくいです。

試しにエラーを解消するように`frame-src`と`img-src`にそれぞれ上のドメインを追記すると今度は新たに

- `https://www.google.co.jp/pagead/1p-co…` が `script-src` で違反

`script-src` にも `www.google.co.jp` を追記するとエラーが解消、広告のコンバージョンも有効になりました。

上記の外部ドメインが具体的にどういう用途で読み込まれているのかを公式に説明したものが今のところ見つからないのですが、Google広告に関連しているものであることは察せるのでとりあえずこの設定で運用しています。
【実務に役立つPower BI】 整形の必要な複数のExcelファイルのシートをフォルダ指定で読み込む

## はじめに
　Excelシートの1行目に「シートに関する情報」や「データベースから指定データをダウンロードする際の条件」などが入力されていて、ビジュアル化に使いたい「列のタイトル」や「数値データ」が2行目以降に入力されていることは良くある。（下図の場合は、5行目に「列のタイトル」、6行目以降に「数値データ」が入力されている。）

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676786/e70ed6ec-e757-d969-84e2-4bdcab47ee5c.png)

　「ファイル指定」で1つのExcelファイル中の1つのシートを指定して読み込む場合は、Power Queryエディターでデータテーブル中の不要な列や行を削除すれば良いのだが、「フォルダ指定」でも同じ操作が行えるという事実を知らない人が多く、「フォルダ指定」で複数のExcelファイルを一括で読み込むための準備作業として、わざわざフォルダ内の複数のExcelファイルのシートを手作業で一つ一つ編集している事例にたびたび遭遇している。

　「2024年！初アウトプットをしよう」の企画に背中を押されて、人生初のQiita投稿をしてみようと思う。

 ## 使用データ
 　PCのデスクトップに作成した「実務DATA」フォルダ内に、以下の3つのExcelファイルを保存している。
* 2024年01月_実務DATA.xlsx
* 2024年02月_実務DATA.xlsx
* 2024年03月_実務DATA.xlsx

　上記の各Excelファイルには、シート名が「DATA」のシート（以下「DATA」シートと記載する）のみがある。（冒頭で示した画像は、「2024年01月_実務DATA.xlsx」ファイルの「DATA」シート。）
## 「ファイル指定」でのPower Queryエディター画面
　Power BI Desktopで以下の操作を行い、Power Queryエディターを起動すると、下図の画面が表示される。
 1. 「ホーム」タブの「データを取得」をクリックする。
 2. 「データを取得」ウインドウで「Excelブック」を選択し、「接続」ボタンをクリックする。
 3. 「開く」ウインドウでデスクトップ上の「実務DATA」フォルダを開き、「2024年01月_実務DATA.xlsx」ファイルを選択し、「開く」ボタンをクリックする。
 4. 「ナビゲーター」ウインドウで「DATA」シートにチェックマークを入れた後、「データの変換」ボタンをクリックすると、下図のようにPower Queryエディターが起動する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676786/6487abb3-9b48-c8f6-25c9-efd518d20ccc.png)

## 「フォルダ指定」でのPower Queryエディター画面
　「フォルダ指定」のデータ読み込み手順と、Power Queryエディターの画面を以下に示す。
 1. 「ホーム」タブの「データを取得」をクリックする。
 2. 「データを取得」ウインドウで「フォルダ」を選択し、「接続」ボタンをクリックする。
 3. 「フォルダー」ウインドウで「参照…」ボタンをクリックしてデスクトップ上の「実務DATA」フォルダを選択し、「OK」ボタンをクリックすると「フォルダー」ウインドウに戻る。
 4. 「フォルダー」ウインドウで「OK」ボタンをクリックすると、選択したフォルダ名とパス名のウインドウ（c:\User\…\デスクトップ\実務DATA）が表示される。
 5. 「c:\User\…\デスクトップ\実務DATA」ウインドウの右下に表示される「結合」ボタンのプルダウンメニューから「データの結合と変換」を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676786/10a7251a-8f41-e70c-c9a9-41cb0ac941b0.png)

 6. 「ファイルの結合」ウインドウで「DATA」シートを選択し、「OK」ボタンをクリックすると、下図のようにPower Queryエディターが起動する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676786/e2e1ec8c-3552-d302-1ef0-3db8b66f2ecc.png)

## 「ファイル指定」と「フォルダ指定」との「クエリ」ペインの比較
　下図は、左が「ファイル指定」、右が「フォルダ指定」のPower Queryエディター画面である。
　データファイルの読み込みを行うと、読み込んだデータファイルのクエリが各々の画面左端の「クエリ」ペインに表示される。
 
　下図左の「ファイル指定」の場合は、読み込んだデータシート名と同じ名前のクエリ（下図の場合は「DATA」クエリ）が表示されている。

　一方、下図右の「フォルダ指定」の場合は、アイコンがフォルダの形をした「その他のクエリ［1］」の下に、読み込んだフォルダ名と同じクエリ（下図の場合は「実務DATA」クエリ）が表示されていると共に、上部に「サンプルファイルの変換」クエリとその関係クエリが表示されているのが分かる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3676786/6efa8fc1-3c65-d401-39da-7bf1457dde63.png)

　実は、「フォルダ指定」で複数のデータファイルを一括で読み込むための準備としてExcel上で行っていた手作業の整形を、この「サンプルファイルの変換」クエリで行うことができる。
　操作方法は「ファイル指定」で作成されたクエリの場合と同様で、画面上部のメニューも共通である。

## 終わりに
　これまで、社内向けにPower BIの教育を行ってきているが、実務で使用されているExcelシートは、全て「フォルダ指定」かつ「更新」ボタンのワンクリックでデータ更新できるようにクエリを作成できている。

　「ファイル指定」で複雑なデータテーブルの編集を行った経験のある方であれば、この投稿に記載した情報を基に、どんなExcelシートでも「事前準備不要の一括更新のクエリ」を作成できると思っているが、「こんな複雑なExcelシートでは、さすがにフォルダ指定での一括読み込みはできないだろう」という事例があれば、私自身のスキルアップのためにご紹介いただければ幸いである。

　引き続き、Qiitaへの投稿で、具体的な操作方法についても詳細に紹介していきたい。
30代片隅エンジニアのTips

# はじめに
こんにちは。投稿方法の練習がてら、Tipsではなく軽い記事を投稿します。
特に役に立つことは書いていないですが、暇つぶし程度の記事でよければ、読んでやってください。

## 自己紹介
ざっくり自己紹介です。
shenといいます。
略歴
- 専門学校に入学
- 四年生大学に入学
- 大学卒業と同時に独立系SIerに入社

社会人としてのスタートはだいぶ遅いほうです。

そのため、他業界からの転職、未経験転職の方と年齢層的には変わらない世代だったりします。

## エンジニアとしての自己紹介
* 組み込みエンジニア
* 請負組込案件のテスター、テスターチームのチームリーダー(TL)
* アプリ開発チームのテスターチームのチーム（プロジェクト）リーダー(TL)
* 組込案件の開発

具体的な業務内容
`テスター` テストの実施
`テスターチームのTL` 進捗管理、進捗レポート作成・報告、お客様との折衝、テストケースの作成、スケジュール作成など
`開発` 実装、Bug調査、改修

エンジニアとしては、組み込みエンジニアに分類される…と思います。
なぜこんなに自信なさげかといえば、SIerに入社して数年経つものの、本格的に技術や開発に携わり始めたのがこの1年ほどのため、エンジニアといえるかどうか怪しいよわよわエンジニア。そんな人間です。

そんな人間でもIT業界の端っこに引っかかって、数年生き延びられてはいます。
幸か不幸かどっちかはわかりません。

## Qiitaに投稿を始める目的
ざっくり２つです。
1. 自分のアウトプットのため
2. 少しでも他人のインプットに役立ててほしいため

前述しましたが、私はよわよわエンジニアです
SIerとして、私個人は技術力を求められてこない立ち位置にいました。
（１つ注釈するのは、SIerといっても参画する案件にだいぶ左右されるため、私のように技術力のない人ばかりではありません。
現に、私の上司や別部署の人でバリバリ実装をしている人も多いです）

ただ、最近技術に触れるようになり、個人的にアウトプットする場が欲しくなりました。
そこで、業務で参考にしているQiitaに投稿すれば、自分のアウトプットがてら、業務でも参考になるし一石二鳥ではないか！という思い付きから始めました。

加えて、自分のような技術よわよわエンジニアでも生きてるよ～という誰かの自信になればいいかな、、、とかも少し思っています。

基本は自分が業務で役に立ったことの、いわゆるTipsをまとめることになりますが、誰かの参考になれば幸いです。
Faradayで302でログイン画面に飛ばされたときに再ログイン

RubyでHTTP通信して何か情報を取ってくるときには、だいたい [Faraday](https://github.com/lostisland/faraday)を使うと楽だ。

* 公式ドキュメント https://lostisland.github.io/faraday/#/
* Faradayの使い方 59のレシピhttps://nekorails.hatenablog.com/entry/2018/09/28/152745

あたりを見れば大体の使い方はわかる。かりに、使い方が分からなくてもソースコードを読めばなんとなく動きはわかる。

## まえおき

CakePHPで作られた古い社内サービスをスクレイピングするスクリプトを組んでたんだけど、何か知らないけど、ときどきUser/Passを入れ直せと、ログイン画面に飛ばされてしまってた。

* Authorizationヘッダーじゃなくて、クッキーで認証管理されてる
* 再認証のときはHTTP 401ではなく302でログイン画面に飛ばされるだけ

ということで、Faradayの標準の仕組みではどうにもならなさそう、ということで調べた。

## faraday-retryは？？

https://github.com/lostisland/faraday-retry

Faradayには、通信エラーのときにリトライするのをうまくやる仕組みがある。

ただ、このfaraday-retryは、基本的にはFaradayの**例外**をキャッチして、それに対してリトライをかける仕組みだ。302はFaradayの例外ではないので、標準ではリトライがかからない。
無理やり以下のようにすればリトライすることはできるが、ユーザID/パスワードで認証するロジックがあっちにもこっちも状態（初回の認証時と、再認証時で異なる場所に実装される状態）になるので、なんだか微妙だ。もっといい方法はないだろうか？

```ruby
retry_options = {
  retry_statuses: [302],
  methods: [],
  retry_if: ->(env, _exception) {
    # 302でログインページに飛ばされていたらtrueを返す
  },
  retry_block: ->(env) {
    # 再ログイン処理
  },
}
```

## 自前でFaradayミドルウェアを書いてみる

Faradayは、Rackを真似たようなミドルウェアの仕組みがある。
https://lostisland.github.io/faraday/#/middleware/index?id=how-it-works

```ruby
class HogeMiddleware < Faraday::Middleware
  def initialize(app, options={})
    @app = app
  end

  def call(env)
    # 通信前に何かする
    response = @app.call(env)
    # 通信後に何かする

    response
  end
end
```

今回のようなケースでは、認証機能、再認証機能を全部おまかせするようなミドルウェアを書いてしまえばいい。


```ruby
class MyAuthMiddleware < Faraday::Middleware
  def initialize(app, options={})
    @app = app
    @options = options
    fetch_token!
  end

  LOGIN_URL = 'https://xxx.example.com/login'.freeze

  def call(env)
    env.request_headers['cookies'] = "hogehoge_session=#{@token}"
    response = @app.call(env)
    if response.status == 302 && response.headers['location'] == LOGIN_URL
    　　　　# 認証トークンを取得し直して1回だけリトライ
      fetch_token!
      env.request_headers['cookies'] = "hogehoge_session=#{@token}"
      response = @app.call(env)
    end
    response
  end

  private

  def fetch_token!
    @token = LoginProcess.new(@options[:user], @options[:password]).call
  end
end

Faraday::Request.register_middleware(myauth: MyAuthMiddleware)
```

そうすれば、スクレイピング部分からは認証ロジックをほぼ追い出すことができて、

```ruby
@connection = Faraday.new do |faraday|
  faraday.adapter Faraday.default_adapter
  faraday.request :myauth, user: ENV['MY_USERNAME'], password: ENV['MY_PASSWORD']
  ...
end
```

こんなかんじでメインの通信をおこなうFaradayコネクションを定義すればおｋ。

## まとめ

通信エラーのリトライくらいならfaraday-retryで十分だが、認証・再認証あたりをする場合には、むりにfaraday-retryで頑張らず、本記事で紹介したように自前でミドルウェアを書いてみるとよい。
そんなに大変ではないし、それなりに役立つと思う。
[postgresql]エラー：FATAL: could not open directory "pg_notify": No such file or directory

備忘録です。

### 経緯
```
$ psql postgres
```
を実行したところ、下記のエラーが発生。

```
FATAL: could not open directory "pg_notify": No such file or directory
```

データベースを削除して再初期化すると解決しました

```
brew services stop postgresql
rm -rf "$(brew --prefix)/var/postgresql@14"
initdb --locale=C -E UTF-8 "$(brew --prefix)/var/postgresql@14"
brew services start postgresql
```

データベース削除を伴うのであまりおすすめできないかもしれませんが、解決策の一つとして誰かの参考になればと思います。

### 参考文献
- https://stackoverflow.com/questions/66620279/how-to-get-postgres-to-start-on-big-sur
- https://stackoverflow.com/questions/72516726/fatal-could-not-open-directory-pg-notify-no-such-file-or-directory
Logrotateによるログ管理の改善（Rocky Linux8）

# はじめに
業務にて、rsyslogの一部ログが欠落していることで全て表示するよう設定変更した。
その結果、対象のサーバでログが肥大化しストレージの容量を圧迫していた。
ログローテーションの設定の見直しを行ったので対応内容を記載します。

ログファイルの中で⚪️G以上の容量のファイルを確認
```terminal
ls -lh /var/log | grep G
```
容量が大きいファイルのtop5を表示
```terminal
du -shc -BG /var/log/* | sort -rn | head -n 5
```

# Logrotateの設定
Logrotateは、Linuxシステムで広く使用されているログファイルの管理ツールです。
サーバーやシステムが生成するログファイルは、時間の経過とともに大きくなり、ディスクスペースを圧迫することがあります。
Logrotateは、これらのログファイルを自動的にローテート、圧縮、削除、またはアーカイブすることで、ディスクスペースを効率的に使用できます。
OSやサービスの再起動は不要。

Logrotateの設定は、主に/etc/logrotate.confと/etc/logrotate.d/ディレクトリ内のファイルで行われます。

##### 現在の設定内容(***はログファイル名)
```terminal
cat /etc/logrotate.d/***
/var/log/***.log
{
    missingok    ログが存在していなくても処理続行（エラーにしない）
    daily　　    1日周期で実行
    copytruncate 既存ログファイル名変更、複製して中身削除
    rotate 7　   ローテーションする回数
    notifempty　 ファイルが空の場合はスキップする
}
```

# 設定変更

現在7世代管理されているため、この部分は変更せずに維持します。
7世代前のまま現行と1世代前以外は圧縮処理させるようにする。
下記の設定を追加。

##### compress
compressは単に古いログファイルを圧縮するためのオプションです。

```terminal
compress
```
##### delaycompress
delaycompressはローテートの１世代目は圧縮されずにそのままにします。
※設定した環境ではCloudwatchAgentが***.logと***.log.1を参照しており、圧縮処理すると影響が出るので設定しています。

```terminal
delaycompress
```

下記でCloudwatchAgentが参照しているか確認することができる。
```terminal
/sbin/fuser -v /var/log/***.log
```

##### 変更後の設定内容(***はログファイル名)
```terminal
cat /etc/logrotate.d/***
/var/log/***.log
{
    missingok
    daily
    copytruncate
    rotate 7
    compress　※追加
    delaycompress ※追加
    notifempty
}
```
※もし既存のファイルが圧縮されていない場合は事前に圧縮処理のみ必要になります。
今回の場合は、***.log.2以降を圧縮しておく。
※***.log.2以降を圧縮していなかったので、処理が実行されていませんでした。

##### ローテート結果のイメージ
| 変更前      | 変更後      |
| ------------ | ------------ |
| ***.log      | ***.log.1    |
| ***.log.1    | ***.log.2.gz |
| ***.log.2.gz | ***.log.3.gz |
| ***.log.3.gz | ***.log.4.gz |
| ***.log.4.gz | ***.log.5.gz |
| ***.log.5.gz | ***.log.6.gz |
| ***.log.6.gz | ***.log.7.gz |



# 動作確認

設定変更を行ったので、事前に動作を確認します。
/var/lib/logrotate.statusを編集する必要があれば修正します。
Logrotateツールによって使用される状態ファイルで、
このファイルには、logrotateが各ログファイルを最後にローテートした日時が記録されています。この情報を使用して、logrotateはログファイルをどのように処理するかを判断します。
特定のログファイルのパスと、そのファイルが最後にローテートされた日時（年-月-日-時:分:秒）が記載されています。
※テストする際の日付と同様だとローテートされないので検証できない。

##### ファイルの中身確認
```terminal
cat /var/lib/logrotate.status | grep ***
"/var/log/***" 2024-1-28-6:25:1
```

##### 動作検証

設定ファイルの内容に基づいて、logrotateの動作をシミュレートします。
このコマンドは実際にはログファイルをローテートしませんが、logrotateがどのように実行されるかを示す出力を提供します。これは、設定をテストしたり、問題を診断する際に役立ちます。

```terminal
sudo logrotate -dv /etc/logrotate.conf
sudo logrotate -dv /etc/logrotate.d/***
```

検証で設定内容が問題なければ、定期実行されていると思いますので、翌日に実行内容の確認を実施する。

# 結果と反省
上記の設定変更を行った結果、私が直面していたログファイルによるディスク容量の逼迫という問題が解決しました。
Logrotateの適切な設定により、ログファイルのサイズが管理され、システムのパフォーマンスと安定性が向上しました。

この経験から学んだことは、システムの監視と継続的なメンテナンスの重要性です。
ログファイルの肥大化は徐々に発生するため、定期的なチェックと適切な管理が必要です。
また、Logrotateのようなツールを利用する際には、その設定を理解し、環境に合わせて適切に調整することが不可欠です。

皆さんの環境でも同様の問題が発生した際に、この記事が参考になれば幸いです。

# 参考
https://qiita.com/zom/items/c72c7bac63462225971b
https://dacelo.space/linux/entry-1265.html
DbeaverをインストールしてDBからデータ取得まで（備忘録）

# 参考1

https://envader.plus/article/121

# インストールから初回起動まで
公式サイトからインストーラをダウンロードする。
次へを押していくとインストール完了。
+ UserはFor me
+ コンポーネントは「☑DBeaver Community,☑Include Java」

起動すると参考１どおりサンプル作るか聞かれる、接続情報（パスワードとか）を入力して終了を押すと下の画面が出た。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/12ef160c-2a2c-d174-be9c-820f60744ab6.png)
内容はDBeaverの機能等の改善のために匿名のデータ送信してよいか聞かれている。
どちらか選択しポップアップが消えると、ドライバをダウンロードする旨のポップアップが出るのでダウンロードボタンを押す。
ダウンロードが終わるとこんな感じになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/82235e51-5ed2-142b-401c-c8267cbfa9db.png)

# テーブルの作成からデータのインサートまで
左上の「ナビゲート(N)」の下にある「SQL」を押すとエディタが出現する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/7bfd4a3a-62a0-428e-e274-a7d220eebbfd.png)
```sql
create table test_tbl(
id int,
name varchar(10)
)
```
↑を実行しtest_tblをcreateしてみる（sqlの実行は右クリック等から）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/f96ad57b-bb17-af94-3761-84719126ce65.png)
test_tblが作成された。

```sql:test.csv
2330,日本電気
6502,トヨタ自動車
9984,LINE
```
適当にtest.csvを作る


```sql
COPY public.test_tbl from 'C:\Users\user_name\Desktop\test.csv' CSV;
```
COPYでinsertするが、下のようなエラーが出てinsert出来ない

「SQLエラー [42501]: ERROR: ファイル"C:\Users\user_name\Desktop\test.csv"を読み込み用にオープンできませんでした: Permission denied
  ヒント: COPY FROMによってPostgreSQLサーバープロセスはファイルを読み込みます。psqlの \copy のようなクライアント側の仕組みが必要かもしれません」

>For people who are still having this issue, one of the fastest workarounds I found (that sidesteps permission changes) is to use the "Users\Public" folder when reading or writing files.
>>まだこの問題が発生している人のために、私が見つけた最も早い回避策 (アクセス許可の変更を回避する) の 1 つは、ファイルの読み取りまたは書き込み時に "Users\Public" フォルダーを使用することです。[^1]

とりあえずという書き込みを見つけたので、
```sql
COPY public.test_tbl from 'C:\Users\Public\Documents\test.csv' CSV;
```
publicで実行してみると成功した。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/0af317f4-9d7a-5bf7-209e-3904dd7f4db4.png)

```sql
select * from public.test_tbl;
```
一応、test_tblの中身を見てみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545173/1bd5fb7a-d420-91d3-9534-9c56260161d4.png)
無事にinsertされている。

# 課題
+ 手動でPostgreSQLサーバーを起動しているが、いちいちサービスから起動するのが面倒くさい
+ COPYのエラーの根本原因はわかってない


[^1]:https://stackoverflow.com/questions/65811460/postgresql-permission-denied-for-copy-from-on-windows
技術に自信がない若手エンジニアは転職活動をした方がいい

## 自己紹介
新卒で入社して、バックエンドエンジニアの経験がまるまる3年くらい。
サービスの規模が大きく、歴史も長いため、運用・保守が業務の大半を占めています。そのため、新規開発をする機会があまりなく、身に付いたのはドメイン知識や社会人力が中心になっていました。
言語はPHPしか使っていません（既存のコードの継ぎ足しや修正しかしていないので果たして使っていると言えるのか・・・）。フレームワークも既存で導入されているLaravelやらFuelやらを触っていますが、正直ノリで使ってるので理解しているかと言われると微妙なところ。筆記テストとかしたら余裕で赤点とると思います。学生自体にPythonとかJavaとか使ってはいましたが、同じくノリで使ってたのでこれらも自信なし。
基本情報やAWSの資格を取ったり、それらについてのなんとなくの知識は少しだけ知ってるよ、みたいな顔ができるくらいのレベル。

## 転職活動をしてみた
うちの会社に入った新卒はだいたい3年目を目安にいなくなることが多いらしく、先輩や同期も辞めていくので、いっちょやってみっか、って感じで転職サイトに登録してみました。年収上がったら嬉しいし。
という感じで適当に転職活動してたら結構絶望したのでそれらを列挙してみます。

### 絶望ポイント1
転職活動するときは新卒の就活同様にアピールするための書類を提出しますが、新卒の就活と違うのは全部事実ベースで書く必要があること。エントリーシートみたいに見せかけの詐欺みたいなアピールは難しいです。まあやってる人はいるのかもしれないけど。自分は性格的に無理でした。
これを書いていると何が辛いって、自分の今までの業務で人様にアピールするようなことが特にないんです。そりゃあ当時の自分の中では結構なチャレンジだったこともありますが、そんな案件ばかりある訳でもなく。運用保守が多いと案件の性質も似てくるので、たいして書くことがなくなってきます。

### 絶望ポイント2
求人には募集必須条件がありますが、その中に「〇〇（開発言語・フレームワーク）を使った経験」とかがあります。自分が行きたいと思った志向も一因にあると思うのですが、だいたいその経験が足りてなかったです。PHPだけできていればいい、という求人はそこまでない印象でした。サーバーサイドをやるにしろ、AWSの知見が必要だったり、マネジメントができたり、何かしら求められます。中途で採用する以上、即戦力を求められるので技術的なハードスキルは必須でした。
冒頭で申したように、PHPがちょっとできるかな？程度だと必要とされないようです。

### 絶望ポイント3
そんなんでも第二新卒枠なら書類通過することもありました。面接の感じも良好で、エージェント経由でもフィードバックの結果も良好でした。
よっしゃあと思ってたら、SPI受けてくれ、との連絡が。SPIってなんか中学数学みたいなやつとか論理パズルみたいなやつとか、そんなに難しい印象はなかったので、余裕ぶっこいて受けたら惨敗しました。
3回くらい受けて全部ボロボロ。簡単な連立方程式解くのすらできなかった。家を先に出た弟を自転車で追いかける兄貴の問題のところでリアルに泣いた。

### 絶望ポイント4
正直自分は技術にそこまで興味があるという訳ではなく、ユーザーの利益があればどの技術使ってようがどうでもいいじゃん、って思ってます。
なのでそれを素直に言いました。面接で。
そしたら「やる気が感じられない」とフィードバックされました。
期間が空いたから分かったことではあるんですが、自分の思考って技術力がないことからの逃げの言い訳としての側面もありました。もちろん今でもユーザー第一という思考は変わらないですが、技術に興味なし、というのは当時の心の弱さから出てきた強がりだったのかもしれないです。

## 転職活動をしてみて
社内の一つの組織しか知らなかったので理解していませんでしたが、現状の自分は市場的に価値がないということが分かりました。
多少分かってたのでそこまでショックではなかったですが、やっぱり精神的に少しきつかったです。
ただ、自分の市場価値を知れたことは大きかったと思います。

正直自分のやりたいことがまだはっきりしてないので、具体的にどういうキャリアを積んでいけばいいかは分かっていません。そんなの決まっている人の方が少ないと思いますが。
だから、とりあえず偏差値を上げて選択肢を広げるというイメージで、やれることを増やしていこうと思えるきっかけになりました。そういう意味では、転職活動って自分の偏差値を知れる模試のようなものだな、と思いました。

なので、同じように悩んでいる人はまず模試を受けてみてもいいのかもしれないですね。
myFirstArticle

# 最初の記事

Qiitaを始めたのでとりあえずQiita CLIのセットアップとGitHubとの連携設定を試す。  
記事は基本的に、使い慣れたMarkdownで書いていければと思う。

この仕事についてからこれまで、沢山の見知らぬ先輩達に助けられてここまできた。  
これからは自分も誰かの役に立てるように、学んだことを少しずつアウトプットしていけたらと思う。
RestTemplateクラスを使用してHTTPレスポンスの詳細を取得する

# 概要
SpringフレームワークのRestTemplateを使用してHTTPリクエストを送り、そのレスポンスの詳細を取得する方法をまとめた。

# 環境
|項目|バージョン|
|----|---------|
|JDK|Oracle JDK 21.0.1|
|SpringBoot|3.0.1|
|SpringWeb|6.0.3|

# RestTemplateクラスの主要なメソッド
下表の通り、HTTPメソッドに対応したメソッドが用意されている。

|HTTPメソッド|RestTemplateクラスのメソッド|
|-----------|---------------------------|
|GET|getForObject<br>getForEntity|
|PUT|put|
|POST|postForLocation<br>postForObject<br>postForEntity|
|DELETE|delete|
|汎用|exchange|

# HTTPレスポンスの詳細を取得する方法
HTTPレスポンスのステータスコードやヘッダーの内容を参照するには、レスポンスをResponseEntityに格納する必要がある。
RestTemplateクラスのメソッドで戻り値としてEntityを取る`GET`と`POST`については、対応する`getForEntity`と`postForEntity`を使用すれば良い。
戻り値として、Entityを取らない`PUT`と`DELETE`については工夫が必要になる。

## 戻り値としてEntityを取るメソッドの実装例
例として`GET`の場合を示す。
```java:GETの実装例
ResponseEntity<Sample> responseEntity = resTemplate.getForEntity(URI, Sample.class);
int statusCode = responseEntity.getStatusCode().value();
System.out.println("StatusCode is " + statusCode + ".");
//実行結果：StatusCode is 200.
```

## 戻り値としてEntityを取らないメソッドの実装例
戻り値としてEntityを取らないメソッドについては、汎用的な`exchange`メソッドを使用して通信し、レスポンスをEntityに格納する。
例として、`DELETE`の場合を示す。
```java:DELETEの実装例
RequestEntity<Void> requestEntity = RequestEntity.delete(URI).build();
ResponseEntity<Void> responseEntity = restTemplate.exchange(requestEntity, Void.class);
int statusCode = responseEntity.getStatusCode().value();
System.out.println("StatusCode is " + statusCode + ".");
//実行結果：StatusCode is 204.
```

# 参考文献
土岐孝平著、「プロになるためのSpring入門」、技術評論社、2023年7月25日 初版 第1刷
可視化ツールWandbを使って、LLMのRAG動きを追跡しました。

Wandbが最近、LLMの処理フローを追跡する（callback）機能をアップデートしました。昨日試してみたところ、非常に使い勝手が良かったので、紹介したいと思います。

LangchainでLLMchainを構築する際に、処理の全プロセス（例えば、RAGの場合は、データの読み込み⇒分割⇒embedding⇒LLM⇒最後chainsのまとめ）を把握するのがなかなか難しいものです。callbackモジュールを使えば確認はできますが、やはり読みにくいために、私の場合は基本的にchainの流れを頭の中で想像しながら作っていました。なので、chainが複雑になると、debugする時は結構大変だったです。


昨日は簡単なＲＡＧを作って試しました。
データベースとして使われたのはChatGPTから作ってくれた"ＲＡＧに関する解説文章"です。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/a9bc39eb-52c3-2aa2-5598-0c527dbec579.png)


まず、検索のoutputを確認しました。ここで初めてwandbを呼び出すため、以下のようなAPIとreportのサイトが表示されます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/5dac7b5d-9528-8a8e-9ac0-1777f6a778b2.png)

reportのサイトに入ると、すでに記録がつけられていました。(検索のみ試したので、チェーンは1つだけです)

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/1327ad02-9ad1-f674-983e-902fd8edce9c.png)

それで、一つ完全なRAGを実行してみたら、以下のような記録が残されました。
最初の実行chainから最後のoutputparserchainまでの流れが示されています。各ステップを押すと、どのような処理を行い、次のチェーンにどのようなデータを渡したのかも、すべて確認することができます。
![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/7161129c-21d9-e401-84ad-dfe81946d92f.png)


例えば、LLMの部分をクリックすると、LLMに渡されるプロンプト文の詳細が表示されます。（参考内容（RAG）と質問がLLMにうまく渡されたことがわかります）

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3556459/2d08d05d-2063-81cb-26bf-0640992b6785.png)

LLMに関する開発では、基本的に複数のツールや機能を組み合わせて使用することになりますので、wandbのような可視化ツールを活用することは大事かなと思います。

興味ある方：

https://docs.wandb.ai/ja/guides/prompts/quickstart
初心者向け！MavenとJavaでデータURIスキームを簡単生成

# はじめに
JavaとMavenを使用して、ファイルをデータURIスキームに変換する方法を、初心者の方にも理解しやすく解説します。この技術は、Web開発において小規模なリソースを効率的に扱う際に役立ちます。

# 解決したい問題
- ファイルをデータURIスキームに変換する基本的な方法を理解したい。
- Mavenプロジェクトでの依存関係の管理方法を学びたい。

# 構成
1. Maven設定の基本
2. JavaでのデータURIスキームの生成方法
3. まとめ

# 1. Maven設定の基本
Mavenはプロジェクトの依存関係を管理し、ビルドプロセスを簡略化します。`pom.xml`ファイルに必要なライブラリを記述することで、依存関係を簡単に管理できます。この例では、Apache Tikaライブラリを使用してファイルのMIMEタイプを検出します。

```xml:pom.xml
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-core</artifactId>
    <version>2.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.tika</groupId>
    <artifactId>tika-parsers-standard-package</artifactId>
    <version>2.9.1</version>
</dependency>
```

# 2. JavaでのデータURIスキームの生成方法
データURIスキームは、ファイルをウェブブラウザが解釈できる形式に変換するために使用します。Javaの`Files`クラスと`Base64`エンコーダを使用して、この変換を行います。

## ステップ1: ファイルの読み込み
指定されたファイルパスからファイルを読み込み、バイト配列に変換します。

## ステップ2: MIMEタイプの検出とBase64エンコーディング
Apache Tikaを用いてファイルのMIMEタイプを検出し、JavaのBase64ユーティリティでエンコードします。

## ステップ3: データURIスキームの生成
最終的に、"data:"、MIMEタイプ、";base64,"、Base64エンコードされたデータを組み合わせてデータURIを生成します。

```java:DataUriSchemeGenerator.java
package jp.livlog.cotogoto.api.discord;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;

import org.apache.tika.Tika;

public class DataUriSchemeGenerator {

    public static void main(final String[] args) {

        try {
            // WAVファイルのパス
            final var filePath = "C:\\temp\\test.wav";

            // ファイルからバイト配列を読み込む
            final var fileContent = Files.readAllBytes(Paths.get(filePath));

            // Base64エンコードされたデータを取得
            final var dataUri = DataUriSchemeGenerator.encodeToBase64WithMimeType(fileContent);

            // 結果を出力
            System.out.println(dataUri);
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }


    public static String encodeToBase64WithMimeType(final byte[] data) throws Exception {

        // MIMEタイプを検出
        final var tika = new Tika();
        final var mimeType = tika.detect(data);

        // データをBase64エンコードする
        final var base64Content = Base64.getEncoder().encodeToString(data);

        // Data URIスキームを付与する
        return "data:" + mimeType + ";base64," + base64Content;
    }
}

```

# 3. まとめ
JavaとMavenを使用したデータURIスキームの生成は、Web開発において非常に便利です。この記事を通じて、初心者の方でも基本的な流れを理解し、自身のプロジェクトに応用できるでしょう。
何気なく使い始めたMarkDownを改めて振り返る

# はじめに
自分は10月よりAPPRENTICE SHIP(内定直結型エンジニア学習プログラム)のカリキュラムに２期生として参加しています。
その一環としてチーム開発を行っており、技術情報をまとめることで学習に役立てるよう、記事を作成しております。

# MarkDownとは？
Markdown（マークダウン）は、文書を記述するための軽量マークアップ言語のひとつである。本来はプレーンテキスト形式で手軽に書いた文書からHTMLを生成するために開発されたものである。しかし、現在ではHTMLのほかパワーポイント形式やLATEX形式のファイルへ変換するソフトウェア（コンバータ）も開発されている。各コンバータの開発者によって多様な拡張が施されるため、各種の方言が存在する。
[Markdown - Wikipedia より](https://ja.wikipedia.org/wiki/Markdown#:~:text=Markdown%EF%BC%88%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%EF%BC%89%E3%81%AF%E3%80%81,%E3%82%82%E9%96%8B%E7%99%BA%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82)
[Markdownチートシート](https://qiita.com/Qiita/items/c686397e4a0f4f11683d)

# 実際に使い始めてみて
MarkDown記法を生活の中で使うようになって、３ヶ月(2024/1 現在)になりました。 
VScodeでドキュメントなどを書くようになり、有り難みをひしひしと感じています。 
「Notion」「Discode」などのアプリケーションでも対応しており、文章を書くのに欠かせない存在となってきました。 
ここで改めて、マークダウンの書き方をおさらいしていきたいと思います。

# よく使う！基本的な記法


Markdownにおける改行と段落の作成方法について説明します。

## 改行
Markdownでは、行の終わりにスペースを2つ入れることで改行が出来ます。

これは最初の行です。  
これは新しい行です。
この例では、です。 の後にスペースを2つ入れています。これにより「これは新しい行です。」が新しい行に表示されます。

## 段落
新しい段落を作成するには、空白行を一行挿入します。つまり、段落間には何も書かない行を1行入れるだけです。

これは最初の段落です。

これは二番目の段落です。
この例では、2つのテキストブロックの間に空白行があります。これにより、それぞれが独立した段落として表示されます。

## 見出し
見出しは#（シャープ）を使用して表現されます。見出しのレベルに応じて#の数を増減させます。
```
# これはH1見出しです
## これはH2見出しです
### これはH3見出しです
```
## リスト
リストは順序付きリスト（番号つきリスト）と順序なしリスト（マークつきリスト）の2つがあります。

### 順序付きリストの例：

1. 項目1
2. 項目2
3. 項目3

### 順序なしリストの例：
- アイテム1
- アイテム2
- アイテム3

また、順序なしリストは*や+、-を使って表現することもできます。

## リンク
リンクを挿入するには、[リンクテキスト](URL)の形式を使用します。

```
[Google](https://www.google.com)
```
これにより、"Google"というテキストがリンクとして表示されます。

## 画像
画像を挿入するには、```![代替テキスト](画像のURL)```の形式を使用します。
```
![OpenAI Logo](https://d3i71xaburhd42.cloudfront.net/8ea5d76d773cbcdfa11dcb1b0c68ebe42d4ac561/1-OpenAI.png)
```
これにより、代替テキストが表示される場所に画像が表示されます。

## 引用
Markdownでは、文章を引用する際には「>」 を使用します。これにより、テキストが引用としてフォーマットされます。

> これは引用の例です。

## コード挿入
コードブロックを挿入するには、バックティック（`）を使用します。単一行のコードには単一のバックティックを、複数行のコードには3つのバックティックを使用します。言語を指定することもできます。

```javascript.js
Copy code
console.log('Hello, world!');
ruby
Copy code
puts 'The best way to log and share programmers knowledge.'
```
## 水平線
文章内でセクションを分けるための水平線を挿入するには、3つのハイフン（---）を使用します。 
 
これはセクションの前のテキストです。

---
これはセクションの後のテキストです。

## 強調
テキストを強調するためには、アスタリスク（*）またはアンダースコア（_）を使用します。イタリック体にするには1つ、太字にするには2つ使用します。
```
markdown
Copy code
*イタリック体*
**太字**
```
markdown
Copy code
*イタリック体*
**太字**

## 所感
改めて、マークダウン記法を振り返りましたが、まだまだ使っていない記法があります。
Qiitaでは、これ以外にも独自の記法があったりと、奥が深いですね。。。
これからも、マークダウンを使って綺麗なドキュメントが書けるようにしていきたいものです。
AWS認定セキュリティ専門知識を更新したので勉強方法を書いておく

# はじめに
セキュリティ専門知識を久々に更新してきたので勉強方法を書いていきます。
SCS-01からSCS-02に変わって初めての試験です。

# 前提
AWSメインのクラウド基盤のエンジニア。Top Engineers(Service)、ALL Certificate Engineersに認定。
勉強期間:１ヶ月くらい

# 問題の解き方
これはどの試験にも共通するのですが、サンプル問題や問題集などをみるとわかるのですが、問題文に書いてあるキーワードから、最も適切な選択肢を選ぶことが要求されます。
例えば、「コストを最重視する」、「可用性が重要」、「運用が楽なもの」等々
どれも実現できそうな選択肢の中から、問題で求められる選択肢を素早く選びます。キーとなる言葉をしっかり押さえることが大事です。

# 試験までに何をやるか
## 試験ガイドを読む
https://aws.amazon.com/jp/certification/certified-security-specialty/

出題される分野、配分をしっかり読みましょう。
ここに書いてあるものを中心に記憶に怪しいサービスがあれば深堀りします。
2年半ぶりなので、サービス内容のアップデートなども気にしました。

## サンプル問題を解く
サンプル問題をといて、どのくらいとけそうかの感覚をつかみます。

## 問題集を解く
以下2つを２周解きました。

https://aws.koiwaclub.com/

https://www.udemy.com/course/aws-certified-security-specialty-practice-exams-course/

## 模擬試験を解く
https://explore.skillbuilder.aws/learn/course/external/view/elearning/15232/aws-certified-security-specialty-official-practice-question-set-scs-c02-japanese

答えも解説もつくようになった練習問題です。
スラスラ解けることを確認したら残りは試験の申込みだけです。


# 試験の感想
そこまで難しくなかったかなぁ・・・という印象、とはいえ、２択で迷ったものが複数ありもう少し勉強しないとなと思いました。


# 試験結果
合格
スコア８８７
まぁまぁよいのでは・・・と思います。

# まとめ
今年は更新が多いので頑張ります！
試験が切り替わっていて過去の記憶ではなんともならないものが多いですね
セキュリティ専門知識はAWSに関わる人であれば一度受けていただきたい資格だなと問題を解着ながら思いました。
FortigateのWANとLANで通信する

# やりたいこと
FortigateのWAN側の端末とLAN側の端末で通信する

## NW環境
以下に記載の構成と同じ
https://qiita.com/KINAKO_OMOCHI/items/77aeb30edb91d0a0f988
デフォルトゲートウェイの設定を追加。
<details><summary>コントロールパネルから設定</summary>

winボタン　+　Rキー　→　ncpa.cpl　→　該当のインターフェースを右クリックしてプロパティ　→　　ipv4の設定を右クリックしてプロパティ　→　FortigateのWANのインターフェースに振られているIPを指定する
</details>

![スクリーンショット 2024-01-28 185600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3683457/e5aa5b7e-4a1e-af71-30e7-30aa0f96c1d2.png)

WAN側の端末は172.18.11.99
LAN側の端末は192.168.11.99（キャプチャ通り）

## やったこと
+ ipv4のポリシー変更
+ LANからWANの通信ポリシー追加
+ 端末のデフォルトゲートウェイの削除と追加

下の参考記事一覧の通りに実施

## ハマったこと
LAN側からWAN側の端末にpingが通らなかった。原因はLAN側の端末にデフォルトルートが2つ存在した。
コマンドプロンプトでデフォルトゲートウェイの削除と追加をすると通信できた。
〇Windows10 home
管理者権限で以下のコマンド実行
【管理者権限でコマンドプロンプト開く方法】
・winキー+rキー　→　ctrlキー+shiftキー　→　OK

C:\WINDOWS\system32>route print　★WAN側の端末の経路情報の表示
●省略●

IPv4 ルート テーブル
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0            リンク上    192.168.11.200    291　★デフォルトルートがふたつある
          0.0.0.0          0.0.0.0    192.168.11.99   192.168.11.200    291　★デフォルトルートがふたつある
     10.212.134.0    255.255.255.0    192.168.11.99   192.168.11.200     36
●省略●

C:\WINDOWS\system32>
C:\WINDOWS\system32>route delete 0.0.0.0　★デフォルトルートの削除
 OK!

C:\WINDOWS\system32>
C:\WINDOWS\system32>route print
●省略●

IPv4 ルート テーブル
アクティブ ルート:　★デフォルトルートが消えた
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
     10.212.134.0    255.255.255.0    192.168.11.99   192.168.11.200     36

●省略●

コントロールパネルで使用しているLANにデフォルトゲートウェイを追加する。コントロールパネルからコマンドでもいいっぽい。


C:\WINDOWS\system32>
C:\WINDOWS\system32>route print

●省略●
IPv4 ルート テーブル
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0    192.168.11.99   192.168.11.200    291　★デフォルトルートが追加された
●省略●

今まで失敗していたpingが通るか確認。

C:\WINDOWS\system32>
C:\WINDOWS\system32>ping 192.168.11.200

192.168.11.200 に ping を送信しています 32 バイトのデータ:
192.168.11.200 からの応答: バイト数 =32 時間 <1ms TTL=128　★pingが通ったことを確認できた
192.168.11.200 からの応答: バイト数 =32 時間 <1ms TTL=128
192.168.11.200 からの応答: バイト数 =32 時間 <1ms TTL=128
192.168.11.200 からの応答: バイト数 =32 時間 <1ms TTL=128

192.168.11.200 の ping 統計:
    パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
    最小 = 0ms、最大 = 0ms、平均 = 0ms

C:\WINDOWS\system32>


## 結果
やりたかったことが実現できた。
デフォルトルートが２つ存在する端末のせいだが、通信できない原因がわからずかなり時間がかかった。

### その他
IPアドレスを変更する機会が多いのでバッチファイルを作って自動化したい。
DHCPサーバからIPを受け取れば自動でデフォルトゲートウェイの設定が入る。

## 参考記事　ありがとうございました！
端末にデフォルトルートが二つある
https://nohohonlab.exblog.jp/8573469/
Fortigateポリシー変更
https://nwengblog.com/fortigate-policy/
JavaScriptで配列をランダムで表示させたい

## はじめに
アンケートを取る機能において、ユーザーがチェックボックスの一番上の項目しかチェックしない問題に直面しました。改善策の一つとしてチェックボックスの項目をランダムに表示させるようします。本記事では、JavaScriptを使用してランダムに表示される方法を書いています。

## TL;DR
* Math.random() は0から1未満の浮動小数点数を生成する。
* Math.random() - 0.5 は-0.5から0.5までの浮動小数点数を生成する。
* Math.random() - 0.5 を使用することで、範囲指定のランダムな数値を得やすくなる。

## Math.random() とは
Math.random() メソッドは、0以上1未満のランダムな浮動小数点数を生成します。以下はその基本的な使い方です。

```javascript:index.js
const random = Math.random(); // 0以上1未満のランダムな数値を生成
```

## Math.random() - 0.5 の使い方
Math.random() - 0.5 を使うことで、-0.5から0.5までの浮動小数点数を生成できます。この値を利用することで、特定の範囲のランダムな数値を得ることができます。以下はその例です。

```javascript:index.js
const randomInRange = Math.random() - 0.5; // -0.5から0.5までのランダムな数値を生成
```

この値を活用することで、範囲指定のランダムな数値を簡単に得ることができます。例えば、-10から10までのランダムな整数を生成するには以下のようにします。

```javascript:index.js
const randomIntInRange = Math.floor(Math.random() * 21) - 10; // -10から10までのランダムな整数を生成
```

この式では、0から20のランダムな数値を生成し、最後に-10を引くことで-10から10までのランダムな整数を得ています。

## ランダムにする関数の例
ランダムに配列をシャッフルする際に、Math.random() - 0.5 を使用した shuffleArray() 関数を以下に示します。

```javascript:index.js
function shuffleArray<T>(array: T[]): T[] {
  const shuffledArray = [...array]; // 配列をコピー

  // 要素のランダムな順序を生成
  shuffledArray.sort(() => Math.random() - 0.5);

  return shuffledArray;
}

// 使用例
const originalArray = [1, 2, 3, 4, 5];
const shuffledArray = shuffleArray(originalArray);
console.log(shuffledArray); // シャッフルされた配列が表示されます
```

この関数を使用することで、与えられた配列の要素がランダムに並び替えられ、シャッフルされた配列が得られます。
* Array.prototype.sort() メソッドと組み合わせて使用しています
* sort() メソッドは、比較関数を引数として受け取り、その関数が返す値に基づいて配列の要素を並び替えます。
* 比較関数が負の値を返した場合、最初の要素を前に移動し、正の値を返した場合、最初の要素を後ろに移動します。ゼロを返した場合、要素は移動しません。

## おわりに
真のランダムシャッフルを実現するには、フィッシャー–イェーツのシャッフルアルゴリズムを使用するのがいいみたい
自宅インフラにいろんなものを導入してどうだったか

ここ2年ぐらいでかなり自宅インフラを充実させてきましたが、色々と良かったこと、微妙だったことなどが見えてきました。
今回はその辺りを思いつくままに挙げてみようかなと思います。
インフラ周りはそこまで強いわけではなく、自己流なのでお手柔らかにお願いします:bow:

**2024/3/9追記**
第二弾も公開しました！
もしよろしければこちらもどうぞ！
https://qiita.com/wancom/items/7265b9e621a72b73e0be

## 1. 良かったこと：Proxmoxで仮想基盤を構築したこと
最近はDockerでサーバーを建てることも一般的になってきているのでVMが必要になるという機会は減ってきていると思います。
一方で、何も考えずに色々入れて試してみる、ネットワーク周りで遊んでみる、という場合は個人的にはVMの方が楽だと感じています。
仮想基盤をきちんと構築すると、気軽にVMを立てて、捨てて、ができるので非常に便利です。

## 2. 良かったこと：早々に仮想基盤を3台のノード構成にしたこと
Proxmoxでは複数ノードによるクラスター構成を組むことができます。そして、クラスター構成にすることで仮想マシンを起動したまま別のノードに移動させる、ライブマイグレーションを使用することができます。
ホストのアップデートや掃除などでノードを時々シャットダウンすることがありますが、仮想マシンに影響を与えずにノードを落とせるので非常に便利です。
また、3台という構成も重要で、メンテナンスや障害で一台ダウンしていても安定した仮想基盤を維持することができます。

## 3. 微妙だったこと：仮想マシンを立てすぎた
仮想基盤便利！と言って40台も建てるとメンテナンスや監視の追加などが大変になります。
ここまできたら、逆に大量のVMに対するソリューションを探してみてもいいかもしれませんね。

## 4. 良かったこと：NASを導入した
先ほど触れたライブマイグレーションはVMのディスクイメージがノード上にあると、そのデータを転送させる必要があります。これに時間がかかるため、NASにディスクイメージを保管し、ノードには保持させないようにしました。
これによりライブマイグレーションが気軽に行えるようになったほか、何らかの障害でノードがダウンしても自動で他のノードで立ち上げてくれるHA(高可用性)を使うことができるようになりました。

## 5. 微妙だったこと：NASでHAを行った
SynologyのNASでは、同じ構成のNASが2台存在すると、そのNAS同士でHA構成を組むことができます。これにより、Disk障害だけでなく、筐体自体に異常が発生してもサービスを継続することができます。
ロマンを求めてNASもHA構成にしてみましたが、
- NASの切り替えに1分以上のサービス断が発生することがあり、その間VMがフリーズする
- NAS間でデータを同期する必要があり、LAGが組めなくなるためその分IOのスループットが下がってしまう

というところで問題がありました。ちゃんとした構成にするのであれば、Cephなどの別の仕組みをきちんと導入した方が良さそうです。
とは言いつつ、1~2分程度不安定になるぐらいでその後も継続してVMは動き続けるので、HAはいざという時には有効的だと思います。

## 6. 微妙だったこと：8ポートのSwitchを買い足した
スイッチ1台では足りなくなったので、2台へ、また足りなくなったので3台へと8ポートのスイッチを増設しました。ただ、8ポートのスイッチを増設すると、スイッチ間の接続で2ポート使えなくなるため、効率が悪くなります。

## 7. 良かったこと：24ポートのスイッチに移行した
結局ポートが足りなくなって24ポートのスイッチに切り替えることで解決しました。ポートの効率が良くなるほか、電源周りも1本で済むのでシンプルになります。最初からポート数の多いスイッチを買うべきでした。
（ちなみに24ポートのスイッチもポートが足りなくなりそうになっています…）

## 8. 良かったこと：2.5Gbpsのスイッチを買った
ノードにしているNUCが2.5Gbpsに対応していたので、それを活かせるように2.5GbpsのシンプルなL2スイッチを購入しました。
基本的にノード間の通信なのでスイッチに高度な機能も必要なく、また、VMのライブマイグレーションが早くなり大満足です。

## 9. 良かったこと：ノードにUSBのNICを足す
クラスター構成時にネットワークが切れると色々と問題が発生します。そのためProxmoxでは複数のネットワークを使ってクラスター間の通信が行えるようになっています。これにより、何らかの理由で片方のネットワークが落ちてももう片方のネットワークでクラスター構成が維持できるようになります。
また、一方は高速な通信が行えるネットワークでノード間の通信を行い、もう一方で外との通信を行う、と言ったことが可能なため、用途に合わせて柔軟な通信が行えるようになりました。

## 10. 微妙だったこと：ネットワーク周りの冗長化が行えていなかった
スイッチが落ちるとほとんどの機器のネットワークがダウンします。1台のスイッチに依存せず、複数のスイッチで通信を分散される構成を考えて設計するべきでした。
とはいえ、なかなかL2のレベルでの冗長化はコストの面から難しいというのが現状です。
最近のProxmoxではL3のレベルで冗長構成を作ることができそうなことも見えていますので、そう言った方法で解決できないか、今後検討していきたいと思っています。


# 他にも…
色々列挙していったところ、思ったよりもたくさん出てきたので記事を分割します。
好評であれば（好評でなくても勝手に）第二弾を書こうと思いますのでお楽しみに？

**2024/3/9追記**
第二弾も公開しました！
もしよろしければこちらもどうぞ！
https://qiita.com/wancom/items/7265b9e621a72b73e0be

# あとがき
正月のキャンペーンで何か適当に書こうかなということでつらつらと書き出してみました。
本当にただ書き出したに近いのでお見苦しいところもあったかもしれません。
ただ、中には変なことをしているものもあるので誰かが何かの参考になったらいいなと思っています。
誤字、間違い等ございましたらご指摘いただけますと幸いです。
SQLAlchemy(非同期)のCRUD

# 1.はじめに
最近、PythonのWebフレームワークであるFastAPIを使っています。
SQLAlchemyのORMapperを使って、PostgreSQLのデータベースを操作するプログラムを書きましたので、備忘録も兼ねて本記事に実装方法を残します。

今回は、以下のようなテーブルを題材にします。

### Testテーブル
| カラム名 | 型 | 備考 |
|:-:|:-:|:-:|
|id|integer|PK,Autoincrement|
|name|character(10)||
|created_at|date|Default: 現在時刻|

### Model
```models/test.py
from sqlalchemy import Column, Integer, String, DateTime
from datetime import datetime
from database.database import Base

class Test(Base):
    __tablename__ = "test"
    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    name = Column(String(10), index=True)
    created_at = Column(DateTime, default=datetime.now)
```

# 2.CRUD(Controller)
```controllers/test.py
from typing import Any, List, Tuple
from sqlalchemy import select, insert, update, delete
from sqlalchemy.engine import Result
from sqlalchemy.ext.asyncio import AsyncSession
from models.test import Test as test_model
import datetime

# データを登録(C)
async def create(db: AsyncSession, name: str) -> Any:
    result = await db.execute(insert(test_model).values(name=name, created_at=datetime.datetime.now()))
    await db.commit()
    return result

# データを全件検索(R)
async def read(db: AsyncSession) -> List[Tuple[int, str, datetime.datetime]]:
    result: Result = await db.execute(select(
        test_model.id,
        test_model.name,
        test_model.created_at
    )
    return result.all()

# データを更新(U)
async def update(db: AsyncSession, id: int, name: str) -> Any:
    result = await db.execute(update(test_model).values(name=name, created_at=datetime.datetime.now()).where(user_model.id == id))
    await db.commit()
    return result

# データを削除(D)
async def delete(db: AsyncSession, id: int) -> Any:
    result = await db.execute(delete(test_model).where(test_model.id == id))
    await db.commit()
    return result
```

# 3.終わりに
今回は、CR(全件検索)UDの書き方について記事にしました。
Rについては、await db.execute(...).filter(...)と書くことで、特定の条件のレコードのみ取得することができます。
本記事はルーティングについて触れていませんので、(気が向いたら)別記事としてあげたいと思います。