OmniAICreator/Qiita-1.07M huggingface dataset:
Tableau Readerがお勧めできない理由

当社ではTableau Onlineを利用しており、Tableauを導入している顧客ではTableau ServerまたはTableau Onlineを利用してVizを共有していますが、やむを得ずTableauパッケージドワークブック（twbx）を配布してTableau Readerを配布していました。

現時点でも、Tableau ReaderでVizを見ていただくことはありますが、これは好ましいことではありません。[Tableau Readerを使うことの課題を述べた優れた記事](https://data-viz-lab.com/tableau-reader)がすでにありますが、これに若干の補足を加えたいと思います。

## リソース喰いで事務系のPCスペックでは足りない

企業においては、導入するPCのスペックが職種によって決められている例も少なくないと思います。たとえば、事務系ではメモリが少なく、ストレージがHDDであるというように。

しかし、たとえば64ビット版Windows 10であれば、経験上、メインメモリが6GB以上ないとディスクI/Oばかりが発生して処理が進まない「スラッシング」が頻繁に発生するようになり、ストレージがHDDだと仕事に支障が出てしまいます。Tableauのようにメインメモリを多く必要とするアプリケーションではなおさらです。

自分の体験としても、事務系のPCを使っている方から「ものすごく苦労して Tableau Reader をインストールしたのに、まともにvizを使えなかった」と直接苦言を呈されたことがあります。

## アンチウィルスソフトが重いとさらに辛い

アンチウィルスソフトの処理が重い場合は、上記の現象はさらに顕著になります。モバイル機器にさらにセキュリティソフトがインストールされていると、二重苦、三重苦という様相を呈してきます。

このような状況では、「Excelブックを配られていたときには軽快に使えていたのに、なんでこんな目に遭わないといけないのか」という不平不満がさらに募ってきてしまいます。

Tableauを推進する方としては、より望ましい方向に進めているという認識があるだけに、利用者との認識の差がしばしば余計な軋轢を生み出すことになります。

## 巨大なソフトを頻繁にアップデートする必要がある

Tableauは年4回のリリースをコミットしています。現時点の最新版は2022.1系列ですが、これは2022年の最初のリリースという意味で、2022年中に2022.4までおおむね四半期ごとにリリースされることになります。

Tableau Desktopはside-by-sideインストール、つまり複数のバージョンを同一環境にインストールできますので、Desktopユーザーは最新版をインストールし、保険として過去のバージョンを使うことが多いと思います。つまり、Desktopユーザーのバージョン追随は早いと思われます。Tableau Readerのユーザーにとっては、どのバージョンのTableau Desktopが使われたかが問題になります。2022.1で作られたファイルは、それよりも前のたとえば2021.4で開くことができません。

Tableau ReaderはTableau Desktopから一部の機能を省いたデチューン版ですが、ソフトウェアの構成はほぼ同じだと思われ、サイズもあまり変わりません。したがって、Tableau Desktopとあまり変わらない大きなソフトを、Tableau Desktopと同じ頻度でアップデートしないといけないのです。リリースごとに機能追加が行われるので、Creatorライセンスをもつ人はTableau Desktopを最新版にする動機をもっています。過去の版を使えるという保険もあります。一方、Readerを使う人はバージョンアップの必要性を感じていない/わからないのです。

## 無料のTableau Publicアプリでデータがバレる

パッケージドワークブックにデータソースを含んでいる場合、Tableau Desktopでひらけばデータが分かります。そして、vizの作りも分かります。

Creatorライセンスを持つ人はTableau Publicサイト（ASP）にvizをパブリッシュできるのであまりご存じないように思われますが、Tableau Publicという同名のデスクトップアプリがあります。Tableau Publicは、ASP、デスクトップアプリとも無料で利用することができます。

Tableau PublicアプリはTablau Desktopの機能制限版で、一番厳しい制限は保存先をTableau Publicサイト（ASP）だけにしているという点です。Tableau Publicサイトは基本的に誰でもアクセスできるので、秘密のvizをパブリッシュすることはできません。（ダウンロードは禁止できますが、検索はしにくくできても閲覧できてしまいます。）

しかし、Tableau Publicアプリでは、データが抽出されたパッケージドワークブックを開くことができます。つまり、Tableau Desktopと同様に、パッケージドワークブックのデータやvizの作りがバレてしまうことがあるのです。

## 大きなデータソースのvizは配布しにくい

数100万行のデータソースだとファイルサイズも大きくなります。実体験として、500万行の販売データをvizにしたときには、パッケージドワークブックが60-70MBにもなりました。メールでのやり取りがしにくいサイズなので、クラウドストレージを使って配布するなどしていました。

## 結論：Tableau OnlineまたはTableau Serverでvizを共有しよう

2021年1月まではViewerライセンスの最低購入数が100で、買いにくいライセンス体系でした。（Creatorはサイトごとに最低1本必要で、Explorerの最低購入数は5でした。）しかし、2021年2月からはViewerライセンスの最低購入数が撤廃され、買い求めやすくなりました。一人社長の超零細企業である当社もViewerライセンスを追加し、お客様とTableau Onlineでvizを共有しています。

viz自体を共有してブラウザで見てもらうので、上述の問題がありません。データソースまたはvizに変更があったとしても、常に最新版を共有できます。

Tableauのアカウントは自由に作成でき、必要に応じてライセンスを割り当てられるのが良い点です。

以上のように、vizの共有はTableau OnlineまたはTableau Serverがお奨めです。
C言語のポインタに関する説明

C言語のポインタに関する記載

以下のコードを解説していく。

```c:
#include <stdio.h>
#include <stdlib.h>

int main(){
	
	int* pointer;
	int data;
	
	data = 513;
	
	pointer = &data;
	
	printf("%d\n",*pointer);
	
	return 0;
}
```

実行結果は以下の通り。
```
513
```

data変数のアドレスは&dataと表現されるので、data変数のアドレスがpointer変数に代入される。
pointer変数はポインタ変数。
*pointerでpointer変数が保持しているアドレスの番地の中身を参照する。

![pointer.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615735/203ee71c-62ac-b4f9-5898-f197fd561063.jpeg)

次にポインタと配列の違いを見ていく。

```c
#include <stdio.h>
#include <stdlib.h>

int main(){
	
	int* pointer;
	int array[10];
	int i;
	
	pointer = calloc(10,sizeof(int));
	
	for(i=0;i<10;i++){
		array[i] = 0;
	}
	
	for(i=0;i<10;i++){
		printf("pointer[%d] = %d\n",i,*(pointer+i));
	}
	
	for(i=0;i<10;i++){
		printf("array[%d] = %d\n",i,array[i]);
	}
	
	return 0;
	
}
```

実行結果は以下の通り。

```
pointer[0] = 0
pointer[1] = 0
pointer[2] = 0
pointer[3] = 0
pointer[4] = 0
pointer[5] = 0
pointer[6] = 0
pointer[7] = 0
pointer[8] = 0
pointer[9] = 0
array[0] = 0
array[1] = 0
array[2] = 0
array[3] = 0
array[4] = 0
array[5] = 0
array[6] = 0
array[7] = 0
array[8] = 0
array[9] = 0

```

pointer変数はポインタでありメモリを動的確保出来る。
calloc関数で動的確保した場合、確保した領域は0で初期化される。

配列arrayを宣言した場合、初期化されないので初期化する必要がある。
typo一覧

タイポ【typo】とは、
俗語の一つで、コンピュータの入力装置の操作ミスなどによって生じた英単語の綴り間違いや誤入力、誤記、誤変換などのこと。

自分のタイポ例を挙げていきたいとおもう。


<例1>
[誤]


       public function index(Request $Request)
    {
        $data = [
            'msg'=>'これはコントローラから渡されたメッセージです。',
            'id'=>$request->id
    ];
    return view('hello.index', $data);
}

[正]

       public function index(Request $request)
    {
        $data = [
            'msg'=>'これはコントローラから渡されたメッセージです。',
            'id'=>$request->id
    ];
    return view('hello.index', $data);
}

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
public function index(Request $request)
→$requestのRを大文字にしていて、$Requestにしていた。
Rails7でdevise_token_authをインストールする

## 遭遇したエラー

```
$ bundle

Fetchings gem metadata from https://rubygems.org/...........
Resolving dependencies....
Bundler could not finde compatible versions from gem "rails":
    In Gemfile:
        rails (~> 7.0.1)

    devise_token_auth was resolved to 0.1.21.alpha2, which depends on
        rails (~> 4.1.4)
```

rubygemsに上がっているdevise_token_authがRails7をサポートしていないためインストールができない
## 解消法

Gemfileに以下のように`devise_token_auth`を追加する
```yaml
gem 'devise_token_auth', '>= 1.2.0', git: "https://github.com/lynndylanhurley/devise_token_auth"
```

githubに上がっているdevise_token_authはRails7をサポートしているので、インストール出来る
プリント スクリーンのショートカット

PrtScnボタンを使用して画面領域切り取りを開く

![20220502.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145508/f2d1ce8c-1514-480c-0bfa-3f0fc38e2bef.jpeg)

いつも忘れるのでここにメモしました。

キーボードの簡単操作の設定を変更することで、PrintScreenキーを入力して、
指定した範囲やウィンドウ領域のスクリーンショットを撮ることもできます。

1.「Windowsマーク」を右クリックして「設定」を選択。
2.設定ウィンドウが開いたら、「簡単操作」をクリック。
3.左ペインで、「キーボード」をクリック。
4.右ペインで、プリントスクリーンのショートカットの「PrtScnボタンを使用して画面領域切り取りを開く」のスイッチを「オン」に変更。

スクリーンショットツールで画面領域を撮る方法
1.［Windowsキー］＋［Shift］＋［S］キーを押し、画面上端に表示されたツールバーの左端のアイコンを選択。
2.キャプチャーしたい領域の四隅のいずれかへマウスポインターを移動。
3.マウスを左クリックしてから領域の対角側までドラッグし、マウスのボタンを離す。
はじめての Zoom Meeting SDK - 準備編

<h2>■ 前置き</h2>
Zoomでは２種類のSDKプラットフォームが用意されています。

 - <b><font color="#2D8CFF">MeetingSDK</font></b>: 通常のZoomミーティング、ウェビナーで利用されるUI/UXを含む各種機能をパッケージ化
 - <b>VideoSDK</b>: Zoomで提供する映像音声処理のコンポーネントのみをUI/UXを含まないままパッケージ化

<font color="#2D8CFF">MeetingSDK</font>ではZoomクライアントで行うミーティング、ウェビナーで利用されるUI/UXをそのままにパッケージした内容となっています。そのため、<font color="#2D8CFF">MeetingSDK</font>でも同じミーティング番号、ウェビナー番号を利用して参加接続できます。
<font color="#2D8CFF">MeetingSDK</font>では映像音声通話を個別に設計することなくZoomのUI/UXを元に短時間で組み込んでいただけるメリットがあります。

<h2>■ MeetingSDKの事前準備について</h2>
MeetingSDKを起動させるためには「SDK Key / SDK Secret」が必要となります。
「SDK Key / SDK Secret」は「<a href="https://marketplace.zoom.us/" target=_blank>https://marketplace.zoom.us/</a>」から取得できます。<br><br>

1.　既存のZoomアカウントを利用してMarketplaceにサインインします。
![スクリーンショット 2022-04-28 11.50.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/cdcf62dd-d4a8-53e7-af78-b196840b9d58.png)
2.　右上プルダウン「Build App」よりSDKの登録に進みます。
![スクリーンショット 2022-04-28 11.50.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/78f6dd2d-bdb3-0a79-1d2c-111501b652a7.png)
3.　SDKのパネル内の「Create」ボタンをクリックして進みます。
*既に生成されている場合は「View here」となります。
![スクリーンショット 2022-04-28 11.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/f1c22816-6186-059f-fb22-c7eedb2f4ad8.png)
4.　スクロールダウンして「Company Name」、「Name」、「Email address」を入力します。
*登録されたMeetingSDKに関わる重要な周知事項が発生した際にはZoomから「Email address」へ通知されますので必ず存在する値を入力ください。
![スクリーンショット 2022-04-30 11.20.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/59a8a741-3ac6-67ab-e37a-76ce5903bb43.png)
5.　各OS、SDKのバージョンごとに「Download」ボタンからサンプルの取得ができます。
![スクリーンショット 2022-04-30 11.21.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/e3e855c2-6a01-4a8d-ee40-7d0d207da176.png)
6.　「App Credentials」から「SDK Key / SDK Secret」を確認できます。
![スクリーンショット 2022-04-30 11.24.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/c9848cc6-097e-739b-6a1a-82ebd565457a.png)


<h2>■ MeetingSDKの実装について</h2>
MeetingSDKの実装に関しての手順、注意事項については「<a href="https://marketplace.zoom.us/docs/sdk/video/introduction" target=_blank>https://marketplace.zoom.us/docs/sdk/video/introduction</a>」で開示されています。

![スクリーンショット 2022-04-30 11.29.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/5d266862-46c7-0a06-51a8-9bb90bb62f1f.png)

MeetingSDKのアップデート情報は「<a href="https://marketplace.zoom.us/docs/guides/stay-up-to-date/changelog" target=_blank>https://marketplace.zoom.us/docs/guides/stay-up-to-date/changelog</a>」で確認できます。
![スクリーンショット 2022-04-30 11.27.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2123842/dd2f6ccb-c646-8078-59d8-0de5d1af0a4d.png)
music-metadata-browserがブラウザで動かなかったのが解決した

javascriptで音楽ファイルのメタデータを読み込める`music-metadata-browser`というnpmパッケージがあります。これは`music-metadata`のブラウザ版らしいです。

https://www.npmjs.com/package/music-metadata-browser

これをviteでバンドルしたWebアプリケーションで使おうとしたのですが、そのままだとブラウザで実行したときにいろいろエラーが出ました。そして今日、ついにエラーなく動作したので対応したことを書きたいと思います。

# 解決

3つのパッケージを追加しました。

```diff_json:package.json
{
  "dependencies": {
    "music-metadata-browser": "^2.5.6",
+   "buffer": "^6.0.3",
+   "events": "^3.3.0",
+   "process": "^0.11.10",
  }
}
```

`music-metadata-browser`パッケージより先に次のコードを読み込んで実行しました。

```typescript.ts
import { Buffer } from "buffer";
import process from "process";

if (typeof window.global === "undefined") {
  window.global = window;
}

if (typeof window.Buffer === "undefined") {
  window.Buffer = Buffer;
}

if (typeof window.process === "undefined") {
  window.process = process;
}
```

# ReferenceError: global is not defined

最初に出たエラーは`ReferenceError: global is not defined`でした。これはGoogle検索すればすぐに情報が出て、次のコードを追加することで対応できるそうでした。

```typescript
if (typeof window.global === "undefined") {
  window.global = window;
}
```

実際にこのコードを追加してみると、このエラーは出なくなりました。

# TypeError: Stream is undefined

最初のエラーが出なくなると、新しいエラーが出るようになりました。今度のエラーはGoogleでいろいろ検索してもうまく情報が出ませんでした。そこで、エラーが出たファイルを見ると次のようなコードを見つけました。

```javascript:node_modules/readable-stream/lib/_stream_readable.js
 42|var Stream = require('./internal/streams/stream');
...|
184|  Stream.call(this);
```

```javascript:node_modules/readable-stream/lib/internal/streams/stream.js
module.exports = require('stream');
```

私はこれを見てnodejsのモジュールの`stream`がブラウザには無いからエラーが出るのだと考え、browserifyの`stream-browserify`パッケージを追加してエイリアスを設定してみましたが、変わらずエラーは出てきました。どうやら`require('./internal/streams/stream')`では`stream.js`ではなく、`stream-browser.js`が読み込まれていたようです。

```javascript:node_modules/readable-stream/lib/internal/streams/stream-browser.js
module.exports = require('events').EventEmitter;
```

試しにbrowserifyの`events`パッケージを追加してみると、このエラーが出なくなりました。

# ReferenceError: Buffer is not defined

3つ目のエラーが出ました。このエラーはwebpackでは解決に便利なプラグインがあるみたいですが、viteの良いプラグインは見つかりませんでした。エラーが出たファイルには使用している場所はありましたが定義している場所はありませんでした。これはエラーメッセージが似ている1つ目のエラーと同じように解決できないかと考え、まず次のコードを追加しました。

```typescript
if (typeof window.Buffer === "undefined") {
  window.Buffer = Buffer;
}
```

次に`Buffer`について調べるとnodejsのライブラリに含まれているようです。browserifyの`buffer`パッケージを追加してインポートしました。

```typescript
import { Buffer } from "buffer";
```

これで3つ目のエラーも出なくなりました。

# ReferenceError: process is not defined

4つ目のエラーです。
3つ目のエラーとエラーメッセージが似ています。`process`について調べるとこれもnodejsのライブラリに含まれていました。
browserifyの`process`パッケージと次のコードを追加しました。

```typescript
import process from "process";
if (typeof window.process === "undefined") {
  window.process = process;
}
```

4つ目のエラーも出なくなりました。

# 解決

4つのエラーを解決したら音楽ファイルのメタデータの読み込みが正常に実行されるようになりました。
【SwiftUI】アラートがなんか使いずらい

# 標準アラート
```Swift
import SwiftUI

struct ContentView: View {
    @State var ShowAlert: Bool = false
    var body: some View {
        VStack {
            Button("アラート表示") {
                ShowAlert.toggle()
            }
        }
        .alert(isPresented: $ShowAlert) {
            Alert(title: Text("アラート"), message: Text("アラートが表示されました。"), dismissButton: .default(Text("OK")))
        }
    }
}
```

# カスタムアラート
```Swift
import SwiftUI

struct ContentView: View {
    @State var ShowAlert: Bool = false
    var body: some View {
        VStack {
            Button("アラート表示") {
                alert(title: "アラート", message: "アラートが表示されました。") {
                    print("アクションが実行されました")
                }
            }
        }
    }
    func alert(title: String?, message: String?, action: @escaping () -> Void) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let defaultButton = UIAlertAction(title: "OK", style: .default) { _ in
            action()
        }
        alert.addAction(defaultButton)
        UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true, completion: nil)
    }
}
```

:::note warn
**注意**
iOS15からUIApplication.shared.windowsが非推奨に変わったのでiOS15以降で使用する場合は1番下の行に警告が出ます。
以下の記事を参考に推奨コードに変更してください。

https://qiita.com/SNQ-2001/items/b14aff80c3038a98129f
:::

これを追加することでボタンの数を増やせます。
```Swift
let Action1 = UIAlertAction(title: "OK", style: .default) { _ in
    // アクション
}
let Action2 = UIAlertAction(title: "Cancel", style: .cancel) { _ in
    // アクション
}
alert.addAction(Action1)
alert.addAction(Action2)
```

```swift:style
// 青細文字
.default
// 細文字
.destructive
// 青太文字
.cancel
```

# おわり
1年前くらいに一定条件下でSwiftUI標準アラートが出ないバグに遭遇して以来、
私はずっと下を使っています。

標準の方はトリガーとアラート本体が別の場所にあってコード量が増えてくると「これなんのアラートだっけ」みたいな感じによくなってました笑(コメント書いとけって話ですが...)
プログラムの面白みは言語の外にある

# プログラミングの面白みは言語の外にある
今しがた思いついた言葉です。
思い返してください。誰しも最初に習う "Hello, World!!" を画面上に出すという、あれ。
大抵は標準ライブラリに必要な機能が備わっており、特に困ること無く入門できたと思います。

しかし組み込み、昔風に言えばマイコンプログラミングにおいては標準ライブラリを導入したところでディスプレイすら無い。いったいどこにヘローワールドすれば良いものやら。
つまりプログラミング言語をマスターしたところで、ハードウェアが対応しないことは出来ないわけです。当然ですね。

## 言語がカバーする範囲とは
プログラミング言語においてその仕様がどこまで及ぶかというのは、言語によって様々かと思います。なので自分の身近な例である C言語を思い浮かべて考えるのですが、標準ライブラリすら言語仕様の外にある、という認識です。今更言語仕様書を読もうとまで思えないからこんなことを言っているのですが、言語の範囲というのはあくまで骨格部分であり、(基本的に)自力で実装できない部分を指すものと思っています。

プログラムというのは矛盾しない範囲で思いつくことは何でもできるというのがポリシーですから、自力であらゆる部分を実装可能なんだろうと思いますが、それをやってしまうと言語を開発するのと同等なので言語を使用するという意味で実装できない範囲は存在する、という考えに基づきます。

## 言語を覚えるのは面白いか？
人それぞれなのでいろいろ有るかと思いますが、まあまあ面白いのではないでしょうか。ただ言語自体を覚えるのはそれほど面白いわけではないんじゃないかと。
使用するという範囲において覚えるべき仕様は実際僅かです。あとは応用に継ぐ応用であり、面白いのはどんな応用で何を作るか、ですよね。

## ハードウェア的視点
プログラムでハードウェアを操作する手段は自分の知る限り大きく2つです。アドレス空間を使用する方法と、何らかの通信によって操作する方法ですね。
また、通信はアドレス空間を使用する手段によって作られていることが多いので結局はアドレスを使用しないと何も出来ない、ということになるんじゃないでしょうか。
いわゆるLチカもIOポートが割当たっているアドレスにアクセスしています。

もしも、アドレス空間に何も接続されていないとしたら？せめてメモリくらいは有っても良いですが。CPUでできることは本当に計算のみになってしまうでしょう。その計算結果を取り出すのも非常に難しいです。Lチカのプログラムを書いても、LEDが無ければ何も起こらない、起こったように見えないわけです。これは全く面白くないですね。

## ソフトウェア的、ではなくライブラリ的視点
もしも一切のライブラリが無く、全てのプログラムをフルスクラッチしなくてはならなかったら、プログラムというものを面白く感じられるでしょうか。
マイコンのように小規模プログラムでかつハードウェアが動くなら結構面白いというのは経験済みです。Lチカだって周期を上げていくだけでワクワクしたものです。
そうではなく汎用のコンピュータ(PC等)を使用するに当たり、ライブラリ無しというのはおそらく苦行でしかないでしょうね。 printf を実装するためにどれだけの資料を漁らなければならないか分かったものではないです。我々のプログラミングはコンパイラやインタプリタも含め、過去の資産有りきで語られています。

言語がいかに強力でも、ライブラリ資産が潤沢でないと面白いソフトウェアを生み出すのは険しい道程になってしまいます。逆に楽しくソフトウェアを作り出すには、ライブラリにお世話にならざるをえないわけです。

## (だからなんだよ)
プログラムに面白さを求めると、それは言語を覚えることではなく、その外側に有るものをどう使うかということに行き着くのだ、というところでしょうか。

言語自体はやりたいことが出来るなら何でも良いんですよね、本当は。ガベージコレクションとかの有無も考えるべきことに思いますが、きちんとケアできればC言語だって何とかなるのです。

### そもそもなんでそんなことを考えているのか
Python を勉強しているのですが、やる気が出ません。流行っているから覚えたいのですが、びっくりするほどやる気が出ません。勉強も面白くないです。なんでこんなに面白くないんだろう。

これは目的意識の希薄さが原因です。つまり Python を覚えることが目的であり手段ではない、Python で何をしたい、というところが無いのです。仕事で必要な処理は C/C++ でたいてい済むし、Windows アプリは C# と .NET で作ってきたし、 VBA は情報が豊富なので時々ちょっと使う。 Python が必要なシチュエーションが身近にないんですよね。

最近ちょっと WEB スクレイピングの必要性があって漸くその有用性を感じたところなのですが、感じさせてくれた人のおかげで必要なくなりました。むむむ。

そして Python がどうしてこんなに人気なのか。言語仕様もあると思いますがそれ以上にライブラリの豊富さなんだろう、というのが界隈の分析ですよね。自分は何が面白くてプログラムを組んでるのかって、やっぱり機械が動くのが嬉しいのであって。
そんなことを考えてたら、プログラミング言語自体ってそういえば大して面白いもんでもないな、と思って Qiita でキーボードを乱打しました。

さて、それじゃあ必要もない WEB スクレイピングを Python でやってみようかな。そしたら少しは面白くなるかもしれませんね。
【備忘録】Sentencepieceの学習時にオーバーフローを防ぎつつspecial tokenを正しく設定する方法

公式のレポジトリにもいくつか説明箇所はあるのですが記法などが統一されていなかったりして少し調べるのに戸惑ったので、こちらのブログに書き残しておくことにしました。

なお、公式のレポジトリは以下になります。

https://github.com/google/sentencepiece

# Sentencepiceの学習について
Pythonインターフェースでのコードですが、非常にシンプルです。ググってよく出てくるサンプルだと例えば以下のような感じだと思います。

```python
import sentencepiece as spm

# モデルの学習
spm.SentencePieceTrainer.Train(
    input='corpus.txt',
    model_prefix='sentencepiece',
    vocab_size=32000,
    character_coverage=0.9995
)

# こんな感じに書くこともできる
spm.SentencePieceTrainer.Train('--input=corpus.txt --model_prefix=sentencepiece --vocab_size=32000 character_coverage=0.9995')
```

# オーバーフローを防ぐ
（多分、、、）テキストファイルを一気にロードするため、大きなコーパスを学習させる際はCPUのコア数を増やす必要があります。その際以下の引数を追加することで、オーバーフローを防ぐことが可能です。

```python
import sentencepiece as spm


spm.SentencePieceTrainer.Train(
    input='corpus.txt',
    model_prefix='sentencepiece',
    vocab_size=32000,
    character_coverage=0.9995,
    train_extremely_large_corpus=True.    # 追加
)
```

# special tokenの設定
上のコードだと`<unk>`（Unknown）, `
`（BOS）, `
`（EOS）の3つのトークンは設定されているのですが、その他のspecial tokenは設定されていません。これだといざ学習されたtokenizerを使用とした際、何かと不都合が生じる可能性があります。以下のようにすることで、この問題は解消できました。

```python
import sentencepiece as spm


spm.SentencePieceTrainer.Train(
    input='corpus.txt',
    model_prefix='sentencepiece',
    vocab_size=32000,
    pad_id=3,
    pad_piece='[PAD]',
    user_defined_symbols=['[PAD]', '[CLS]', '[SEP]', '[MASK]']
)
```

試しに生成されたtokenizerをtransformerのインターフェースでインスタンス化してみたところ以下のように正しく設定されていることが確認できます。

```python
from transformers import DebertaV2Tokenizer


tokenizer = DebertaV2Tokenizer(
    vocab_file=sentencepiece.model,
    bos_token='
',
    eos_token='
',
    unk_token='<unk>',
    pad_token='[PAD]',
    cls_token='[CLS]',
    sep_token='[SEP]',
    mask_token='[MASK]',
    extra_ids=0,
    additional_special_tokens=(),
    do_lower_case=True
)

unk_id = tokenizer.unk_token_id
bos_id = tokenizer.bos_token_id
eos_id = tokenizer.eos_token_id
pad_id = tokenizer.pad_token_id
cls_id = tokenizer.cls_token_id
sep_id = tokenizer.sep_token_id
mask_id = tokenizer.mask_token_id
print(unk_id, tokenizer_myself3.convert_ids_to_tokens(unk_id))
print(bos_id, tokenizer_myself3.convert_ids_to_tokens(bos_id))
print(eos_id, tokenizer_myself3.convert_ids_to_tokens(eos_id))
print(pad_id, tokenizer_myself3.convert_ids_to_tokens(pad_id))
print(cls_id, tokenizer_myself3.convert_ids_to_tokens(cls_id))
print(sep_id, tokenizer_myself3.convert_ids_to_tokens(sep_id))
print(mask_id, tokenizer_myself3.convert_ids_to_tokens(mask_id))
```

結果

```
0 <unk>
1 

2 

3 [PAD]
4 [CLS]
5 [SEP]
6 [MASK]
```
スクラム開発について

職場でスクラム開発を導入することになったのでまとめます！

# スクラム開発とは
アジャイル開発ではチームメンバーや関係者からフィードバックを定期的に受けながら柔軟に計画を調整する。
スクラム開発はアジャイル開発手法の１つ。

# スクラム開発の特徴
#### 目的
- 正確な工数の見積もり
- 素早い問題検知
- 生産性の向上

#### 特徴
- 透明性： 　現状や問題を明確にする
- 検査：　　進め方に問題がないか確認する
- 適応：　　やり方に問題があれば変える
- 短期間：　タイムボックスに区切って作業をする
- 効率化：　価値・リスク・必要性を基準にして並べ替えてその順にプロダクトを作る

#### 必要なこと
スクラム開発は不確定要素の多い複雑な問題を扱うのに適した手法で、最低限のルールで構成される。
ソースコードの記述ルールやテスト項目や実施方法などのスクラム開発で決められていない部分には、自分たちの状況を踏まえて取り組んでいく必要がある。

# スクラム開発のステップ
1. プロダクトバックログの作成
1. スプリントバックログの作成
1. スプリント
1. スプリントレビュー
1. スプリントレトロスペクティブ

### 1. プロダクトバックログの作成
ユーザー（顧客）が求めるプロダクトに対しての要求リストのこと。
「タスクを書いたカード」のようなものを順位付けしておく。
プロダクトが完成するまでの間、プロダクトバックログは管理され続ける。

### 2. スプリントバックログの作成
プロダクトバックログを元に実際の作業を抽出し、順位付けしたもの。
チームメンバー全員で共有・確認し、開発チームが工数を見積もり、最終的にどこまで開発するか決定する。

### 3. スプリント
- 要求確認
- 設計実装
- テスト

これらの工程を繰り返す。
#### デイリースクラム
毎朝行う報告会
「昨日したこと」「本日すること」「障害となっていること」
これらを報告し、チームでどうするかを決定する。
開発チームで障害を取り除くことができない場合、スクラムマスターとその障害をどのように扱うか判断する。

### 4. スプリントレビュー
スプリント完了時にプロダクトの確認をする。
プロダクトオーナーを含め様々な人からレビューを頂くことでプロダクトが計画時の条件を満たしているか確認するのが目的。
最終的にはプロダクトの品質向上について話し合う場。

### 5. スプリントレトロスペクティブ
スプリント内で発生した課題やどうやって解決したかを振り返る。
今後のスプリントを改善できるような意見を出し合う場。

# 役割分担
1. プロダクトオーナー
1. スクラムマスター
1. 開発チーム

### 1. プロダクトオーナー
最終的なプロダクトについて責任を持つ立場。
プロダクトに必要な機能を定義し、タスクの順位付けを行う。

### 2. スクラムマスター
スクラムがうまく回るように全体調整する役割。
開発チームで出た課題を解決するために、外部と交渉を行なったり、メンバーと相談したりして課題解決を進める。

### 3. 開発チーム
「設計者」「プログラマー」「デザイナー」といった区分けはせず、機能を横断的に作れる能力を持った人たちが、プロダクトを中心において開発を進めていく。

# 参考
https://ssaits.jp/promapedia/method/scram.html

https://goworkship.com/magazine/scrum-development/
ParseErrorsyntax error, unexpected ‘Route’ (T_STRING) エラー(解決)



//文法エラー
Route::get('hello', 'function(){
    return view('hello.index');
});


