OmniAICreator/Qiita-1.07M huggingface dataset:
（謎解決）warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいま す。データの損失を 防ぐために、ファイルを Unicode 形式で保存してください。

# utf-8で保存しているはずなのに... 
ファイルはutf-8のはずなのにこのwarning C4819がうるさい。いろいろ調べていった結果コメントに含まれる日本語が原因なことがあるらしい。ということで、コメントの削除とコンパイルを繰り返してどの文字が原因かを二分探索した結果、文字数が多くなるとこの警告がでてくることが分かった。
### 環境情報
- Windows11
- NVCC
- Microsoft Visual Studio 2022

エラーがでるソースコード。
```c++:hellowworld.cu
#include <iostream>

int main(){
    //あああああああああああああああ
    std::cout << "Hello, World" << std::endl;
}
```
```bash
helloworld.cu
helloworld.cu(1): warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいま
す。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
helloworld.cu(1): warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいま
す。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。
   ライブラリ a.lib とオブジェクト a.exp を作成中
```
警告がでないプログラム。
```c++:helloworld.cu
#include <iostream>

int main(){
    //ああああああああああああああ
    std::cout << "Hello, World" << std::endl;
}
```
```bash
helloworld.cu
   ライブラリ a.lib とオブジェクト a.exp を作成中
```
変更点はコメントの"あ"を1文字削除しただけ。**ワケガワカラナイ...**

# まとめ
コメントの文字数を減らすとなくなるかも？？

# 追記
visual studioでutf-8 (**BOM付き**)を選択することで解決できました。
点群表示機能を自作してみる

Windows 上で点群を表示する機能を実装してみたくなったので作ってみます。
手元のノートPCではGeForce とか使えないけど、100億点位は表示してみたいなあ。