⬇️

Route::get('hello', function(){
    return view('hello.index');
});

functionの前にアポストロフィ(')があり、エラー。




https://prograshi.com/framework/laravel/parse-syntax-error/

→こちらの記事を参考にさせて頂きました。
VPN利用時にWSLから外部SSLへの接続が確立できない場合の対処

## 前提：構成
* Windows10
* WSL2 カーネルバージョン 5.10.102.1
* Ubuntu 20.04.4 LTS
* docker-ce 20.10.14　※docker desktopは未使用


## 事象
WSL上で稀に一部URLへアクセスできない場合が発生する
```
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg
 curl: (35) OpenSSL SSL_connect: 接続が相手からリセットされました in connection to download.docker.com:443
```

## 原因
自分のとこのVPNに接続した場合に [MTU](https://www.nic.ad.jp/ja/basics/terms/mtu.html) の値が
小さくなっていたが、WSL側にそれが伝搬されていないのが原因っぽい
```
c:\>netsh.exe interface ipv4 show subinterfaces

   MTU  MediaSenseState  受信バイト   送信バイト  インターフェイス
------  ---------------  -----------  ----------  -----------------
  1200                1  190906226   24442368  VPN
  1500                1  1559306163  204611796  Wi-Fi
  1500                1    7174232  304467918  vEthernet (WSL)
```

## 対応
WSLのターミナル起動時に、MTU値を取得して動的にWSL側のI/Fに設定するようにした
```bash:/etc/profile.d/network-env.sh
#!/usr/bin/env bash
CMDNETSH="/mnt/c/WINDOWS/system32/netsh.exe"
MTU=$($CMDNETSH interface ipv4 show subinterfaces | grep VPN | head -1 | awk '{print $1}')
if [ "$MTU" == "" ]; then # VPN接続していない場合
  MTU=$($CMDNETSH interface ipv4 show subinterfaces | grep "vEthernet (WSL)" | head -1 | awk '{print $1}')
fi
export MTU # ※後述
if [ "${MTU}" != "" ]; then # eth0のMTU値を変更する
  sudo ip link set eth0 mtu $MTU
fi
```

## 追加対応
この設定をdockerのコンテナにも適用するため、docker-compose.ymlに下記を追加
この設定の為、先のshell(network-env.sh)内で `export MTU` してる
```yaml
networks:
  default:
    driver_opts:
      com.docker.network.driver.mtu: ${MTU}
```


## 参考にしたサイト
https://ydkk.hateblo.jp/entry/2020/12/06/150914
https://github.com/microsoft/WSL/issues/4517#issuecomment-628701283
【SwiftUI】SPMを利用したマルチモジュールにおけるモジュール間の画面遷移

マルチモジュールでのモジュール間の画面遷移について調べていたところ、以下の記事が良さそうだったので、そちらを参考に実装してみました.
簡単な説明とサンプルコードを記載します.

https://techlife.cookpad.com/entry/2021/06/16/110000

### 前提
- FeatureA, FeatureBというモジュールを作成
- 循環参照を避けるため、それぞれ依存関係にない（お互いの存在を知らない) 状態

### やりたいこと
FeatureAのView から FeatureBのViewへと画面遷移したい

ただ、FeatureAは、FeatureBの存在を知らないのでそのままFeatureBをimportして画面遷移させることはできないので、DIを利用して画面遷移を実現


### 実装

https://github.com/daigou26/MultimoduleSample

#### ViewDiscriptorの作成
どのモジュールからでもアクセスできる CoreModules内に作成.
ここに遷移先の情報を列挙していきます.
必要なパラメータ、返ってくる型などの情報をもつ.


#### ViewResoverの作成
DIコンテナの役割をする Environment の protocol.
CoreModules内に作成し、画面遷移する際に FeatureModules内で利用.

#### SampleEnvironmentの作成
DIコンテナの役割.
FeatureModule内で、EnvironmentにDescriptorを渡し、遷移先のViewを要求.



#### 画面遷移
画面遷移は以下のように実装します.

```
public struct FeatureA: View {
    var environment: ViewResolver
    public init(resolver: ViewResolver) { self.environment = resolver }
    
    public var body: some View {
        NavigationView {
            VStack {
                Text("FeatureA")
                NavigationLink("Next", destination: environment.resolveConcrete(ViewDescriptor.FeatureBDescriptor(text: "FeatureB")))
            }
        }
    }
}
```

### 参考
https://qiita.com/darquro/items/66aa0b47983510022c3c
[ハンズオン]AWS Docker入門編

先日、Dockerを触れる機会があったので忘れないようシェアしたいと思います。

# Dockerとは？
初心者の方向けにAWS公式のDockerに関する説明をリンク貼付しました！
https://aws.amazon.com/jp/docker/

[ポイント]
Dockerは
・コンテナ型
・ソフトウェアプラットフォーム
・すばやくビルド、出荷、実行＝テスト環境の構築に向いている

とはいうものの、ものは試しというわけで早速やってみましょう！

今回は練習なのでAWS EC2環境で体験してましょう！
:::message
Amazon Linux2でのインストールを試みます。
:::

Docker インストール
```js
yum install -y docker
```

起動・自動設定
```js
systemctl start docker.service
systemctl enable docker.service
```

念の為起動しているか確認してみましょう。
```js
ps -efH | grep docker 
```

これで起動の確認が取れました！
```js
root      3759     1  0 08:57 ?        00:00:05   /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --default-ulimit nofile=32768:65536
root      4769  3759  0 09:03 ?        00:00:00     /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 80 -container-ip 172.17.0.2 -container-port 80
root      4774  3759  0 09:03 ?        00:00:00     /usr/bin/docker-proxy -proto tcp -host-ip :: -host-port 80 -container-ip 172.17.0.2 -container-port 80
```

今回はnginxをコンテナで動かそうと思います！
まず、作業ディレクトリを作成します。
```js
mkdir /work
cd /work
```
次にhtmlファイルを作成し、適当な文言を入力しておきます。
```js
vim index.html

#以下ファイルの中に文言入力
---------------
Hello World
--------------
```
今回「Dockerfile」というものを使ってnginxのイメージを作成しようと思います。
https://www.designet.co.jp/faq/term/?id=RG9ja2VyZmlsZQ

以下をファイルに入力してください。(詳細は後ほど説明)
```js
vim Dockerfile

#以下ファイル内に入力
----------
FROM nginx
EXPOSE 80
#Docker上のディレクトリにコピー
COPY index.html /usr/share/nginx/html
#OS特有の実行させたいコマンド
CMD nginx -g "daemon off;"
----------
```


- FROM nginx
ベースとするDockerイメージを指定します。
- EXPOSE 80
80番ポートの開放を意味します。
- COPY index.html /usr/share/nginx/html
EC2内にあるファイルindex.htmlをコンテナ内の所定のディレクトリにコピーします。
- CMD nginx -g "daemon off;"
コンテナのデフォルトの実行コマンドを定義します。
nginx を起動する際に daemon off; をつけることでフォアグランドでの起動が可能になります。

では作成したイメージを立ち上げてみましょう！
```js
docker build . -t my_nginx
```

できたイメージの確認もついでにやってみます。
```js
docker images

REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
my_nginx     latest    3ce060349dc5   28 minutes ago   133MB
```

Dockerfileで作成したイメージの結果が出ていますね！

では最後にコンテナの作成です！

```js
docker run -d -p 80:80 --name web my_nginx
```

コンテナが作成されたか確認してみましょう。
```js
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                               NAMES
116c37fb82c4   my_nginx   "/docker-entrypoint.…"   29 minutes ago   Up 29 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp   web
```

最後にブラウザで自分のグローバルIP/index.htmlでHello Worldと出れば成功です！
![](https://storage.googleapis.com/zenn-user-upload/ad445a0ee21b6be1fa19cb45.png)

＊グローバルIPのみだと以下のように表示されます。
![](https://storage.googleapis.com/zenn-user-upload/7db6eac64139f53dd497b68d.png)


今回はDockerの起動とDockerfileからのnginxを作ってみました。
自身もDockerは勉強始めたばかりなのでちょくちょく更新していこうと思います！
王者Apacheのシェア越え！ 初心者向けNGINXのインストールについて

最近yahoo newsにて以下のような記事を見かけました。
https://news.yahoo.co.jp/articles/4459d66372eaa5451b0d10e3d9d3883ca1ec622a
なんと絶対王者ApacheのシェアをNGINXが抜いたらしいです！
今後もますますシェアを伸ばすであろうNGINX、その波に乗るべく今回は初心者でも簡単にNGINXがインストールできる方法を記事にしました！
:::message
今回はAmazon Linux2でのインストールを試みます。
:::

EC2起動

yumのupdate
sshでのEC2ログイン後、rootユーザにて以下yumのアップデート
をおこないます。
```js
yum update -y
```
nginxのインストール
```js
amazon-linux-extras install nginx1 -y
```
以下nginxが起動する
```js
java-openjdk11           available    [ =11  =stable ]
 34  lynis                    available    [ =stable ]
 35  kernel-ng                available    [ =stable ]
 36  BCC                      available    [ =0.x  =stable ]
 37  mono                     available    [ =5.x  =stable ]
 38  nginx1=latest            enabled      [ =stable ]
 39  ruby2.6                  available    [ =2.6  =stable ]
 40  mock                     available    [ =stable ]
 41  postgresql11             available    [ =11  =stable ]
 42  php7.4                   available    [ =stable ]
 43  livepatch                available    [ =stable ]
 44  python3.8                available    [ =stable ]
 45  haproxy2                 available    [ =stable ]
 46  collectd                 available    [ =stable ]
```

バージョン確認
```js
nginx -v
```
 システム起動・自動設定
 ```js
 systemctl start nginx.service
 ```
 ```js
 systemctl enable nginx.service
```
 
 システム確認
```js
systemctl status nginx.service
```
プロセスが正しく起動されているか念の為確認。
```js
ps -efH | grep nginx.service
```


グローバルIPをブラウザに入力し以下の画面が出てくれば成功
![](https://storage.googleapis.com/zenn-user-upload/f7a42c54725e53e9ca9ed785.png)



以上、超初心者向けにNGINXのインストール方法をご紹介いたしました！
Route53で自分のドメイン作ってみない？😏

先日、個人的にAWSのRoute53でドメインを登録する機会があったので備忘録として載せておきます！
使用方法は未経験の方でもすごい簡単で値段も安いものもあるので是非使ってみてください！
では行ってみよう！


# Route53とは？
https://aws.amazon.com/jp/route53/

[ポイント]
Route53は
・AWSサービスにおけるDNSサーバの役割
・可用性、柔軟性、信頼性において抜群の安定感
・UIがシンプル！(個人感想)

DNSサーバを一から構築した人ならいかに便利なサービスか理解してもらえると思います！
まぁ、百聞は一見にしかず、早速コンソールを見てみましょう🐶



**AWSでRoute53と検索すれば以下のような画面が現れます。**
![](https://storage.googleapis.com/zenn-user-upload/12c816b5a85a216614a1fda6.png)

赤枠が私が現在保有しているドメインを表しています。
今日はここを作っていきます。

**まずは左のペインから「登録済みドメイン」をクリックして「ドメインの登録」**
![](https://storage.googleapis.com/zenn-user-upload/e4a69a0aba86730bdd4bdfca.png)

**取得したい名前のドメインを入力して「チェックボタン」をクリック**
これで使われていなドメイン名であれば取得ができます！

![](https://storage.googleapis.com/zenn-user-upload/6fb32ead538619ee2e9a23dd.png)

興味本意で色々試したんですが、「.com」はやっぱり人気のトップレベルドメインなので取得できない名前が多かったですね...
【ServiceNow】【Sandiego】日本語Pluginの設定

ServiceNowにて日本語化を行うためにはPluginを導入する必要があります。
今回はその方法を以下に手順化しておきます。

バージョン：Sandiego
UI：旧UI

手順1:ServiceNowへログイン後、アプリケーションナビゲータに"plugin"と入力します。
手順2:メニューより"plugins"を選択します。
手順3:検索フィルタに"I18N: Japanese Translations"と入力します。
※Japaneseのみでも可

手順1~3は以下の画像を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399414/0bd14c63-d2b1-5108-84d6-998a492b0d71.png)

手順4:検索結果、表示されたPluginにおいて"Install"をクリックしてください。
手順5:"Activate Plugin"が開くので、Acrivateをクリックしてください。

手順4~5は以下の画像を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399414/bb48076c-e9f2-05c5-a2bd-4f2ec39ba6a1.png)

手順6:Pluginの進捗が表示されます。待ちましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399414/3895d320-403f-f986-e9ad-2d536c470855.png)

手順7:日本語化してみましょう。ユーザアカウント横のギアマークより、システムの設定を表示します。
　　　　　言語のリスト欄に日本語が追加されています。選択すればメニューの日本語化が可能です。

手順7は以下の画像を参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399414/903c03ce-c722-4e77-6afe-e47b9871c1d5.png)
SingleChildScrollViewにListViewのウィジェットを含ませる


## 概要
掲題の件の実現に詰まり、下記のようなエラーと遭遇した。

```console
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during performResize():
Vertical viewport was given unbounded height.
Viewports expand in the scrolling direction to fill their container. In this case, a vertical
viewport was given an unlimited amount of vertical space in which to expand. This situation
typically happens when a scrollable widget is nested inside another scrollable widget.
If this widget is always nested in a scrollable widget there is no need to use a viewport because
there will always be enough vertical space for the children. In this case, consider using a Column
instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
the height of the viewport to the sum of the heights of its children.
```
と怒られる。。。

## 原因
SingleChildScrollViewとListView共に表示スペースを最大限まで取得しようとして、いたちごっこになっているせいなのかな？

## 解決策
下記のように[shrinkWrap](https://api.flutter.dev/flutter/widgets/ScrollView/shrinkWrap.html)と[physics](https://api.flutter.dev/flutter/widgets/ScrollView/physics.html)を追加することで解決した。

```dart
        ListView.builder(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
```

### shrinkWrap
スクロールビューの範囲を、表示中のコンテンツによって決定するかどうかを指定するプロパティ。デフォルトはfalseであり、この場合はスクロールビューはスクロール軸において許容される最大サイズまで拡大される。

trueを明示することで、ListViewは必要最低限の表示スペースを取得する。

### physics
スクロール方法を設定できるプロパティ。
NeverScrollableScrollPhysicsを設定するとスクロールができないようになる。
AWS利用料をDailyでメール通知するlambda関数

# 内容
AWS利用料をDailyでメール通知するlambda関数です。

# 設定
* SNS Topic作成
* Lambda用のロール作成
CostExploereへアクセス（ce:GetCostAndUsage）及びSNSのパブリッシュ（sns:Publish）権限
* Lambda関数作成
* EventBridgeからスケジュール設定

# コード

```python
import boto3
import datetime

# SNS Topic
topicArn = 'arn:aws:sns:ap-northeast-1:"account_id":"topic_name"'
# 1 days ago
date1 = (datetime.datetime.now() - datetime.timedelta(days = 1)).strftime('%Y-%m-%d')
# 2 days ago
date2 = (datetime.datetime.now() - datetime.timedelta(days = 2)).strftime('%Y-%m-%d')

def lambda_handler(event, context):
### Get Daily Cost from Cost Exploere ###
    date2 = (datetime.datetime.now() - datetime.timedelta(days = 2)).strftime('%Y-%m-%d')

    client = boto3.client('ce', region_name = 'ap-northeast-1')
    response = client.get_cost_and_usage(
        TimePeriod = {
            'Start': date2,
            'End': date1
        },
        Granularity = 'DAILY',
        Metrics = [
            'UnblendedCost',
        ]
    )

    for ResultsByTime in response['ResultsByTime']:
        cost_start = ResultsByTime['TimePeriod']['Start']
        cost_end = ResultsByTime['TimePeriod']['End']
        cost_amount = ResultsByTime['Total']['UnblendedCost']['Amount']
        cost_unit = ResultsByTime['Total']['UnblendedCost']['Unit']

### Send E-mail with SNS ###        
    client2 = boto3.client('sns')
    response2 = client2.publish(
        TopicArn = topicArn,
        Message = "Cost Start Day:" + cost_start + '\n' + "Cost End Day:" + cost_end+ '\n' + "Cost:" + cost_amount + '\n' + "Unit:" + cost_unit,
        Subject = "AWS Daily Cost Report of Test Environment",
        )
    return response2
```
Next.jsで戻るリンク

以下のような感じで戻るリンクを実装しようとした所、

```React
import { useRouter } from 'next/router'

<Link href='#' onClick={router.back()} >
  戻る
</Link>
```

エラーが発生
``
No Router Instance
``
next/routerがサーバー側(SSR、SSG)実行時に参照できないことが原因

## 解決策

* コールバック定義とすることでリンクのクリック(=クライアント側)でのみ評価される
* Linkのhrefは必須なので"#"を指定


```React
import { useRouter } from 'next/router'

<Link href="#">
  <a onClick={() => { router.back() }} >
    戻る
  </a>
</Link>
```

## 参考

[Next.JS Error: No router instance found](https://www.marcusoft.net/2021/05/nextjs-error-no-router-instance-found.html)
状態遷移表から状態遷移テストのテストケースを作成するツール

# はじめに
状態遷移表（状態×イベント）からテストケースを作成するツールを作りましたので、その紹介です。といっても、状態遷移表をテストケースに変換するアルゴリズム的な部分は作っておらず、そこはベリサーブ社の[テスト技法ツールGIHOZ](https://www.veriserve.co.jp/gihoz)のWeb APIを利用しています。GIHOZは2022年5月時点では、状態遷移図からテストケースを生成できますが、状態遷移表からテストケースを生成することはできないため、自分でツールを作成してみました。

# ツールの概要
状態遷移表（状態×イベント）のCSVファイルを読み込んで、状態遷移テストのテストケースを作成するツールです。テストケースの網羅基準として、0スイッチ～3スイッチを選ぶことができます。GIHOZが対応している「無効遷移を含めた全遷移テストケース」を選ぶこともできます。

# ソースコード・動作確認環境
ソースコードは[GitHubのリポジトリ](https://github.com/ktanizaki/gihoz-utilities)で公開しています。
以下の環境で動作することを確認しました。

* OS: Windows10
* 言語: Python 3.9.5

# 使い方

まずPython3をインストールし、次にpandasというライブラリをインストールします。pandasはpipコマンドが使える方は以下のコマンドでインストールできます。Anacondaなどをご利用の方はご自分でコマンドを調べてください。

```
$ pip install pandas
```

ここまでで環境の準備は完了です。入力データとして下図の形の状態遷移表をCSV形式で作成します。下図はストップウォッチの状態遷移を想定して作成した例です。Excelなどで作成してから、CSV形式で保存すると楽に作れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/ce1ab114-73cc-cd6a-c9e7-c2d64cada38e.png)


上記のGitHubのリポジトリからソースコードをクローンするかダウンロードして、`utilities/generate_testcases_from_state_transition_table.py`の中の`YOUR_PERSONAL_ACCESS_TOKEN `という文字列を、GIHOZで発行したAPIトークンの文字列に置き換えます。APIトークンの発行の手順は[GIHOZのヘルプページ](https://gihoz.com/help/api/how_to_use_api)に記載があります。
あとは、コマンドプロンプトやPowerShellでソースコードの保存先を開いて、以下のコマンドを打てば実行できます。

```
$ python generate_testcases_from_state_transition_table.py <CSV file name> [desired test case]
```

`<CSV file name>`には、状態遷移表のCSVファイルの名前を指定します。
`[desired test case]`には`0_switch` `1_switch` `2_switch` `3_switch` `all_transition`のいずれかの文字列を指定します。何も指定しなかった場合は、`all_transition`を指定したものとしてテストケースを作成します。

実行に成功すると`1_switch_test_cases.csv`といった名前のファイルが生成されます。ファイル名の`1_switch`の部分は`[desired test case]`で指定した文字列となります。

ファイルを開くと下図のようになっています。ヘッダはありませんが左の列から順に、状態名・イベント名・状態名・イベント名・・・となっており、1行が状態遷移テストの1個のテストケースになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188690/d2b87b06-6daf-2c0d-4207-bda37cdf689b.png)

# おわりに
状態遷移表（状態×イベント）からテストケースを作成するツールのご紹介でした。GIHOZのWeb APIを利用することで、テストケースの生成アルゴリズムを自作する必要がなく、短時間でツールを作ることができました。状態遷移テストでテストケースを作成する際の一助になればと思います。
OAuth2.0 フロー図を書いてみた Part1

# 目的

・VSCodeでPlantUmlを使った図を書く練習。
・最近OAuth2.0を使ったシステムに携わっているのでそのあたりのフロー図を書いてみた。

## 環境
・Visual Studio Code にPlantumlの拡張機能を追加して使用。

## 詳細

※参考にさせていただいた記事、書籍
https://pdf.plantuml.net/PlantUML_Language_Reference_Guide_ja.pdf
https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
https://www.amazon.co.jp/dp/B07L5M7DXS/

![Aauthフロー1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/834885/30d4e87b-b920-04ae-3253-6ac5bb86c887.png)

```markdown:Aauthフロー1.pu
@startuml

' 見た目の設定
skinparam sequence {
    ArrowColor Black
    ActorBorderColor Black
    LifeLineBorderColor Black    
    ParticipantBorderColor Black
    ParticipantFontSize 9
'    ActorBackgroundColor aqua
'    ActorFontColor DeepSkyBlue
    ActorFontSize 10
'    ActorFontName Aapex
}


actor "ユーザ\n(画面表示/入力)" as User
participant "クライアント\n(アプリ/ブラウザ等)" as Client #87cefa  
box "認可サーバ" #White
participant "認可サーバ\n認可エンドポイント" as AuthserverNinka #99FF99
participant "認可サーバ\nトークンエンドポイント" as AuthserverToken #99FF99
end box
participant "認証システム" as AuthSystem #99FF99
participant "APIサーバ" as APIserver #99FF99
database "保護リソース" as DataBase  #99FF99

title Oauth フロー図１

autonumber 1 1 "(00)"
User -> Client: 機能使いたい
Client -> User: 別システムと連携必要なので\nリダイレクトします
User -> AuthserverNinka: リダイレクト
AuthserverNinka --> User: 利用許可しますか？\nID/PW入力して
User -> AuthserverNinka: 許可します\nID/PW 入力
AuthserverNinka -> AuthSystem: ID/PW 認証
AuthSystem --> AuthserverNinka: 認証OK
AuthserverNinka --> Client: 許可されたので\n認可コード発行します
Client -> AuthserverToken: 認可コード渡すので\nアクセストークン下さい
AuthserverToken --> Client: アクセストークン発行します
Client -> APIserver: アクセストークン渡すので\nリソース下さい
APIserver -> DataBase: リソース取得
APIserver --> Client: リソースどうぞ
Client -> User: 機能使えます

@enduml

```


## 感想

Plantumlはすごく良いです。以前はPlantuml単体で使ってたけど、VSCode上で使う事でプレビューしながら作業できて、捗りました。
勉強がてらOAuthをもう少し掘り下げた図とかも、書いてみようと思います。
React: useEffectが2回実行される件【備忘録】

## 前提

以下のような関数コンポーネントがある

```
const MyComponent = () => {
  useEffect(() => {
    console.log("useEffect処理実行");
  }, []);
  return <div>マイコンポーネント</div>;
};
```

開発モードで確認すると、コンソールログが2度表示される

## 原因
* Strictモードが原因だった模様
* 開発モードの時のみ意図的に挙動となる(特定の関数のみ、公式に記載あり)

```
<React.StrictMode>
  <MyComponent />
</React.StrictMode>
```

* 以下に役立つ(公式より)
> 安全でないライフサイクルの特定
> レガシーな文字列 ref API の使用に対する警告
> 非推奨な findDOMNode の使用に対する警告
> 意図しない副作用の検出
> レガシーなコンテクスト API の検出
> 安全でない副作用の検出

## 参考
* [strict モード | React](https://ja.reactjs.org/docs/strict-mode.html)
WPFのListView(DataGrid)の検索機能

# はじめに

今回は **WPF** と呼ばれるWindowsアプリケーション用に使用するフレームワークで使用するC#の記事になります。
WPFを使用する際には、UIの作成にXAML、クリックイベントやバックエンドにC#と言語を分けて作成します。

# 今回やりたいこと
WPFのListView(DataGrid)で、取得したい情報をTextBoxから取得し、取得できた情報をListViewの選択した状態で返すプログラムを作成します。

## なぜこのようなプログラムが必要なのか
ListViewで表示されている情報から、欲しいデータを取得することを目的とします。
しかし今回は、View表示している元データを保持していないので、Viewに表示している情報から検索をしたいと考えています。

# 実際に苦労したこと
List表示の際に、Itemsourceを利用すると思うが、そのデータをパブリック変数として保持せずに、ListView(DataGrid)内のメソッドで完結させるのが、苦労しました。


# WPFのListView(DataGrid)の検索機能追加
## 今回使うデータ

今回使用するデータは以下のようになっているとします。

|ID  |店名    |前回の連絡日付|次回の連絡日付|担当者 |
|---|---|---|---|---  |
|1  |俺のリップクリーム    |2022-3-18|2022-3-22|山田太郎 |
|2  |俺のスマホ    |2022-3-18|2022-3-22|高橋花子|
|3 |俺の天津飯    |2022-3-18|2022-3-23|ああ二郎 |
|5  |俺のチャオズ  |2022-3-18|2022-3-23|ああ二郎  |
|7  |俺のヤムチャ |2022-3-18|2022-3-23|サブロー |

今回取得したい情報は入力したIDから、一致する列データを選択状態にするということをしたいと思います。

```C#:listcolumns.cs
public void class Listitems
{
    public int id {get; set;}
    public string shop_name { get; set; }
    public string pre_date { get; set; }
    public string next_data { get; set; }
    public string people { get; set; }
}
```

## 追加方法

ラムダ式を用いて、取得します。
**num**には、idを取得し、**found**では、条件に一致するデータの列情報を取得します。


```C#: Example.cs
num = Int32.Parse(textbox.text);
var found = listview.Items.Cast<Listitems>().Where(row => row.id == num).ToList();

listview.SelectedItems.Add(found[0]);

```
found[0]にしているのは、一行目のみ取得するためである。
もし複数の情報を取得したい場合はforeach文で回すと取得できると思います。
SQLを自由に試したい！WindowsにMySQLをインストール

## 誰を対象とした記事か
- SQLを自由に試したい方
- 環境構築を手軽に終わらせたい方

## 著者の環境
- Windows10
- MySQL Ver 8.0.29 for Win64

## MySQLのインストール
- MySQLをダウンロード
https://dev.mysql.com/downloads/windows/installer/8.0.html
mysql-installer-community-8.0.29.0.msiをダウンロードする

次ページでは「No thanks, just start my download.」をクリックする
(mysqlのアカウントが必須ではない)
![NoThanks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630401/837c3d32-42b2-8d22-e4ed-0935ad225c64.png)

- MySQLインストーラの起動～インストール
ダウンロードしたファイルをダブルクリック
初期画面で「Server only」を選択する（SQLを試すだけならこれだけで十分）
特にこだわらなければ「Next」を押していけば良い
途中でrootユーザのパスワードを入れる画面があるので設定する
![RootPassword.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630401/a5d22982-3ab4-f6a3-967d-4a83463d80a9.png)

- パスを通す
MySQLの実行ファイルがあるフォルダに対してシステム環境変数の設定をおこなう
変更していなければ以下になっているはず
`C:\Program Files\MySQL\MySQL Server 8.0\bin`
コントロールパネル > システム環境変数の設定 > Pathに追加

- バージョン確認
コマンドプロンプトから`mysql --version`を実行してバージョンが出ればインストールOK

***
## データベースを作成する
rootユーザでMySQLに接続して、データベースを作成する
```MySQL
mysql>CREATE DATABASE [データベース名]
```
## ユーザの作成
新規ユーザを作成して、データベースレベルの権限を付与する
(個人的にはローカル環境といえどrootユーザは普段使いしない癖を付けたいためです)

```MySQL
mysql>CREATE USER [ユーザ名] IDENTIFIED BY '[パスワード]';
mysql>GRANT ALL ON [データベース名].* TO [ユーザ名];
mysql>GRANT SELECT, INSERT ON [データベース名].* TO [ユーザ名];
```

## データベースを選択
上記で作成したユーザでMySQLに接続して、データベースを選択する
```MySQL
mysql>SET [データベース名]
```
あとはテーブルを作成してデータを入れて、自由にSQLを実行して楽しみましょう。
Flutter : Navigator2.0 river_pod で画面遷移を実現する

## 先に実装のせておきます
どなたかの参考になれば幸いです。
こうした方が良いなどあればご教示願います！
```dart: custom_roter_delegate.dart
enum PageName {
  hogePage,
  fugaPage,
  fooPage
}

// これをローカルに宣言してしまうと画面遷移アニメーションが表示されないようになってしまいます。
final GlobalKey<NavigatorState> _nestedNavigatorKey = GlobalKey<NavigatorState>();

class CustomRouterDelegate extends RouterDelegate<void>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin {
  CustomRouterDelegate(this.ref);

  final WidgetRef ref;

  @override
  GlobalKey<NavigatorState>? get navigatorKey => _nestedNavigatorKey;

  @override
  Widget build(BuildContext context) {
    final fooPageTitle = ref.watch(fooPageTitleProvider);

    return Navigator(
      key: _nestedNavigatorKey,
      pages: [
        const MaterialPage(
          arguments: PageName.hogePage,
          child: HogePage(),
        ),
        if (ref.watch(showFugaPageProvider))
          MaterialPage(
　　　　　　　　　　　　　　　　　　　　　　　　// nameというプロパティも存在しますが、こちらはルートのnameなのでどの画面かという情報を渡すには適していないと思われます。
            arguments: PageName.fugaPage,
            child: FugaPage(),
          ),
        if (fooPageTitle.isNotEmpty)
          MaterialPage(
            arguments: PageName.fooPage,
            child: FooPage(title: fooPageTitle),
          )
      ],
      onPopPage: (route, result) {
        // 戻る先の画面がない場合にはfalse返す
        if (!route.didPop(result)) {
          return false;
        }
　　　　　　　　　　　　　　　　　// 戻る挙動の元の画面をpagesから除く為に値を戻したりする
        final pageName = route.settings.arguments as PageName;
        switch (pageName) {
          case PageName.fugaPage:
            ref.read(showFugaPageProvider.notifier).state = false;
            break;
          case PageName.fooPage:
            ref.read(fooPageTitleProvider.notifier).state = "";
            break;
          default:
            break;
        }
        return true;
      },
    );
  }

  @override
  Future<void> setNewRoutePath(void configuration) async {}
}


final showFugaPageProvider =
    StateProvider.autoDispose<bool>((ref) => false);

final fooPageTitleProvider = StateProvider<String>((ref) => "");
```
でこれを使いたいWidgetのところに置いてあげる
```dart: 使いたいwidget.dart
Container(
  child: Router(routerDelegate: CustomRouterDelegate(ref));
)
```

ざっくり実装を載せましたがこんな感じでいけます。

## 私がこれを実装するまでに起こった事象
・どこかのサイトを見てnavigatorKeyをCustomRouterDelegateのコンストラクタのところでインスタンス生成してしまっていたため、画面遷移animationが消える
・onPopPageのところでproviderに値を戻す操作をしていなかったため、戻るボタンで戻ってきた後に再度画面遷移しようとしたら反応しなくなってしまう
PowerVC : snapshot をAPI実行で作成する

##### はじめに

PowerVC は API を実行することで GUI で実施している内容を CLI でも実行することが可能です。
当記事では snapshot の作成を確認しました。

・Virtual machine snapshot and restore API
https://www.ibm.com/docs/en/powervc/2.0.2?topic=apis-virtual-machine-snapshot-restore-api


---
##### 環境

PowerVC 2.0.2 (RHEL 8.4 ppc64le)


---

##### 事前準備

APIを実行できるように変数を設定します。

```
# source /opt/ibm/powervc/powervcrc
# export OS_USERNAME=root
# export OS_PASSWORD=<パスワードを記入>
```

token を発行し、変数に入力します。

```
OS_TOKEN=`openstack token issue | grep "| id" | awk '{print $4}'`
```

---

##### snapshot 作成実行

コマンド参考：
「curl -ks -X POST -H "X-Auth-Token: $OS_TOKEN" -H "Content-Type: application/json" -d '{"vm-snapshot": {"name": "xxxx", "description": "xxxx", "volumes": [ "対象のボリュームID" ]} }' "https://(PowerVC Virtual IP):8774/v2.1/(tenant-id)/servers/(仮想マシンのID)/action"」


実行例

```
# curl -ks -X POST -H "X-Auth-Token: $OS_TOKEN" -H "Content-Type: application/json" -d '{"vm-snapshot": {"name": "VM_snapshot_test", "description": "VM_snapshot_test_API", "volumes": [ "503xxx18-dxxx-48b1-8xxx-2fdbxxx72a5b" ]} }' "https://PowerVC Virtual IP:8774/v2.1/e2XXXXX0856XXXXX4XXX2dfbbXXX3/servers/9XXXXaf8-7XXX-4de2-8XXX2-4f6XXXXXXX89/action"
```

---

##### 結果確認

jq コマンドを使用して対象のsnapshot 名で結果を抜粋。
status が avilable を確認できます。


```
# curl -ks -H "X-Auth-Token: $OS_TOKEN" -X GET https://<PowerVC Virtual IP>:9000/v3/e2XXXXX0856XXXXX4XXX2dfbbXXX3/instance-snapshots| jq '[.instance_snapshots[]]' | jq 'map(select( .name == "VM_snapshot_test" ))'
[
  {
    "created_at": "2022-04-28T00:58:37.000000",
    "updated_at": "2022-04-28T00:58:59.000000",
    "id": "9bXXXX5e-1b33-XXXa-98c9-09d6eXXXX64d"
    "instance_id": "9XXXXaf8-7XXX-4de2-8XXX2-4f6XXXXXXX89",
    "name": "VM_snapshot_test",
    "status": "available",
    "description": "VM_snapshot_test_API",
    "snapshots": {
      "503XXXX8-dXXX3-4XXX-8XXX-2fdXXXXXXa5b": "eXXXX450-ed57-4XXXf-XXXc-78XXXXX3ac5d"
    },
    "temp_snapshots": null,
    "failed_snapshots": null,
    "task_uri": "https://<Powervc virtual IP>:9000/v3/e2XXXXX0856XXXXX4XXX2dfbbXXX3/instance-snapshots/get_progress?action=snapshot&task_id=3dfXXXX490-4446-9433-b8aXXXd1799&id=9bXXXX5e-1b33-XXXa-98c9-09d6eXXXX64d", 
    "user_id": "32d6cXXXX80d9e09dXXXX9843a9",
    "project_id": "e2XXXXX0856XXXXX4XXX2dfbbXXX3",
    "status_description": null
  }
]
```

---
##### 参考
[PowerVC_API操作 Hint & Tip](https://ibm.ent.box.com/s/v9siieyjiraicrfscui8kvpxdfe99632)



以上です。
シェルスクリプトでたくさんなrarファイルを解凍する

貰ったファイルがこうなっている事ってたまにあるんじゃないっすか
```
$ pwd
~/Downloads/[有名な作者]すごい面白い漫画
$ ls -1
'すごい面白い漫画 01.rar'
'すごい面白い漫画 02.rar'
'すごい面白い漫画 03.rar'
'すごい面白い漫画 04.rar'
'すごい面白い漫画 05.rar'
...
'すごい面白い漫画 99.rar'
```
手動で一個一個解凍するのは流石にめんどいなんで、簡単なシェルスクリプトで自動でやってみる
まずファイルを作る
```
$ touch batch_unrar.sh
$ chmod +x batch_unrar.sh
```
中身はこう
```sh
#!/bin/sh

#このディレクトリにある全てのrarファイルに対してループする
for f in *rar 
do
    # サブディレクトリ名を決める
    dir="${f/.rar/}" 
    # 解凍で出てきたファイルを入れるサブディレクトリを作る
    mkdir $dir 
    # unrarコマンドで解凍する　なかったらhomebrewかpacmanとかで"rar"をインストールしてね
    unrar x $f 
    # 出てきたjpg画像をサブディレクトリに入れる
    mv *jpg $dir 
    # いらなくなったrarファイルを消す
    rm $f 
done
```
そしてスクリプトを実行すれば完成
```
$ ./batch_unrar.sh
```
おしまい
The '.git/hooks/pre-commit' hook was ignored because it's not set as executable.の対処

### 結論
pre-commitに対して実行権限を付与。
```
chmod +x .git/hooks/pre-commit
```

### 環境
OS: macOS Big Sur
PHP: 8.0
Laravel: 8.0

Docker Desktop for macも使用

### 現象

phpmdやphp-cs-fixerをpre-commitフックを使って、コミット時に問題の検出とコード整形を自動化しようとして、
`[プロジェクト名]/.git/hooks/pre-commit`にpre-commitのスクリプトを記述。

「よっしゃこれで手動でコマンド打たんでもよーなったで！」

```console:コンソールに表示されたエラー
hint: The '.git/hooks/pre-commit' hook was ignored because it's not set as executable.
hint: You can disable this warning with `git config advice.ignoredHook false`.
```

無視された。

「おいらの書いたスクリプトは見る価値もないのか。。。」

### 原因
実行権限がない

### 対策
実行権限を与える。
以下のコマンドを実行。
```
chmod +x .git/hooks/pre-commit
```

`+`は、指定した権限を付与する変更方法
`x`は実行権限

`+x`で`実行権限を付与する`という意味になるようです。

「it's not set as executable.のことか！」って気づきました。
権限とかそういうワードを出力してよ〜って思いました。
AjaxSwingをインストールしてみた

なんと、JavaのSwingアプリを、ブラウザーから利用できるようにする製品があるそうです。

AjaxSwingという製品。
デモは無料なので、ためしにインストールしてみました。

http://creamtec.com/products/ajaxswing/doc/index.html

Licensingのところに値段が書いてあります。
一番左のやつは無料なので、ためしにダウンロード。

ダウンロードするためには、自分の名前を入れる事と、メールアドレスを入れるだけでした。

Windowsの場合、インストーラーのEXEがあります。
それ以外のプラットフォームはZIPがあり、ごにょごにょインストールするみたいです。

私の場合は、Windows10なので、インストーラーでインストールしました。
ちなみにOracleのJAVA Version8が必要だそうです。

私は、AdoptのOpenJDK8を使っているのですが、OracleのJavaを再インストールするのは
面倒だったので、そのまま続行してみました。

なおインストーラーを起動すると、Windowsが警告してきましたが、無視して、続行しました。

インストールの最後にデスクトップのショートカットをつくるのかと聞いてきました。
それをチェックして、インストール後に、ショートカットから起動したのですが
うまくいきませんでした。

上記サイトの情報によると、どうもこのショートカットは関係ないみたいです。

C:\AjaxSwing4.7.3\bin
の位置で、コマンドプロンプトを開いて、StartSereverとすると
起動できました。

ブラウザーから
http://127.0.0.1:8040/
に、アクセスすると、ページがでます。RunDemoのボタンを押すと、
Swingのデモ画面が出ました。



![ajaxSwing_Capture.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339875/16e8afc8-f796-fd91-2136-40e3276d6a7e.png)


残念ながら、私の環境では、いろいろExceptionが出まくって、いました。全部は動いていないみたいです。

たぶん、AdoptのOpenJDK8を使っているせいなのでしょう。
今度、OracleのJava8のWindowsを用意できたら、再度ためしてみたいと思います。

C:\AjaxSwing4.7.3\bin
の位置で、コマンドプロンプトを開いて、stopSereverとすると
終了です。

最後まで、お読みくださり、ありがとうございました。
脆弱性診断士メモ

# 脆弱性診断士メモ
PF/WA脆弱性診断やペンテストについて、学習した内容をアウトプットしていきます。




## ペンテスト
### pivotとは
既に侵入したシステムから、同一ネットワーク上の別システムへの侵入を行うこと。
水平移動（lateral movement）とも表現される。

外部からの侵入のシナリオとして、一般ユーザの認証情報で侵入されたとします。
攻撃者の目標を達成するために一般ユーザの権限では足りない場合、同一ネットワーク上でpivotし、より権限の強いシステムに対して更に侵入を試みます。


## 脆弱性診断
今後追加予定