[GitHub](https://github.com/yumimatoba14/DirectXMfc)

# 記事の予定

1. [DirectX11 を使ってみる](https://qiita.com/yumimatoba14/items/e2eb77f82cac7a161c30)
1. [DirectX で点を表示してみる](https://qiita.com/yumimatoba14/items/e8203e1baf9572b61e96)
1. [100万点を表示してみる](https://qiita.com/yumimatoba14/items/f49ea3be322f2865efff)
1. [大規模点群を表示する作戦を考えてみる](https://qiita.com/yumimatoba14/items/61ea891b303f2063d263)
1. [プログレッシブ表示とLODを実装してみる](https://qiita.com/yumimatoba14/items/f9e7684949c33a8dcf34)
1. [点群分割を実装してみる](https://qiita.com/yumimatoba14/items/06a9cfbd1f029e3e44d0) (10億点)
1. [pts ファイル読み込みを実装してみる](https://qiita.com/yumimatoba14/items/2b913f66dae5ba2bf2ac) (2億点)
1. [点群のピック機能を実装してみる](https://qiita.com/yumimatoba14/items/de4fec3114153a5a725f)

おまけ
1. [(C++)クラスのmove操作を実装する](https://qiita.com/yumimatoba14/items/5885b804fef76ed83b61)
1. [(C++)独自の streambuf を実装する](https://qiita.com/yumimatoba14/items/c4ac015a09dea2b54bf3)

# 目論見書
作りながらも更新しています。悪しからず。

## 目標
* 3D点群ファイルを表示する機能をWindows上で試作する。点数としては10億点位は最低限目指す。 （GPUにデータを全部乗っけることは不可なレベル。）
* それなりにチープな環境で動かすことを目指す。 CPU 内臓のGPUで動けば、まあまあ満足するかなという目論見。

詳細を補足します。
* 試作ということで、大体こういう作りをすれば表示できそうだなということが分かるところまで（または無理ということが分かるまで）作ることにします。
  * 100億点→10億点の違いは根性のなさです。
* スキャナがないので点群情報は自作します。
  * 1m×1mの平面に 1mm ステップで点を打てば100万点、それを1000枚用意すれば簡単に10億点の点群も用意できるということに気づき、やってみる気になりました。
  * 開発用にはこういう単純なデータの方が都合もよいし。
  * [静岡県のデータ](https://www.geospatial.jp/ckan/dataset/virtual-shizuoka-mw)とかはいずれ試そう。
* CPU 内臓のGPUで動けばいいかなと思ってましたが、実際試したところ思ったより動くようです。 OpenGL と相性が悪いだけなのかもしれません。 試してみるものですね。
  * ということで、グレードダウンしてDirectX のリファレンス実装で動かすことにするかもしれません。
* CPU(非グラフィック機能)、メモリ、ディスクにどれくらい依存するかは未定、というか今回の技術調査の目的の一つですね。(アウトオブコア前提なので、基本的には点群データをSSDに乗せることは避けられないが...)

## 前提とする環境
* CPU は Core i5-8250U (1.6GHz)
  * Core i5 をチープと言ったら怒られそうですが、最新なわけではないですしご容赦を。
* GPU は Intel UHD Graphics620 を使用してます。
    * 「ギリギリ軽めの3Dゲームまで」使えるそうです。([参考](https://pc-bto.net/intel-uhd-graphics-game/))
* 開発環境は Visual Studio 2019、C++
  * たまたま手元にあった開発環境です。
* 3D 描画には DirectX11 を使用します。
  * 3D表示をしたいだけなので 12 だと面倒かなあというのと、11 なら十分情報が落ちているだろうという算段。(12でも十分あるのかな。またいずれ。)
  * OpenGL だと GPU を選ぶ印象。 DirectX ならそういうこともないかなあという期待のため。(DirectX 初体験です。DirectX を使ってみたかっただけでもある。)
* Windows デスクトップアプリケーションの構築には MFC を使います。
  * ここは今回のこだわりポイントではないので、一番慣れてるものということで採用。 Visual Studio2019 なら問題なく使えるようだし。
* RAMは16GB。
    * メモリもチープとは言えないですね。主な用途はファイルキャッシュになる見込みです。
* システムドライブは SSD で 200GB 超(カタログスペック的には256GBかな?)です。
    * 100億点の点群をファイルに書くと150GB位になりそうで、ちょっと足りない...
【設計】<第1回>デザインパターンの学習

# デザインパターンの学習
今回はデザインパターンの学習記録をする。
アウトプットとして書くだけなのできれいにまとまらないと思う。

# Iterator
#### Iteratorパターン
何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのもの。
数え上げの仕組みがAggregateの外に置かれている特徴がある。この特徴によれ、1つのConcreteAggregateに対して複数のConcreteIteratorを作れる。

#### Aggregateインタフェース
数え上げるものの集合体。
宣言するメソッドはiteratorメソッドのみ。
iteratorメソッドはiteratorを1個作成するためのメソッド。
集合体の数え上げ、スキャンはiteratorメソッドを使ってIteratorインタフェースを実装したクラスのインスタンスを1個作成する。

#### Iteratorインタフェース
要素の数え上げを行うもの、ループ変数のような役割を果たすもの。
最も単純なものでは、「次の要素」が存在するか調べるメソッドと「次の要素」を得るためのメソッドを宣言する。
次の要素を取得する際は、裏では内部状態を次に進める処理が動いている。

#### Iteratorパターンの要素

###### Iterator(反復子)の役
要素を順番にスキャンするインタフェースを定める。次の要素が存在するかどうかを得るメソッド、次の要素を得るメソッドを定義する。
###### ConcreteIterator(具体的な反復子)の役
Iterator役が定めたインタフェースを実装する役。スキャンするために必要な情報ももっておく必要がある。
###### Aggregate(集合体)の役
Iterator役を作りだすインタフェースを定める。スキャンしてくれる人を作り出す。
###### ConcreteAggregate(具体的な集合体)の役
Aggregate役が定めたインタフェースを実装する役。ConcreteIterator役を作り出す。

#### なぜIteratorを使うのか？
実際の実装とは切り離して数え上げができるから。実装したものに依存しない。

#### 注意点
AggegateとItteratorは依存している。
具体的なクラスだけを使うとクラス間の結合が強くなる。
「次」メソッドは現在の位置を返しつつ次の位置に進める。
rex0220 レコードロック・プラグイン

kintone 編集画面で、レコードロックを行うプラグインです。

主な特徴
・編集開始時に、レコードのレビジョンをチェック
・編集開始時に、レコード編集中かチェックし、編集を中止か続行を選択
・編集中の担当者に、コメント送信する機能
・ステータス変更・作業者変更対応
・３ヶ国語対応
・PC・モバイル対応

Ver.2
・プラグイン設定画面のデザイン変更対応
・プラグイン設定に、「説明」設定機能を追加

# 概要

編集画面表示中に、CSV読み込み・REST API によるレコード更新が行われると編集の保存ができません。
編集画面表示中に、ブラウザーを閉じたりPCの電源を切ったりした場合は、レコード編集中のままになります。
レコード編集中のままになったレコードは、編集画面表示時に「編集を続ける」を選択して編集できます。

- 編集画面
![2023-02-19_20h36_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/6e93cd95-a371-2706-42e2-3235911c806b.png)

- ステータス変更、作業者変更
![2023-02-19_20h39_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/6f3887be-f82f-efce-7848-f50aa8cb7ca5.png)

- 編集一覧画面
![2023-02-19_20h43_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/79b68bfb-2af5-d929-e2d6-03da6f7d35e7.png)

- モバイル表示
![2023-02-19_20h47_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/b3e27cdb-1bc7-5d48-5d5a-65b3614d0b82.png)

# 操作例

![2023-02-19_20h57_35.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/a2dd6847-b11f-2270-4199-246838d89021.gif)

# レコードロック管理アプリ

レコードロック管理アプリテンプレートファイルからレコードロック管理アプリを作成します。
レコードロック管理アプリは、kintone 内で共有できます。
ただしゲストスペースはゲストスペース毎にレコードロック管理アプリが必要です。

※参考：[kintone ヘルプ テンプレートファイルからアプリを作成する](https://jp.cybozu.help/k/ja/user/create_app/app_csv/add_app_template_file.html)

APIトークンを生成し、レコード閲覧・レコード追加・レコード編集・レコード削除の権限を付与します。

![2023-02-19_21h10_00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/f1c2ebfe-3220-7418-7e84-223273f90ca4.png)


![2023-02-19_21h06_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/37a1445d-6da3-b157-ec3a-cc87bf5b081f.png)

# プラグインの設定
レコードロック・プラグインを設定するアプリ

- NOTE：プラグイン設定についてのメモ等
- プラグイン有効
- コメント設定
- プラグイン設定のダウンロード
- プラグイン設定のアップロード
- アプリ：　レコードロック管理アプリを指定
- APIトークン：　レコードロック管理アプリで生成したAPIトークンを指定し、テストボタンをクリック
![2023-02-19_21h07_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/0dfe669a-a7cf-e608-0d9c-795c6261c496.png)


- コメントの設定：　コメントの規定値を指定します。
![2023-02-19_21h13_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5deee62b-bc9d-1f94-6b22-ee8e815e1670.png)

# 注意事項

- 既に、JavaScript によるカスタマイズを行っているアプリの場合、処理が競合して、エラーになる場合があります。その場合は、該当するアプリでプラグインの使用を中止してください。
C#で関数のデフォルト引数にDateTime型を用いると引数のデフォルト値が指定できない点の回避


# 関数のデフォルト引数にDateTime型

C#で、関数のデフォルト引数に、DateTime型を用いた時に、
例えば、デフォルト値にDateTime.MinValueを指定すると、エラーになってしまい(下図)、引数のデフォルト値が指定できません。

```C#
public static void FuncA(DateTime t1 = DateTime.MinValue) { }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/f4114f9f-bf69-af58-4fe1-e22ba830fee0.png)

# defaultを使用する方法

:::note info
DateTime?型(null許容型)を使用できる場合は、後述の「追記: DateTime?型を使用する方法」を利用した方が、より少ないコード量で、柔軟にコードが記述できるようです。
本章の「defaultを使用する方法」は、DateTime型を使用したい場合(null許容型を使いたくない場合)の回避方法になります。
:::

default(DateTime)を使用すれば、引数のデフォルト値に指定が可能になります。(下図、エラーが消える)

```C#
public static void FuncA(DateTime t1 = default(DateTime)) { }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/86163ca2-01ea-c22d-1fdf-b2aeff01679f.png)

更に簡略化して、defaultだけでもOKのようです。

```C#
public static void FuncA(DateTime t1 = default) { }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/b88fd3eb-0fa1-1dc2-6183-f7a21cb02c2b.png)
　↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/5f201f23-4099-8d12-d9c7-0612ca88f78f.png)

DateTimeのdefault値は、DateTime.MinValueと同じ値の、0001/01/01 0:00:00になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606924/1a8b9e46-592c-6bfc-52d8-25e387e2832a.png)

## 判定

関数引数がデフォルト値かどうか、if文で判定する場合は、

```C#
public static void FuncA(DateTime t1 = default) {
    if (t1 == default)
        t1 = DateTime.Now;
    ...
}
```

若しくは、

```C#
public static void FuncA(DateTime t1 = default) {
    t1 = t1 == default ? DateTime.Now : t1;
    ...
}
```

# 追記: DateTime?型を使用する方法

:::note info
コメント欄でのご指摘をここに追記致します。
:::

DateTime?型を使用することで、デフォルト値にnullを指定することが出来るようになり、これを回避できます。(コメント欄参照)

```C#
public static void FuncA(DateTime? t1 = null) { }
```

こちらの方が、より少ないコード量で、柔軟にコードが記述できるようです。

## 判定

関数引数がデフォルト値かどうかは、if文で判定するより簡単な「??」を使用することが出来るようで、

```C#
public static void FuncA(DateTime? t1 = null) {
    t1 = t1 ?? DateTime.Now;
    ...
}
```

若しくは、

```C#
public static void FuncA(DateTime? t1 = null) {
    t1 ??= DateTime.Now;
    ...
}
```

より少ないコード量で、柔軟にコードが記述できました。


# 参考: 
https://redwarrior.hateblo.jp/entry/2016/06/27/090000

https://smdn.jp/programming/dotnet-samplecodes/basic_types/dbcfdb73097211eb8f59d142a410c52f/
【AWS】aws help でCould not find executable named "groff" エラー


# 事象
```
$ aws ecs help

Could not find executable named "groff"
```

# 解決方法

```
$ sudo ln -s /usr/bin/mandoc /usr/local/bin/groff
```

[参考](https://github.com/aws/aws-cli/issues/7400#issuecomment-1326547683)
同じパッケージの関数を認識してくれない（Go）

modelフォルダ内のファイルのパッケージ名をmodelとして、
modelフォルダ内に別ディレクトリを切ってその中のファイルのパッケージ名をmodelとしたが、modelフォルダのファイルの関数を認識してくれない（泣）

chatGPT先生に詳しく聞いたところ、
>viewer フォルダ内の view.go ファイルのパッケージ名を model にすることはできません。パッケージ名は、そのパッケージが属するディレクトリ名と一致する必要があります。したがって、 viewer フォルダ内のファイルでパッケージ名を model にすることはできず、コンパイルエラーが発生します。
>
とのことでした。。
viewerフォルダ内のファイルのパッケージ名をviewerに変えてmodelパッケージをインポートしていく必要がありそうです。
Discord.pyで新規スレッドを作成する方法

Discord.py(2.1.0)でスレッドを作成する方法について紹介します。

```console
$ python3 --version
Python 3.9.2

$ pip show discord
Version: 2.1.0 
```

# スレッドとは
スレッドとは、一般のチャンネルの中に作られる一時的な子チャンネルのようなものです。
画面右上付近のシャープマークから確認でき、親のチャンネルとは内容は分離されるため、特定のテーマについて会話したり共有したりといった目的に向いています。
作成されてから一定期間(設定可能)が経過すると自動的に非表示&書き込み不可になるため、使い所には注意が必要です。
![Discord.py(qiita).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/823378/5e391b42-1f45-59b9-42e9-bc5135cb38cf.png)

# 実装
:::note warn
当方、初心者で苦戦した結果、@bot.tree.command というデコレータを使用しています。
お使いの環境で動作するとは保証できないことご了承ください。
また記事の構成も分かりづらく申し訳ないです。
:::

### 基本コード
```python
import discord
from discord.ext import commands

intents = discord.Intesnts.default()
bot = commands.Bot(command_prefix='/',intents=intents)
 
@bot.tree.command(name="chat", description="新しいスレッドを作成します。")
async def chat(ctx):
    channel = ctx.channel
    thread = await channel.create_thread(name="ぼっち・ざ・ろっくを語りたい",reason="雷鳴を轟かせたいから")
    link = thread.mention
    await thread.send("調整中です")
    await ctx.response.send_message(f"{link} こちらで会話してください")

bot.run("your token")
```
スレッド作成のため、まずスレッドを作成したいチャンネルのIDを取得します。
```python
channel = ctx.channel
```
次に`channel.create_thread()`でスレッドを作成。
`name`には作成したいスレッドの名前、`reason`にはサーバの監査ログに表示したい作成の理由を入力してください(任意)。

```python
thread = await channel.create_thread(name="ぼっち・ざ・ろっくを語りたい",reason="3周目だから")
```
Discord上からユーザの任意の名前でスレッドを作成したい場合はこのような感じでしょうか
```python
@bot.tree.command(name="chat", description="新しいスレッドを作成します。")
async def chat(ctx,title:str):
    channel = ctx.channel
    thread = await channel.create_thread(name=title)
```
その他引数の一部

```python
|auto_archive_duration #スレッドがアーカイブされるまでの時間(分)を60/1440/4320/10080から選択する。
|type #作成するスレッドの形式を選択する。デフォルトはNoneでプライベートスレッドになる。((discord.)ChannelType.private_tread/public_threadを動作確認済み)
|slowmode_delay #スレッドの低速モードを26100秒まで設定可能。
```


### スレッドとの連携
スレッドへのメンションを取得したりスレッドへデフォルトの書き込みを投稿したりできます
```python
link = thread.mention #押したらスレッドへ移動できるリンクを取得
await ctx.response.send_message(link)
await thread.send("Hello World!") #スレッドへ予め投稿できる
```
:::note warn
Forbiddenが返された場合、Botの権限が不足している可能性があります。
今一度スレッドを作成する権限があるか確認してみてください。
:::
https://discord.com/developers/

ここまでお読みいただきありがとうございました。
もしお疲れでしたら下記がおすすめです。

[こちら](https://abema.tv/video/title/26-175 "下北沢行きたいです")

# 参考
公式リファレンス(それぞれ異なる内容です)

https://discordpy.readthedocs.io/ja/latest/api.html#discord.TextChannel.create_thread

https://discordpy.readthedocs.io/ja/latest/api.html#discord.ChannelType
「Error: error:0308010C:digital envelope routines::unsupported」のエラーが出てきた時

実行コマンドをたたいたところ、このようなエラーが出てきた
「Error: error:0308010C:digital envelope routines::unsupported」

# 原因
nodeのバージョンが新しすぎるため対応していないとのこと。
私の環境は最新版のv18.14.1だった。

**Node.jsのバージョン確認**
```
node -v
```

# 対処
voltaというバージョン管理ツールを使ってnodeのバージョンを下げた。
バージョン管理ツールはいくつかあるが、個人的に速さとOSなどの環境によらずに利用できる点でvoltaを採用している。

参考
[Voltaを利用してMacでNode.jsのインストール - homebrewは使わないで！](https://zenn.dev/protoout/articles/17-how-to-nodejs-install)

**Node.jsの安定版をインストール**
```
volta install node@16
```
ちなみにインストールしたものが、その時のバージョンとなる。

```
User ~ % volta install node@16
success: installed and set node@16.19.1 (with npm@8.19.3) as default
```
successが出ればOK。インストールめちゃ早。

voltaでは、プロジェクトのディレクトリ毎でバージョンを固定できる。

**バージョンを固定**
```
volta pin node@16.19.1
```
これでOK。
Salesforce認定アソシエイト試験の公式練習問題解説してみた-06

こんにちは

Salesforce認定アソシエイト試験の公式練習問題があるのはご存じでしょうか。
Trailheadにフラッシュカードとして何度も解ける練習問題がございます。

今回は
認定資格試験の準備: Salesforce 認定アソシエイト＞
ナビゲーションについて知識を深める

の問題2問目を解説します


今回の内容は動画でも解説しています

https://www.youtube.com/watch?v=Wl-KdQN-ki0&t=204s

<iframe width="560" height="315" src="https://www.youtube.com/embed/Wl-KdQN-ki0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

https://trailhead.salesforce.com/ja/content/learn/modules/cert-prep-salesforce-certified-associate/explore-navigation


![kanban02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/4e20628a-f836-3fe4-f6c2-ea002c8ccf02.png)


正解はBです
リストビューは
・テーブル
・Kanban
・分割ビュー
というものがあります

Kanbanについてはこのような画面になります。
なのでサポートプロセスが容易に動かすことが可能です
![Kanban.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/a0bdd2ec-30de-b1d3-7622-1295178dcaff.png)


テーブルは通常のリストビューの形なのでどの段階にケースがあるのか不適切ですね

分岐（分割ビュー？）は左にリストビューで右にレコードページの形なのでこちらはすべてのケースの状況を確認が不適切ですね
https://help.salesforce.com/s/articleView?id=release-notes.rn_lex_split_view.htm&release=226&type=5

## 他の認定アソシエイトの勉強方法について

他の勉強方法についてはこちらをご確認ください

https://qiita.com/Takaa/items/d35c9955fe8eb889d76b
contabo VPSにWireGuardをインストールしてみた(windows編)


# WireGuardをインストールする

# サーバー設定ファイルを編集する
ubuntu用の設定を削除する
SaveConfig/PostUp/PostDownはwindowsでは不要なので削除する

```conf:/etc/wireguard/wg0.conf
[Interface]
SaveConfig = true
PostUp = 
PostDown = 

```
# サーバーに設定ファイルを読み込む
## ファイルからインポートをクリック
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/c51023f0-8a06-19b9-9915-3cdd90c3c5e4.png">

## 設定ファイルを読み込むと設定が反映される
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/a6daa69a-c120-01ac-e222-88af2e5be80c.png">



# インターネット接続共有を設定する
ubuntuの時と同じでWireGuardインターフェースからインターネット接続インターフェースに転送する設定をしないと
VPN接続した端末がインターネットに接続できない

## windows > 設定
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/e956a558-5acb-536f-868e-1750e0a08ee0.png">

## ネットワークとインターネット
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/8febdbb5-a4f8-3b84-793d-e0e0cd0f472c.png">

## アダプタのオプションを変更する
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/9a0c4170-33e3-7f15-9a0f-812fed1864ee.png">

## インターネットに接続しているインターフェースを右クリックしてプロパティをクリックする
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/b4f60e56-d8dc-ced5-17db-edae062a33cb.png">
<br>
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/1ea0bae7-8dee-be49-9cb0-8f4513b9955f.png">

## ネットワークのほかのユーザーに、このコンピュータのインターネット接続をとおしての接続を許可するにチェックを入れる
<img width="400" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/eddebc49-ddc5-14ea-1703-8c8828d5bac1.png">


# 検証する
WireGuardインターフェースに付与したIPアドレスでリモートデスクトップできた
<img width="400" alt="ip1" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/0a1f5bdc-01c9-e988-585b-46d682b05c96.png">



ブラウザでcontabo VPSのアドレスが表示された
<img width="400" alt="ip2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99234/9109673b-7805-0a34-e91b-6654ecfc6f7a.png">
簡単なSQLをpandasに紐づけて学習しよう

# はじめに
本記事では、SQLと同等の動きをするpandasの記述方法をまとめます。

# 記事の方針
pandasを学習している際に、動作が似ていて、普段使用しているSQLに紐づけることで記憶に残るのではないかと考えました。
そのため、普段SQLで使用する操作をpandasではどのように記述するのかという視点でまとめていきたいと思います。


# 使用データ
本記事では以下のデータを使用します。
拾ったお金の情報を記したものとしてみてください。

以下データは、SQLではtblテーブル、pandasではtbl.csvとして操作するものとします。

| ymd | name | money |
| :--- | :--- | ---: |
| 2023/02/01 | Sato | 10000 |
| 2023/02/01 | Tanaka | 3100 |
| 2023/02/01 | Aoki | 8800 |
| 2023/02/02 | Sato | 2000 |
| 2023/02/02 | Tanaka | 9300 |
| 2023/02/02 | Aoki | 12000 |
| 2023/02/03 | Sato | 2000 |
| 2023/02/03 | Tanaka | 10000 |
| 2023/02/03 | Aoki | 10000 |
| 2023/02/04 | Sato | 5800 |
| 2023/02/04 | Tanaka | 1000 |
| 2023/02/04 | Aoki | 7500 |

# 前提
以降、下記操作を前提とします。

```python
import pandas as pd
df = pd.read_csv('tbl.csv')
```

# SELECT
## 全件
- SQL
```sql
SELECT * 
FROM tbl;
```
- pandas
```python
print(df)
```

## 列指定
- SQL
```sql
SELECT ymd, money 
FROM tbl;
```
- pandas
```python
df[['ymd', 'money']]
```

## 条件指定
### 等価条件
- SQL
```sql
SELECT * 
FROM tbl
WHERE name = 'Sato';
```
- pandas
```python
df[df['name'] == 'Sato']
```

### 比較条件
- SQL
```sql
SELECT *
FROM tbl
WHERE money < 9000;
```
- pandas
```python
df[df['money'] < 9000]
```

### 複数条件
- SQL
```sql
SELECT * 
FROM tbl
WHERE ymd <> '2023/02/03' and name <> 'Aoki';
```
- pandas
```python
df[(df['ymd'] != '2023/02/03') & (df['name'] != 'Aoki')]
```
※pandasの複数条件指定では、条件ごとに括弧で括る

# UPDATE
- SQL
```sql
UPDATE tbl
SET money = 0
WHERE ymd < '2023/02/02' or name = 'Sato';
```
- pandas
```python
df['money'] = df['money'].where((df['ymd'] < '2023/02/02') | (df['name'] == 'Sato'), 0)
```

# INSERT
- SQL
```sql
INSERT INTO tbl
VALUES('2023/02/05', 'Sato', 20000);
```
- pandas
```python
df2 = pd.Series(['2023/02/05', 'Sato', 20000], name=0, index=['ymd', 'name', 'money']) # 追加する行を作成
df = df.append(df2) # 行追加
df = df.reset_index(drop=True) # インデックス番号を連番に変更
```
※pandasでは、追加する行のインデックスを仮置きで0としている。

# DELETE
## 全削除
- SQL
```sql
DELETE tbl;
```
- pandas
```python
df = df.drop(range(len(df)))
```

## 行削除
- SQL
```sql
DELETE tbl
WHERE money < 10000;
```
- pandas
```python
df = df[df['money'] >= 10000]
```

# 最後に
よりスマートなpandasの記述方法があると感じました。
また、SQLの操作はまだまだ存在するため、随時紐づけ


# 参考文献
https://note.nkmk.me/python-pandas-reset-index/
Spresense用のBMI270とAK09918Cを搭載した9軸加速度センサーボードをテストしました

設計はずいぶん前に終わっていましたが、製造が2度ほど延期になってしまっていた
9軸加速度センサーボードがやっと届いたのでテストしました。
このボードは以前から販売していた**BMI270**を使った**6軸加速度センサーボード**に
Asahi Kasei Microdevices（AKM）の3**軸磁気センサAK09918C**を追加したボードとなります。

[メーカーのAK09918Cのページ](https://www.akm.com/jp/ja/products/electronic-compass/lineup-electronic-compass/ak09918c/)

なお**BMI270**に関してはこちらを参考にしてください
[SONY Spresense用の6軸センサーBMI270ボードをArduino IDEで動かしてみた](https://qiita.com/kaz19610303/items/2f394e3e35e1b47be3c8)

コチラが回路図になります。
![NS-SPR-0315.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99249/2badeff5-ee71-f409-c58f-fca71f2e4c95.png)

Arduino IDEで使えるAK09918Cのライブラリはこちらから入手できます。
[Seeed-Studio/Seeed_ICM20600_AK09918](https://github.com/Seeed-Studio/Seeed_ICM20600_AK09918)
ここからダウンロードしたzipファイルをArduino IDEのライブラリに追加してください。
exampleフォルダーの中の
test_magnet がAK09918のサンプルプログラムとなります。
```test_magnet.ino
#include "AK09918.h"
#include <Wire.h>

AK09918_err_type_t err;
int32_t x, y, z;
AK09918 ak09918;

void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    Wire.begin();

    err = ak09918.initialize();
    ak09918.switchMode(AK09918_POWER_DOWN);
    ak09918.switchMode(AK09918_CONTINUOUS_100HZ);
    Serial.begin(9600);

    err = ak09918.isDataReady();
    while (err != AK09918_ERR_OK) {
        Serial.println("Waiting Sensor");
        delay(100);
        err = ak09918.isDataReady();
    }
}

void loop() {
    err = ak09918.isDataReady();
    // err = AK09918_ERR_OK;
    if (err == AK09918_ERR_OK) {
        err = ak09918.isDataSkip();
        if (err == AK09918_ERR_DOR) {
            Serial.println(ak09918.strError(err));
        }
        err = ak09918.getData(&x, &y, &z);
        if (err == AK09918_ERR_OK) {
            Serial.print("X: ");
            Serial.print(x);
            Serial.print("uT");
            Serial.print("\tY: ");
            Serial.print(y);
            Serial.print("uT");
            Serial.print("\tZ: ");
            Serial.print(z);
            Serial.println("uT");
        } else {
            Serial.println(ak09918.strError(err));
        }
    } else {
        Serial.println(ak09918.strError(err));
    }
    delay(100);
}
```
このプログラムは修正無しで実行できます。
実行するとシリアルモニタに以下のようにデータが表示されます。
```
AK09918_ERR_DOR: Data skipped
X: 0uT	Y: 23uT	Z: -31uT
AK09918_ERR_DOR: Data skipped
X: 0uT	Y: 21uT	Z: -30uT
AK09918_ERR_DOR: Data skipped
X: -2uT	Y: 23uT	Z: -30uT
```
**AK09918_ERR_DOR: Data skipped** と表示されているのは
データの読み込みでオーバーフローが発生している警告なので
実際の測定には問題ないと思います。
[コチラのデータシート](https://www.akm.com/content/dam/documents/products/electronic-compass/ak09918c/ak09918c-en-datasheet.pdf)を見て頂くと
mode1 ～ mode4 が連続取り込みとなっていて各モードでサンプリング間隔が変わっています。

これでSpresense用の加速度センサーのラインアップが
[3軸センサー](https://qiita.com/kaz19610303/items/77b1ce32859e947da137)
[6軸センサー](https://qiita.com/kaz19610303/items/2f394e3e35e1b47be3c8)
この9軸センサーと揃いました>
ChatGPTとは

# 概要
ChatGPTは、OpenAIが開発した大規模な言語モデル(LLM)で、人工知能技術を用いて、自然言語による会話を行うことができます。ChatGPTは、膨大な量のテキストを学習しており、人間のような返答をすることができます。

今後のChatGPTの応用としては、ビジネス領域での活用が期待されています。例えば、ChatGPTを用いたカスタマーサポートや、自動応答システムの開発などが挙げられます。ChatGPTは、大量のデータを処理することができるため、膨大な量のお問い合わせや問題解決に対応することができます。また、ChatGPTは人間のような応答をするため、カスタマーエクスペリエンスの向上につながる可能性があります。

そして、ChatGPTは自然言語処理技術を用いた分野での応用が期待されています。例えば、ChatGPTを用いた文章の要約や翻訳、情報検索などが挙げられます。ChatGPTは、自然言語処理技術の進歩によって、ますます高度な応用が可能となっていくと予想されます。

さらに、ChatGPTの発展的な応用として、AIによる創作や芸術分野での利用も期待されています。例えば、小説の自動生成や音楽作品の自動生成、絵画の自動生成などが考えられます。

ビジネス領域でのChatGPTの応用には、カスタマーサポートや自動応答システムのほかにも、マーケティングや広告、人事などの領域での活用が期待されます。ChatGPTを用いた自然言語処理技術の進歩によって、ビジネス領域でのChatGPTの応用はますます拡大していくと予想されます。

# OpenAIのChatGPTとGoogleのBardの相違点 
ChatGPTとBERTは、両方とも自然言語処理に利用される言語モデルであり、高い性能を発揮していますが、いくつかの違いがあります。

まず、ChatGPTは、OpenAIによって開発されたモデルであり、文章生成、文章要約、言語翻訳などのタスクに優れた性能を発揮します。一方、BERTは、Googleによって開発されたモデルであり、文章の分類、情報抽出、自然言語推論などのタスクに優れた性能を持ちます。BERTは、前処理時に文書内のトークンの位置関係を明示的に考慮することで、文章の意味を理解することができます。

また、ChatGPTは、単純な自己回帰モデルを採用しているのに対し、BERTは、エンコーダ・デコーダ構造を採用しています。BERTは、Transformerというアーキテクチャを採用しており、並列処理によって高速な学習が可能です。

さらに、ChatGPTは、語彙数が非常に大きいことが特徴であり、トークンレベルでの処理を行います。一方、BERTは、WordPieceという手法を採用し、より小さいサブワードに分割することで、より効率的な学習を行うことができます。

最後に、ChatGPTは、オープンソースであり、誰でも利用することができますが、BERTは、Googleが開発した専用のモデルであり、一般には公開されていません。ただし、BERTの事前学習済みモデルは公開されており、多くの研究者や企業が活用しています。

以上のように、ChatGPTとBERTは、両方とも自然言語処理に利用される言語モデルであり、高い性能を発揮していますが、開発元や性能、処理の方法などに違いがあります。
“Dependency 'androidx.appcompat:appcompat-resources:1.6.1' requires libraries and applications thatdepend on it to compile against version 33 or later of the Android APIs."のエラーを解消させる

# エラー

Android Studioで、runさせようとすると、

```bash
Dependency 'androidx.appcompat:appcompat-resources:1.6.1' requires libraries and applications that
      depend on it to compile against version 33 or later of the
      Android APIs.
```

と表示されました。また、それと同時に

```bash
Dependency 'androidx.core:core:1.9.0' requires libraries and applications that
      depend on it to compile against version 33 or later of the
      Android APIs.
```

とも表示されました。

# 解決

Gradle Script / build.gradle(Module　の中の、

```bash
dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
...
}
```

を

```bash
dependencies {

    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'androidx.appcompat:appcompat:1.4.2'
...
}
```

に書き換えます。

Android Studio上部に、「Syncしますか」と表示されますので、Syncさせます。

これで、正常に動作しました。

# 参考

[Dependency 'androidx.appcompat:appcompat-resources:1.6.1'](https://stackoverflow.com/questions/75431649/dependency-androidx-appcompatappcompat-resources1-6-1)
MySQLを用いたレプリケーション

間違っている部分がありましたらご指摘いただけると幸いです。

## レプリケーションとは？
マスタサーバの DB をサブのスレーブサーバに配布する仕組み。
負荷が高い参照処理はスレーブサーバを利用し、データの作成などの処理をマスタサーバで行う構成をリードレプリカ構成という。

### レプリケーションの仕組み

スレーブの I/O スレッドが、マスターからのバイナリログの情報を受け取りその情報を一旦リレーログファイルに書き込む。
そして、SQLスレッドがそのリレーファイルから更新情報を読み取り、データベースに適用することで非同期的にスレーブのデータが更新される。
図で表すと下記のような流れになる。

![replication.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575294/18b50f7b-1595-dbe5-64ad-93c01561e6a7.jpeg)


### バイナリログとは？

テーブル作成操作やテーブルデータへの変更などのデータベース変更を記述する「イベント」が格納される。

バイナリログを使用する目的は下記の2つ。

- スレーブサーバーにデータ変更のレコードを提供する。
- 特定のデータリカバリー操作時に使用する。バックアップをリストアした後、バックアップが実行された後に記録されたバイナリログ内のイベントが再実行される。

### リレーログファイルとは？

スレーブのみに生成されるファイル。
ファイルの生成タイミングは、下記になる。

- `CHANGE MASTER`でマスターの変更が行われた時
- `RESET SLAVE`が行われた時

今回は、slaveの作成時に、`CHANGE MASTER`を利用して masterの設定をしたので、そのタイミングでリレーログファイルが作成されたことになる。

### 個人的にハマったポイント
MySQL5.7は、デフォルトではバイナリログが無効になっているため、`log-bin`オプションを追記して、バイナリログを有効化する必要があった。
この記述をしないまま`MASTER`指定をしたため、上手くレプリケーションができなかった。

ちなみに、MySQL8.0からはデフォルトでバイナリログが有効になっているらしい。
[MySQL :: MySQL 8\.0 リファレンスマニュアル :: 17\.1\.6\.4 バイナリロギングのオプションと変数](https://dev.mysql.com/doc/refman/8.0/ja/replication-options-binary-log.html)

#### 参考記事

[MySQL 入門　レプリケーション編 \- Qiita](https://qiita.com/Tocyuki/items/c224cef57493f536a941)
[MySQL :: MySQL 5\.6 リファレンスマニュアル :: 5\.2\.4 バイナリログ](https://dev.mysql.com/doc/refman/5.6/ja/binary-log.html)
[第 53 回　リレーログファイルについて \| gihyo\.jp](https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0053)
[第8回　MySQLのレプリケーション構成 \| gihyo\.jp](https://gihyo.jp/dev/serial/01/MySQL-tuning-scale/0008#sec2)
[MySQLレプリケーションについて \- DENET 技術ブログ](https://blog.denet.co.jp/mysqlreplication/)

下記が今回のレプリケーションを作成したレポジトリになります。
https://github.com/hayura-k/mysql-replication

実際にレプリケーションをする際の操作は後で追記します:bow:
Ruby FizzBuzz問題 応用編

# FizzBuzz応用問題

FizzBuzzという言葉遊びがあります。
1から数を数えていく際に、それが3の倍数だったら「Fizz」, 5の倍数だったら「Buzz」と言う、というものです。ただし、15の倍数の時は「FizzBuzz」と言います。

例） 1, 2, Fizz, 4, Buzz, Fizz ,,,,

このFizzBuzzを再現できるメソッドを作成してください。
ただし、いくつまでカウントするか、引数で指定できるものとします。

- ヒント
範囲を指定して繰り返しをする場合は、範囲演算子(..)を使用します。
以下のように使用します。
```
(開始する値..終了する値）
```
具体的には以下のように使用できます。
```ruby
# 1～6までの範囲を指定して、繰り返し表示
(1..6).each do |num|
  puts num
end
```

模範解答
```ruby
def fizzbuzz(max_num)
  (1..max_num).each do |num|
    if num % 15 == 0
      puts "FizzBuzz"
    elsif num % 3 == 0
      puts "Fizz"
    elsif num % 5 == 0
      puts "Buzz"
    else
      puts num
    end
  end
end

puts 'いくつまで数えますか？'
num = gets.to_i
fizzbuzz(num)
```
## 解説
範囲演算子「　..　」を使用して1から指定された数字（max_num）まで繰り返し処理を行います。
その次に、条件分岐であるif文の条件に沿って「Fizz」「Buzz」「FizzBuzz」の処理を行います。
NextJS勉強用(API呼び出し編)

# 目的
NextJSからFastAPIのAPIを呼び出す

作業ディレクトリ:
/www/js/next_api

参考にさせていただいたサイトでfetchとaxiosの両方のサンプルを見せていただいたが、axiosの方が取り扱いやすそうだったので、axiosを使用する。
SWRの勉強も兼ねて、axiosとSWRをインストールする
```
# npm install axios swr
```
## GETリクエストの試験
ColorApiGetButton.tsxでもよい
```diff:index.tsx
+import ColorApiButton from '../src/components/ColorApiButton'
+      <ColorApiButton title="0" data-color="0"/>
```

```ApiGetButton.tsx
    async function event_btn_click(e){
        e.preventDefault();
        const res = await axios.get('https://192.168.1.106/api/user/');
console.log(res);
    }
```

## POSTリクエスト
```ApiPostButton.tsx
    async function event_btn_click(e){
        e.preventDefault();
        const res = await axios.post('https://192.168.1.106/api/color/', {"color_code": color_code});
console.log(res);
    }
```

## useSWRを使用する方法
以下のサイトによるとuseSWRを使用してPOSTは推奨されないとのこと。
そのため、GETで取得する。
https://omkz.net/nextjs-swr-post/

:::note warn
`get()` `post()` と異なり、`useSWR()`は関数の外で宣言しないとエラーになる。
当初、axios.post()と同じようにevent_btn_click()の中でuseSWR()を宣言してエラーになっていたが、
「useSWRは関数の外で宣言せよ」とStackOverflowに同様の回答があったので、関数外に宣言して無事動作確認

https://stackoverflow.com/questions/68126375/invalid-hook-call-with-swr-library

:::
```ApiSWRButton.tsx
import useSWR from 'swr'
import axios from "axios";
const fetcher = url => axios.get(url).then(res => res.data);
const ColorApiButton=(props)=>{
    const { data, error } = useSWR("https://192.168.1.106/api/user/", axios);
    async function event_btn_click(e){
        e.preventDefault();
        let color_code=e.currentTarget.dataset.color;
console.log(data)
    }
```

# axiosのパラメタURLとレスポンスを省力化する。
レスポンスのデータはres.dataに設定されている。
また、URLもフルパスで記述しているが、以下のようにfetcherを宣言して、省力化する。
```tsx
const axiosBase = require('axios');
const axios = axiosBase.create({
  baseURL: 'https://192.168.1.106/api/',
  headers: {
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest'
  },
  responseType: 'json'
});
const fetcher = url => axios.get(url).then(res => res.data);
```
上記のfetcherをuseSWR()の第二引数にすることで、URLは相対パス、dataはres.dataのみで扱える.
```diff:tsx
-    const { data, error } = useSWR("https://192.168.1.106/api/user/", axios);
+    const { data, error } = useSWR("/user/", fetcher);
```

# 参考
https://qiita.com/ksh-fthr/items/2daaaf3a15c4c11956e9

https://www.sukerou.com/2019/05/axios.html

https://omkz.net/nextjs-swr-post/

https://qiita.com/aKuad/items/d213dc1f7075ab2dd69a

https://reffect.co.jp/react/next-js#i-7
【SwiftUI】StoreKit2で価格表示を通貨によって切り替える

# はじめに
現在作成しているアプリにアプリ内課金を導入していて、通貨によって表示を出しわけする方法が見つからなかったので記録しておきます。

以下の記事を参考に実装をしています。

https://qiita.com/alt_yamamoto/items/334daaa33ff12758d114

# やりかた
```swift:ContentView
import SwiftUI

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    var body: some View {
        VStack {
            let price = viewModel.product?.price ?? 0
            let priceFormatStyle = viewModel.product?.priceFormatStyle ?? Decimal.FormatStyle.Currency(code: "JPY")
            Text(price, format: priceFormatStyle)
        }
        .task {
            guard let product = try? await viewModel.fetchProducts("ここにProductIDを入力する").first else { return }
            viewModel.product = product
        }
    }
}
```

```swift:ViewModel
import StoreKit

final class ViewModel: ObservableObject {
    @Published var product: Product?

    func fetchProducts(_ productId: String) async throws -> [Product] {
        let products = try await Product.products(for: [productId])
        return products
    }
}
```

# 完成
AppStoreの言語設定によって表示が変わります(たぶん)

私は以下のようにSandboxテスターで2アカウント作成して、アカウントを切り替えることによって表示言語が切り替わることを確認しました。
![スクリーンショット 2023-02-19 19.57.53.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/ce5e597a-e2c9-dd08-a640-1922cef908fa.jpeg)

|日本|アメリカ|
|-|-|
|![スクリーンショット 2023-02-19 19.48.32.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/4a7785b4-53a0-2b97-b276-bf71f8267152.jpeg)|![スクリーンショット 2023-02-19 19.59.58.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c0382e57-6f78-9779-f35d-cc60ed937034.jpeg)|

# おわり
リリース前に課金周りは入念に確認しておきたいです。
Docker：nginxのコンテナで静的なHTMLを表示させる（バインドマウント）

# Dockerデスクトップが起動しているかを確認
起動忘れをはじめ、Macをスリープから復帰した時など、Dockerが意図せず終了している時がある。


# ローカル（コンテナ）側のHTMLファイルの領域（設置場所）を用意する

この例では、 /Users/myusername/Documents/ に学習用のディレクトリ「testdocker」を作成し、その下に「html」というディレクトリを作成、この中にindex.htmlを設置して、ホスト側からここのパスをバインドマウント（紐付け）する。


# ローカル（コンテナ）側の方でバイドマウントさせたいディレクトリやindex.htmlを作成

バインドマウントさせて表示させたいhtmlファイルを準備させておく。
vimまたはプログラミング用エディタでindex.htmlを作成しておく。


## nginxのコンテナを立ち上げる（バインドマウントを使用する場合）
nginxのリポジトリの説明には以下のコマンドが紹介あり

```
$ docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx
```

「/some/content」：　ホスト側のディレクトリ。
「/usr/share/nginx/html」：　コンテナ側のマウントポイント。上のホスト側のディレクトリを「バインドマウント」する。
「:ro」：　オプション。read only。マウント先のコンテナに読み取り専用でマウントする指示

## -v の書き方
絶対パスであることに注意。相対パスと間違いやすいらしい。

### Mac
```
-v /Users/<ユーザー名>testdocker/html:usr/share/nginx/html:ro
```

### Windows
```
-v /c/Users/<ユーザー名>testdocker/html:usr/share/nginx/html:ro
```

## 上記を日本語を交えて表現すると以下

```
docker run --name <コンテナ名> -d \
 -v<ホスト側のディレクトリ>:<コンテナ側のマウントポイント>:<オプション> \
 -p<ホスト側のポート番号>:<コンテナ側のポート番号> \
<イメージ名>
```


# バインドマウント（紐付け）する

## 先に作成したHTMLファイルの置き場所のパスをpwdコマンドで取得

もちろん、欲しいパスがどこかわからないのだから、まずは欲しいパスのディレクトリまでcdで移動してからpwdコマンドを打つ。

```
pwd
```
これで絶対パスが返ってくるのでコピーしておく。
※マウントさせたいHTMLファイルはすでに冒頭で作成済み。


## コピーした絶対パスを使い、今回の場合に合わせた具体的なコマンドを打つ

```
docker run --name first-nginx \
 -v /Users/myusername/Documents/testdocker/html:/usr/share/nginx/html:ro \
 -d -p 8080:80 nginx
```
※バックスラッシュはWarp（ターミナル）上で改行になる。


## 無事にバインドマウントできたかをブラウジングして確認

http://localhost:8080/
へアクセスして作成したHTMLが表示されるかを確認。
【備忘録】Javascriptでオブジェクトをfor in文で回す際の注意点 

# Qiita初投稿です。

## for in文の中で　オブジェクト名.プロパティ名としても上手くいかなかった
```javascript
let feature = {age :25, weight:65, name:'Tanaka'};

for(let p in feature) {
    console.log(p + '=' + feature.p);
}
/*出力結果
age=undefined
weight=undefined
name=undefined
*/
```
### 原因

feature.pとかくと、featureのpプロパティという意味になるみたい。

ここでは、呼びたいのはfeatureオブジェクトのプロパティage,weight,name

### 正しい書き方
```javascript
let feature = {age :25, weight:65, name:'Tanaka'};

for(let p in feature) {
    console.log(p + '=' + feature[p]);
}
/*出力結果
age=25
weight=65
name=Tanaka
*/
```
思ったとおりの結果になった。
【AWS Amplify】React開発でAWS Amplifyを使ってみた

# はじめに
最近、Reactで個人開発をしていてiPhoneレイアウトを実機から確認したいと思い、開発ブランチを簡単にデプロイできる方法を探していた。
結果、AWS Amplifyというサービスを見つけ、利用してみた。
とても便利だったので、それについて書く。
※なお、デプロイ対象はフロントエンドのみ。

# AWS Amplifyとは
AWSのページに行くと、「フルスタックのウェブアプリケーションとモバイルアプリケーションを数時間で構築します。開始も簡単、スケールも簡単」と書いてあった。

今回のようなケースで使うことを想定しているのかは分からないが、簡単にビルドデプロイできそうだったので試してみた。
以下リンク。
[AWS Apmlify](https://aws.amazon.com/jp/amplify/)

# 試したディレクトリ構造
以下、私の個人開発のディレクトリ構造。
AWS Amplifyでのビルド用ファイルのパスのところで少しだけハマったので念の為共有。

```:ディレクトリ構造
root/
  ┝ main/react/
          ┝　src/ 
            　┝ components/
                　┝ assets/
                　┝ components/
                　┝ index.tsx　
                　┝ App.tsx　
            　┝ node_modules 
            　┝ public/

※細かい部分は省略
```

# 構築方法（React）
使い方は公式を見るのが早いと思う。
https://aws.amazon.com/jp/getting-started/hands-on/build-react-app-amplify-graphql/module-one/

かなり簡単なので、ここでは概要のみ。
1. Githubと連携
2. フロントエンドのビルド設定の確認
3. ビルドのボタン押す

# ハマったとこ
上記の2番で少しハマった。
ビルド設定用ファイル（amplify.yml）は自動的に作成されるが、そのまま実行するとうまくいかなかった。
※デプロイまでは完了したが、ドメインにアクセスすると404エラー。
ログを見ると、「No index.html detected in deploy folder:...」というエラーが出ていたので、ビルドした後のファイル読みこめてないじゃんってなった。

以下のようにファイルを変更すると、正常に動作した。

```yml:amplify.yml
version: 1
frontend:
  phases:
    # IMPORTANT - Please verify your build commands
    preBuild:
      commands:
        - cd src/main/react
        - npm ci
    build:
      commands:
        - 'npm run build'
  artifacts:
    # IMPORTANT - Please verify your build output directory
    baseDirectory: ./src/main/react/build
    files:
      - '**/*'
  cache:
    paths: []
```

- preBuild
ビルド前に設定することを記載。
私の場合は、
1. reactプロジェクト配下まで移動
2. ライブラリのインストール
を行った。

- build
ビルド方法を記載。
私の場合は、'npm run build'を記載した。
これを叩くと、react配下にbuildフォルダが作成される。

- baseDirectory
ビルドにて生成されたファイルの場所を指定。
私の場合は、react配下のbuildを指定した。

これでビルドデプロイが成功すると、無事AWS Apmlifyから提供されるドメインにてアプリが確認できる。
iPhone実機から自分のアプリを確認できたので、よかった。
もっと使い道は多いと思うので、後におべんきょします。
[Wordpress] ブロックエディタ（Gutenberg）を開くと大量の 403 Forbidden が表示される

Wordpressで記事を編集しようとしたら大量の `403 Forbidden` が表示されました。

![スクリーンショット 2023-02-19 18.59.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513/711dec6b-441c-a802-33c1-724891f347d7.png)

しかし該当URLのみ開くと普通にJSONが表示されます。

# 解決方法

Chrome のシークレットモードでは問題ありませんでした。

該当サイトのクッキーを全て削除したらエラーは表示されなくなりました。
Autosar CountDown Calendar 2023

2023年12月1日まで、Autosar CountDown Calendar 2023を開催予定です。　

一部は、QiitaのCalendarの機能を利用し、一部はQiitaの記事の機能を利用する予定です。　

それまでに、JavaScriptかTypeScriptで何かかければ、別の方法を取るかもしれません。　


もし、QiitaのCalendarの機能が現状のままであれば、複数のカレンダーに登録した内容を、　
Qiita記事で統合することを考えるかもしれません。　

# AUTOSAR Weekly and Monthry Report, 64記事

# AUTOSRR R22-11, 330記事

# AUTOSRR R23-11 330記事

# AUTOMOTIVE Handbook Bosch 50 記事　

# C言語 100記事　
# C++ 100記事
固定グローバルIPとドメインを取得したら世界が変わった話

始めまして。やまねこです。

先日インターネットプロバイダ(ISP)を変更し，念願の固定グローバルIPアドレスを手に入れました。
ついでにドメイン取得もしたら様々なサービスをインターネットに公開できるようになったので，固定グローバルIP入手前の状態と比較して何が変わったのか，備忘録がてらまとめてみました。
# 固定グローバルIP導入前
## DDNSの管理が面倒
2019年ごろにネットワークに興味を持ち，2020年2月に手元に余ってたRaspberry piでOpenVPNサーバーを立ててみました。
そのころはISPにソフトバンク光を使っていたので，固定グローバルIPアドレスは入手できずDDNSを使用して名前解決できるようにしていました。
その後，L2TP/IPsec VPNサーバーを立てたりSoftEther VPNサーバーを立てたりして，その度にDDNSでサブドメインを追加し，サーバーにIP通知用スクリプトを導入し...と気づいたら4つくらいDDNSのサブドメインが出来上がっていました。
私が使っていたDDNSはMyDNSだったので，IP通知が30日間なかった場合はサブドメインが失効します。サーバーメンテやトラブルシューティングに時間がかかりIP通知用スクリプトが30日動作せず，IP非通知を知らせるメールがMyDNSから届き，ドメインが失効し...などDDNSの管理が煩雑になり，サーバーを容易に追加できなくなっていきました。

## DS-liteとの闘い
前述のソフトバンク光ではIPv6のオプション(高速ハイブリッド)をつけていました。高速ハイブリッドではONUの配下にBBユニットという~~謎の装置~~ホームゲートウェイ的なものを設置しなければいけません。このBBユニットが色々イケてないのですが，嬉しいことにこの装置を設置するとグローバルIPアドレスが実質固定されます。
さらに，高速ハイブリッドではIPv6とIPv4が純粋なデュアルスタックで接続されます。つまり，ポート開放が可能です。当時，ネットワークについて学び始めたばかりだったのでポート開放はできて当然と思っていました。

しかし，ソフトバンク光の契約も4年が経ち「そろそろ別のISPも検討してみようか」と思い，1年間利用料無料で有名な楽天ひかりに切り替えました。これ選択が様々な問題を引き起こします。

楽天ひかりに切り替えが完了した後，VPNサーバーに接続できなくなる問題が発生しました。「どうせNAPT周りのトラブルだろう」と思いポート変換のルールを確認しましたが特に問題ありません。不思議に思い，楽天ひかりで同様の問題が発生していないかググってみたら...

**"楽天ひかりではDS-Liteを使用しているため，ポート開放はできません"**

やってしまいました。これではISPをまた変えない限りVPNを使えません。DS-Lite環境下でどうにかサーバー公開できないか調べてみたらとある記事を見つけました。

https://asashin1.com/furutao/ipv4-and-ipv6-coexist-pppoe-and-ds-lite-rakuten-hikari/

他の記事も見ましたが
**ルーター1でDS-Lite接続 + ルーター2でPPPoE接続**
というのが解決策だということが分かりました。早速手元に余ってた個人向けルーターでこの環境を構築し事なきを得ましたが，ルーターが2つ，さらにセグメントが分かれているため管理が煩雑になってしまいました。

### YAMAHAルーターの導入
1つのルーターで2つのセグメントを管理したいのですが，そんなことができる個人向けルーターはもちろん存在せず，インターネット回線が実質2つあることになるので，WANポートが2つあるルーターが必要になります。「これはついに業務用ルーターの出番か...」と思い，秋葉原の某所でRTXシリーズを購入しました。

その頃ちょうど業務でCiscoルーターを触り始めたので，コマンドの違いはあれどセットアップは問題なく終わりました。

### 楽天ひかり 無料期間終了のお知らせ
気づいたら楽天ひかりの無料期間の終了が迫っていました。これまでDDNSとDS-Liteに振り回されてたので，今度ISPを変えるなら固定グローバルIPアドレスと願わくば非DS-Lite環境が提供されているISPがいい...と思いISPを調べてみたところ，
1. 朝日ネット
2. インターリンク

に絞られました。インターリンクの契約では回線そのものはフレッツで契約することになり，契約回りの手続きが煩雑になりそうだったので朝日ネットを契約してみました。
朝日ネットは厳密に言うとDS-Liteを提供しているのですが，先のDS-Lite + PPPoEで何とかなりそうだったのでこちらに切り替えました。

# 固定グローバルIP+ドメイン取得後
## DDNSの管理からの解放
もうサーバーを追加するたびにDDNSのサブドメインアカウント作成し，スクリプトを導入する必要がなくなりました。サーバーを追加したいときはCNAMEやAレコード，AAAAレコードを追加すればいいだけです。

## Cloudflareのサービスを利用できるようになった
これが今のところ一番大きいです。
ネームサーバーをCloudflareにすることで，WebサーバーのIPアドレスを公開することなくインターネットにWebサーバーを公開できるようになりました。しかも自動更新してくれるサーバー証明書も無料でついてきます。Let's Encryptのようにスクリプトを回す必要がないのは助かります。

さらに，Cloudflare Zero Trustで安全に自宅のSSHサーバーにブラウザで接続できるようになりました。これまでSSHサーバーを公開したいと思いつつ，セキュリティ上の観点からできずにいました。さらに，ブラウザからコマンドを入力したかったのでCloudflare Zero Trustは自分にぴったりでした。
調べてみたところ，Cloudflare Zero TrustはRDPやVNCにも対応しているようです。今までVPN接続してRDPしてたので，構築してみたいと思います。


他にもできることは増えたと思うので，いろいろ探して試してみたいと思います。

長文になりましたが，お読みいただきありがとうございました。
[Rust] Cargoコマンド集

# Cargo コマンド

## プロジェクトの作成

```bash
cargo new <project>
```

## ビルド

```bash
cargo build
```

## ビルドと実行

```bash
cargo run
```

## リリースビルド

```bash
cargo build --release
```

## ビルド可能かの確認

```bash
cargo check
```

## ドキュメントの生成

```bash
cargo doc
```

## テストの実行

```bash
cargo test
```

## パッケージのアップデート

```bash
cargo update
```

## パッケージの追加

```bash
cargo add <package>
```

## パッケージの削除

```bash
cargo rm <package>
```

## パッケージのアップデート

```bash
cargo upgrade <package>
```

## パッケージのアップデート

```bash
cargo outdated
```

## 全てのパッケージのアップデート

```bash
cargo install-update -a
```
jsでリダイレクトかけるとブラウザバックできない

下記のような記述で pageB リダイレクトをかける。
```js
// 今は pageA にいる
location.href = '/pageB/';
```
その後スマホで、とび先からブラウザバックすると
pageB → pageA → pageB のように元のページに戻されてしまう。
（この現象はスマホのみでPCでは発生しない）

ブラウザのキャッシュが原因らしく、ブラウザバックできないのはストレスなので
下記のようにsetTimeout内に処理を記述するとキャッシュから逃れられるみたいです。
```js
window.setTimeout(function(){
    location.href = '/pageB/';
}, 10);
```
GCP 認定資格向けのメモ_Cloud Funcion

Cloud funtion

Cloud Functionは　GCP環境のイベントに応じて単一目的のコードを実行するように設計されたサーバーレス　コンピューティングプラットフォームです。
Node.jsとPythonで記述されたコードを実行できます。

　・Cloud Functionはセキュリティ保護された分離された実行環境で実行されます。
　・コンピューティングリソースは飛鳥に応じてスケーリングします。スケーリングを制御する上でユーザーは何もする必要はありません、必要な和のCloud Functionのインスタンスが実行されます。
　・1個のCloud Functionの実行は、他の全てのFunctionから独立しています。Cloud Functionのライフサイクルはお互いに依存しません。
　
　用は、Cloud Functionの各呼び出しは個別のインスタンスで実行されるため、Cloud Functionはメモリや変数を共有しません。
　一般的にCloud Functionはステートレスでれければならないことを意味します。
　
Cloud Functionのユースケース
　
　　短期間のイベント駆動型の処理に適しています。
　　　・IoT　センサーまたはほかのデバイスがセンターの状態に関する情報を送信し、送信された値に応じて、Cloud Function
　　　　でアラートをトリガーする、またはセンサーからアップロードされたデータを処理することができます。
　　　　
　　　・モバイルアプリケーションは処理するためのデータをCloudに送信するもの。
　　　
　　　・非同期ワークフロー、
サッカーの試合データをPythonでデータ分析してみた

# はじめに
FootyStatsという世界のサッカーの試合データを閲覧できるサイトのAPIを用いて
Pythonでデータ分析してみました

FootyStatsのAPIのチュートリアルはこちら

https://footystats.org/api/tutorials

ご自身で分析したいリーグのデータをご用意してください

# やってみた

今回は、サンプルで用意されているAPIを使ってデータ分析していきます

・API
```
https://api.football-data-api.com/league-tables?key=example&league_id=1625
```

```python
import pandas as pd

# 上記のAPIのレスポンスの中から"league_data"を取り出す
league_data = res["data"]["league_table"]

# をチーム毎のスタッツをテーブルデータに整形
df = pd.DataFrame(league_data)
```

dfをprintすると不要な列（項目）があったので削除します
```python
df = df.drop(['cleanName', 'shortHand', 'url', 'seasonURL_overall',
            'seasonURL_home', 'seasonURL_away', 'corrections'], axis=1)
```

ゴール数の全体平均を求めてみます
```python
seasonGoals = df['seasonGoals'].mean()
print(seasonGoals) #53.6
```
53.6点でした

ちなみに、この年のプレミアリーグ1位のマンチェスターシティのゴール数は95点
平均を大きく上回っています！
「Rで学ぶデータサイエンス-入門- 」で必要なRstudioとサクラエディタの設定

# この記事について
現在、放送大学インターネット公開配信講座
(https://aoba.ouj.ac.jp/rpv/home/)
にて無料で配信されている「Rで学ぶデータサイエンス-入門-」 について、受講する際に必要なRstudioと（使っている方は）サクラエディタの設定で講座で紹介されていない部分を備忘録的にまとめます。

## サクラエディタ側
1.ＣSVファイルに対応した設定にしておく
[参考ページ]https://excel-excellent-technics.com/sakura-editor-csv-7494

## Rstudio側
1.標準の文字コードをSHIFT-JISに設定する
（デフォルトのUTF-8だとエディタ内で文字化けするのでプログラムが正確に実行されずエラーとなる）


他にも気づいたら追記します。
GCP 認定資格向けのメモ_App　Engine、Kubernetes　Engine

App　Engine
　App Engineは　アプリケーションを実行するための管理プラットフォームを提供するPassコンピューティングサービスです。
　App Engineを使う場合、アプリケーションを中心に考える必要があります。コードの実行に必要なリソースはGoogleが管理します。
　１、App Engineには、スタンダード環境とフレキシブル環境が提供れる。スタンダード環境は提供された言語ランタイムを利用しますが、フレキシブル寛容はより柔軟なコンテナ実行フラットフォームです。
　
２、App Engineフレキシブル環境はフルマネージドPaasを提供します。アプリケーションとサービスを小さなコンテナセットにパッケージ化できる場合に適したオプションです。これｋらのコンテナは負荷に応じて自動スケーリングできます。
　
３、App Engineのユースケース
　　　・基盤となるOSやストレージの構成を制御する必要がない場合
　　　・App Engine　スタンダード環境は、サポートされている言語のいずれかで記述されたアプリケーション向けに設計されています。
　　　・App Engineフレキシブル環境では、サービスに分解でき、各サービスをコンテナ化でｋるアプリケーションに適している。
　　　・App Engine　スタンダードでが、負荷がない場合は実行中のインスタンスがなくなるまでスケールダウンしますが、フレキシブル環境の場合はそうではありません。サービスは常に1個以上のコンテナが実行されてます、システム負荷がかかっていなくても、
　　　コンテナが実行されている間は課金される。
　　　

Kubernetesは、仮想マシンとベアメタルマシンのクラスタを管理する木庭でGoogleが作成したオープンソース　ツールです。次のことができます。
　　・コンテナのためにKubernetesオーケストレーション　ソフトウェアを実行するVMのクラスタを作成する。
　　・コンテナ化されたアプリケーションをクラスタにデプロイする。
　　・クラスタを管理する。
　　・自動スケーリングなどのポリシーを指定する。
　　・クラスタの状態を監視する。
Kubernetes　EngineはGCPのマネージドKubernetesサービスです。Kubernetes　Engineが次の機能を提供します。
　　・Kubernetesクラスタ内にデプロイされたCompute　Engine　VM全体の負荷分散
　　・クラスタ内にあるノード（VM)の自動スケーリング
　　・クラスタソフトウェアの必要に応じた自動スケーリング
　　・ノードのモニタリングと正常性の修復
　　・ロギング
Kubernetesクラスタには、クラスタマスターノードと複数のワーカーノードが含まれており、
それぞれマスターとノードと呼ばれます。
　　マスタノードはクラスタを管理します。Kubernetes　APIサーバー、リソースコントローラー、スケジューラんどのクラスタ
　　サービスはマスターで実行されます。
　　Kubernetesクラスタが作成されると、一定数のノードも作成されあ、これらがCompute　Engine　のVMとなります。
　　KubernetesはPodと呼ばれる単位でコンテナをデプロイします。一つの同じPODに入っているコンテナは、ストレージと
　　ネットワークリソース、IPアドレスとポートすベースを共有します。PODとはサービスを提供するための論理的なユニットです。
　　コンテナはこの単位でデプロイ及びスケーリングされます。
　　
　　Kubernetesの高可用性
　　・リソース不足しているPODをシャットダウンすること。
　　・複数の同一PODを実行できること、同一PODはReplicaと呼ばれます。
XMLのCDATAとは

## はじめに
　仕事でXMLに触れる機会があり、`<![CDATA[https://example.com/hoge/fuga]]>`という記述がありました。
このCDATAというのがどのような意味なのか気になったので調べてみることにしました。


## CDATAとは
まず[MDNドキュメント](https://developer.mozilla.org/ja/docs/Web/API/CDATASection)には、以下のような記述がありました。

> `CDATA セクション` 内では、通常の文字として XML 内に記述する場合に実体参照として記述する必要がある < や & など（※本来これらは `&lt;` や `&amp;` のように記述する必要がある）を、エスケープ無しでそのまま記述する事ができます。

ここから、CDATAセクションというものを使えば、 < や & などのエスケープを省くことができるということがわかりました。

書き方としては、`<![CDATA[  ... ]]>`という形で書くことができます。

使い方はそれでいいと思うのですが、XMLの実体参照というものが気になったので追加で調べてみました。

まず、XMLにおいて、< や & などは特殊文字であり、そのままでは文字データとして使うことができません。
そこで、「アンパサンド」＋文字列＋「セミコロン」の形式で表現する必要があるらしいです。
例えば、
`<`   →  `&lt;` 
`>`   →  `&gt;` 
`&`   →  `&amp;`
`"`   →  `&quot;`
のような形で記述する必要があります。

その書き方のことを`実体参照`というらしいです。

つまり、その実体参照という書き方をいちいちしないで済むように、`<![CDATA[  ... ]]>`という形を使うということでした。



## まとめ
- XMLには`<![CDATA[  ... ]]>`という書き方がある。
- `<![CDATA[  ... ]]>`を使うとそれで囲んだ部分は実体参照を使う必要がない。
- XMLの実体参照とは「アンパサンド」＋文字列＋「セミコロン」の形式で特殊文字を囲む形式のこと。

## 参考文献

https://developer.mozilla.org/ja/docs/Web/API/CDATASection

https://ja.wikipedia.org/wiki/Extensible_Markup_Language

https://segakuin.com/html/character-and-entity-references.html
ソフトウェアテスト講義ノオト　メモ

## テストの基礎
テストの目的
① 成果物が、その目的を達成出来ているか確認するために行われている。
②作るときめた通りの成果物かどうかを確認する
③ユーザーの要求に応えられるものかどうか確認する
③所定のレベルにあることを確認する
⑤不備や欠点の発生を防ぐ。類似の問題を防ぐ。
⑥故障や欠陥を見つける。
⑦ステークホルダーが意志決定できるように④と⑥を示す。
⑧修正したことにより発生した新たな不具合を防ぐ。
⑨仕様書に記載されていない開発しないものを確認する。
バグとデバックの違い
バグ：成果物が期待通りに動かないことを因子
デバック：その因子をなくし、期待通りに動かせるようにすること。

テスト計画の作るポイント
作成した計画書について関係者で話し合う
完璧なものを最初から作らない
実体と異なる場合、スピーディーに修正する
また、テストの目的と目標を明確にする。

テスト分析
何をテストするのかを明確にする。
テスト対象を明確にする
テスト対象のステークホルダーを明確化
要件定義書等のテストベースを読み込む
テストベースを細分化し、何をテストするのか知る
優先度とトレーサビリティを確立する

テストケース
どのようにテストをするのか抽象的、具体的にまとめること

テスト実装
テスト実行のための準備を行う
①テスト手順
②テスト環境
③テストデータ

テスト実行
テスト時は、エラーを見つけるつもりでテストに取り組むこと。

テスト完了
サマリーレポートをマネージャーに提出
資産共有

## テストタイプ
テストタイプ
機能テスト：システムは「何を」すべきを評価するテスト
例）UT,結合、システム、受け入れテスト
非機能テスト：システムが「どのように」振る舞うのかテスト
例）セキュリティ、信頼性、効率性、保守性、移植性、使用性、アクセシビリティ

## テスト技法
機能テスト
同値分割法、境界値、デシジョンテーブル、状態遷移、
ユースケース、組合わせテスト、

非機能テスト
性能テスト、負荷テスト

## 参考資料
ソフトウェアテスト講義ノオト
【SwiftUi】背景にグラデーションを設定する方法

今回は、SwiftUiで背景にグラデーションを設定する方法です。


```SwiftUIView.swift
struct SwiftUIView: View {
    let backGroundColor = LinearGradient(gradient: Gradient(colors: [Color.white, Color.black]), startPoint: .top, endPoint: .bottom)
    
    var body: some View {
        ZStack() {
            backGroundColor.edgesIgnoringSafeArea(.all)
        }
    }
}
```

`LinearGradientで（colors）グラデーションの色の設定とをします。（startPoint、endPoint）グラデーションの方向今回は上下になってます。`

![スクリーンショット 2023-02-19 18.03.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274615/94c3504b-b672-ff83-3277-aa7e56150915.png)
【Node.js】GoogleCloudStorage(GCS)に画面から受け取ったファイルをアップロードする

## 概要
Node.jsでGoogleCloudStorage(GCS)にアップロードする際に、サーバに配置されてるファイルをアップロードする実装サンプルはけっこう見つけられたのですが、
画面経由で受け取ったファイルを、GCSにアップロードする実装があまり無かったので、今回メモ書きします。

## 前提
* GCSの設定やライブラリ（google-cloud/storage）については、[Node.jsからCloud Storageを利用(入門)](https://qiita.com/NOGU626/items/1af991b1757988b6cee6)をご参照ください。ライブラリの基本的な使い方は[【GCP】Cloud StorageをNodeのSDKから実行してみる](https://zenn.dev/nananaoto/articles/c419d9afc9b03e5e3efb)を参照ください。
* アップロードの実装は[Node.js - Upload File to Google Cloud Storage](https://www.woolha.com/tutorials/node-js-upload-file-to-google-cloud-storage)の記事を参考にしました。streamを取得した後に、受け取ったファイルをbufferにしてアップロードする形になります。
* 今回の実装サンプルはNestJSを導入した状態のものですが、Node.jsのみでの環境でもほぼ同じ実装でいけると思います。

## 実装サンプル

```typescript
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
  Storage as Gcs,
  Bucket,
} from '@google-cloud/storage';

@Injectable()
export class FileUploadService {
  constructor(private configService: ConfigService) {}

  getGcsBucket(): Bucket {
    // 環境の設定ファイルから認証用のキーファイルとバケット名を取得
    const storage = new Gcs({
      keyFilename: this.configService.get<string>('GOOGLE_SECRETS_FILE'),
    });
    return storage.bucket(
      this.configService.get<string>('GOOGLE_STORAGE_BUCKET'),
    );
  }

  // 画面から受け取ったファイルとアップロード先のパスを引数に設定
  addFileToGcs(uploadFile: Express.Multer.File, uploadPath: string): Promise<string> {
    return new Promise((resolve) => {
      const bucketFile = this.getGcsBucket().file(uploadPath);
      const stream = bucketFile.createWriteStream({
        metadata: {
          contentType: uploadFile.mimetype,
        },
      });

      stream.on('error', (err) => {
        throw err;
      });

      stream.on('finish', async () => {
        bucketFile.makePublic().then(() => {
          resolve(bucketFile.publicUrl());
        });
      });
      // ファイルからBufferを取得
      stream.end(uploadFile.buffer);
    });
  }
}
```