OmniAICreator/Qiita-1.07M huggingface dataset:
[勉強メモ]マイクロサービスを理解 Vol4

Vol1：マイクロサービスの概要->https://qiita.com/get-latest-information/items/e959d12a54a378ffceb2
Vol2: DDD(ドメイン駆動設計)を用いたマイクロソフトサービス設計アプローチ -> https://qiita.com/get-latest-information/items/5da2088e952c2562347f
Vol3: 戦術的DDDを使用したマイクロサービスの設計 ->https://qiita.com/get-latest-information/items/7d191527bbe7daac5cbe
Vol4: マイクロサービス境界の識別　＊今回

##今回のフォーカス
MicrosoftのAzure Docsで色々な機能やアーキテクチャなどがまとまっているので、
今回は、マイクロサービス関連で下記ページをサマリーします。**`なるべく短い言葉を使って。`**
◆マイクロサービス境界の識別
https://docs.microsoft.com/ja-jp/azure/architecture/microservices/model/microservice-boundaries

##ドメインモデルからマイクロサービスへ導くアプローチ
- アプリケーション内のマイクロサービスの識別
    1. マイクロサービスの機能が、複数のコンテキストに跨っていないことを確認。跨っている場合は、ドメイン分析の修正が必要。
    1. ドメインモデルの集約を確認。
        - 適切な集約は以下の特徴がある。
            1. 集約は、技術観点ではなく、ビジネス要件から派生している 
            1. 集約には、高凝集度が必要。
            1. 集約同士は、疎結合である。
    1. ドメインサービスは、複数の集約に対するステートレスな操作。複数のマイクロサービスを含むワークフローである。
    1. 機能以外からの要件検討。機能以外とは、チームの規模、データの型、テクノロジー、スケーラビリティ、可用性、セキュリティなど。
- 上記の後は、次の基準で設計を検証
    1. 各マイクロサービスが１つの役割のみをもっていること。
    1. サービス間で頻繁な呼び出しが行われないこと。頻繁におこるなら１つにしたほうがいいかもしれない。
    1. 各マイクロサービスが、チームが対応できる規模であること。
    1. 他のサービスに依存してないこと。「Aをデプロイしないと、Bがデプロイできない」といったことがない、つまり個別に展開できること。

※１つのMSを複数に分割するほうが、複数のMSをリファクタリングするよりも簡単である。

##ドローン配送アプリにマイクロサービスを適用する
これまでの検討を踏まえたMS設計概要図↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284059/c6032255-1f49-13be-2c54-291278e32e3a.png)
- 以下の要因に対しても考慮が必要。
    - ネットワークオーバーヘッドがあある。
    - データスキーマが適しているか。新たに作成する必要があるか。
    - レガシーシステムはあるか。
    - 他のコンテキストを担当するチームと簡単に連絡/連携できるか。

##感想
次の勉強候補。
- ソフトウェア開発関連 ->オブジェクト指向,クリーンアーキテクチャ,レイヤードアーキテクチャ,ヘキサゴナルアーキテクチャ
- アジャイル開発
- API
ピクトグラムについて

インフォグラフィック
　情報をわかりやすく伝えるもの
　名前をしらなくてもなんとなくわかる

ピクトグラム
　トイレ、オリンピックとか文字がなくてもわかる

こういうのを作ってくださいもいえる
やりたいこと　どういうふうに宣伝していきたいのか　方向性がきまってればこちらで考えてもいい

情報をだして興味をひかして誘導する
製品の紹介だと誘導しないけど、関連する情報があればクリックしたくなるみたいな
【AWS】EC2インスタンスのユーザーデータ

#プログラミング勉強日記
2021年5月21日
EC2インスタンスを起動するときにユーザーデータを活用してセットアップを自動化できる。

#ユーザーデータ
　インスタンスの起動の詳細設定の一番下にあるものがユーザーデータになる。スクリプトを設定することでLinuxの初期設定を自動化できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/588677e1-c0c1-7c3b-98aa-34efe36d0b17.png)


#ユーザーデータの活用
　EC2インスタンスを立ち上げるたびにアップデートやApacheのインストールをするのはめんどくさいので、スクリプトを実行することで(スクリプトをユーザーデータに入れておくことで)、ユーザーデータからスクリプトのデータをインスタンスに引き渡すことができる。起動するときにそのスクリプトを自動で実行してくれる。
【API】RESTful API

# RESTful API とは
- Webシステムを外部から利用するためのプログラムの呼び出し規約（API）の種類の一つ
- RESTと呼ばれる設計原則に従って策定されたもの

## REST とは
- REpresentational State Transfer の略
- 分散システムにおいて複数のソフトウェアを連携させるのに適した設計原則の一つ

### REST の原則
- セッションなどの状態管理を行わず、やり取りされる情報はそれ自体で完結して解釈することができる
- 情報を操作する命令の体系が予め定義・共有されている
- すべての情報は汎用的な構文で一意に識別される
- 情報の一部として、別の状態や別の情報への参照を含めることができる

# RESTful API を使うメリット
- URIに規律が生まれることで、APIを利用するサービス開発者が楽になる
- URIに規律が生まれることで、API開発者もURIからソースのどの部分なのかが容易にわかる
- ブラウザのアドレスバーにURIを入力すればリソースが参照できる
- サーバ、クライアント間で何も共有しないことにより、負荷に応じた拡張性が向上する
- GET・POST・PUT・DELETE等のHTTP標準のメソッドを使うことで、シンプルで一貫性のあるリクエスト標準化が円滑に行える
すべてのワークスペースに共通のデバッグ構成を設定する（Visual Studio Code）

Visual Studio Codeでのデバッグの構成（`project_root/.vscode/launch.json`）はプロジェクトごとに作成します。ですが、だいたいいつも同じ内容なのに、毎回作成するのはめんどうですね。
[公式ドキュメント](https://code.visualstudio.com/docs/editor/debugging#_global-launch-configuration)に従い、`settings.json`に追加することで、グローバルに共通のデバッグ構成（＝デフォルトの`launch.json`）を設定することができます。

# 設定例
以下のデバッグ構成をすべてのワークスペースから利用できるようにしてみます。

```json:launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        },
    ]
}
```

`Ctrl+Shift+P`でコマンドパレットを開き、`settings.json`と入力し、`Preferences: Open Settings(JSON)`を選択し、ユーザーの`settings.json`を開きます。開いたら、次のようにデバッグ構成を追加します:

```json:settings.json(ユーザー)
{
    ....
    (何かいろいろなもの)
    ....
    "launch":{
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Python: Current File",
                "type": "python",
                "request": "launch",
                "program": "${file}",
                "console": "integratedTerminal"
            }
        ]
    }
}
```

これでOKです。

# 参考
https://code.visualstudio.com/docs/editor/debugging#_global-launch-configuration
引数と戻り値と仲良くなろう

![a02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191635/eca987ba-4b14-e24d-e46e-4c0638bc565c.png)

# メモ
- 詳細説明は後日追記します。scratchのブロック定義と比較したいのです。
- まずはボタン押下時にどういうロジックで機能しているかを、デバッグログをひとつひとつたどりながら確認していきましょう。
- ボタン押下時のスクリプトの設定の仕方は、[こちら(「【Unity】Sceneを切り替えるコード」の記事内)](https://qiita.com/sanpeita/items/912bf1fbaab975c9b7d4#%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E7%94%BB%E9%9D%A2%E3%81%A7%E3%83%9C%E3%82%BF%E3%83%B3%E6%8A%BC%E4%B8%8B%E6%99%82%E3%81%AB%E3%82%B2%E3%83%BC%E3%83%A0%E7%94%BB%E9%9D%A2%E3%81%B8%E9%81%B7%E7%A7%BB%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88)を参考にしてみてください。
- コンソール画面での確認がメインになるので、デバッグログの記載は、自分にとって分かりやすい表現にしましょう。「aaa」とか「あ」とかだと、一週間後に分からなくなりますよ～

# 画面レイアウト
- 下図のように、ボタンを設定します。
![a01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/191635/e7af322e-4f31-b680-a712-0fb62a0d6ec5.png)

# スクリプト
## 左側のボタン(引数を確認するボタン)

```btnLogicHikisu.cs

using UnityEngine;

public class btnLogicHikisu : MonoBehaviour
{
    public void OnClick()
    {
        Debug.Log("引数ボタンが押されたよ");
        Add10(5);
    }

    void Add10(int AddNum)
    {
        Debug.Log("===== Add10メソッドが開始されたよ =====");
        Debug.Log("引数は " + AddNum + " だよ");
        int Num = 10 + AddNum;
        Debug.Log("計算結果は " + Num + " だよ");
        Debug.Log("===== Add10メソッドが終了したよ =====");
    }
}

```

## 右側のボタン(戻り値を確認するボタン)

```btnLogicModorichi.cs

using UnityEngine;

public class btnLogicModorichi : MonoBehaviour
{
    public void OnClick()
    {
        Debug.Log("戻り値ボタンが押されたよ");
        Debug.Log("戻り値は " + Add50(20) + " だよ");
    }

    int Add50(int AddNum)
    {
        Debug.Log("===== Add50メソッドが開始されたよ =====");
        Debug.Log("引数は " + AddNum + " だよ");
        int Num = 50 + AddNum;
        Debug.Log("計算結果は " + Num + " だよ");
        Debug.Log("===== Add50メソッドが終了するよ =====");
        return Num;
    }
}

```
React + axios で画像を取得して表示させる

# はじめに

[Django REST Framework で画像を返す API を作成](https://qiita.com/honda28/items/4a37e951059d74fcf9f1)で画像を返す API を作成したので、React アプリで画像を取得して表示させてみる。

# axios のインストール

以下コマンドで axios をインストールする。

```
$ npm install axios
```

# コンポーネントの作成

画像を取得し、それを表示するコンポーネントを作成する。ここでは例として、[React Logo](https://logos-download.com/wp-content/uploads/2016/09/React_logo_wordmark.png) を取得する。コードは以下。

```js:AxiosTest.js
import React, { useState, useEffect } from 'react'
import axios from 'axios'

const AxiosTest = () => {

    const [image, setImage] = useState()

    useEffect(() => {
        axios.get('https://logos-download.com/wp-content/uploads/2016/09/React_logo_wordmark.png')
            .then(res => {
                setImage(res.data)
            })
    }, [])

    return (
        <div>
            <img src={image} width="500" />
        </div>
    )
}

export default AxiosTest;
```

しかし、アプリを起動してみると以下のように適切に画像が表示されない。

![noImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/9ace7f4e-475d-b695-d647-5b28e2fefb84.png)

解決法を調査したところ、[Axios serving png image is giving broken image](https://stackoverflow.com/questions/42785229/axios-serving-png-image-is-giving-broken-image) に解決策があった。修正したコードが以下。


```js:AxiosTest.js(revised)
import React, { useState, useEffect } from 'react'
import axios from 'axios'

const AxiosTest = () => {

    const [image, setImage] = useState()

    useEffect(() => {
        axios.get(
            'https://logos-download.com/wp-content/uploads/2016/09/React_logo_wordmark.png',
            {responseType: 'blob',}
            )
            .then(res => {
              setImage([URL.createObjectURL(res.data)])
            })
    }, [])

    return (
        <div>
            <img src={image} width="500" />
        </div>
    )
}

export default AxiosTest;
```

適切に表示させることができた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769545/da7fe04c-85c3-6928-7614-70797563b815.png)
スマホの低電力モードだとpixi.jsのFPSが半分になる問題を無理やり解決する

pixi.jsには`ticker`という機能があり、ほとんどの場合においてFPS60でループ処理をしてくれる。
しかし、環境にも依るかもしれないが、スマホの「低電力モード」が発動しているとFPSが30まで低下する。

```js:script.js
let app = new PIXI.Application({});

app.ticker.add(loop);

function loop(delta){
    console.log(app.ticker.FPS);
}
```
このような書き方だと、端末の状態によってループ速度が変わってしまい、いろいろなところに影響が出る。
そこで次のようにして無理やり解決を試みた。

```js:script.js
let app = new PIXI.Application({});

app.ticker.add(loop);

function loop(delta){
    for(let i=0;i<Math.round(delta);i++){
        main();
    }
}

function main(){
    console.log(app.ticker.FPS);
}
```

`delta`は`app.ticker.deltaTime`の値であり、前のフレームから今のフレームまでの経過時間を【1/60秒】の単位で出してくれる。
もし`delta`が2に近い場合、つまりFPSが半分になっている場合は、`loop`の内容を2回繰り返す。
こうすれば、描画自体はもっさりとするが処理はほぼ想定通りの速度でやってくれる。

2021/06/02 試行錯誤の続き→[pixi.jsでtickerのFPSが端末の状態に依存しないように見せる](URL:https://qiita.com/nanohanabuttobasu/items/94de586d06445a03a6bb)
Nuxt Composition APIでMaximum call stack size exceededエラー

## 環境

- Nuxt
- Nuxt Compostion API

## なぜ出たのか

- 結論から言うと、Vueのテンプレート側で`router`を呼び出していたため。

```vue:修正前
<template>
  <button @click="router.push('/hogehoge')">hogehogeボタン</button>
</template>

<script lang="ts">
import { defineComponent, useRouter } from '@nuxtjs/composition-api'

export default defineComponent({
  setup() {
    const router = useRouter()
    return { router }
  }
})
</script>
```

```vue:修正後
<template>
  <button @click="toHogehoge">hogehogeボタン</button>
</template>

<script lang="ts">
import { defineComponent, useRouter } from '@nuxtjs/composition-api'

export default defineComponent({
  setup() {
    const router = useRouter()
    // 関数で定義したものをtemplateで使う
    const toHogehoge = () => {
      router.push('/hogehoge')
    }
    return { toHogehoge }
  }
})
</script>
```
Railsの命名規則(キャメル＆スネーク）

#Railsの命名規則
モデル名の途中に大文字が含まれていた場合、単語の組み合わせと判断され、間に_（アンダースコア）が挿入されます。

たとえば「RubyOnRails」とした場合は、ruby_on_railsになります。
豆知識として覚えておくとよいでしょう。

#キャメルケース（ラクダ？）
キャメルケース（英: camel case）は、複合語をひと綴りとして、要素語の最初を大文字で書き表すことをいう。キャメルケースとは、大文字が「らくだのこぶ」のように見えることからの命名である。

```
RubyOnRails
```

![480px-CamelCase_new.svg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1514036/074566ea-907d-acef-3215-2550dd264195.png)

#スネークケース
アンダースコア（_）を区切記号として単語をつなげる。

```
ruby_on_rails
```

#チェインケース
ハイフン（-）を区切記号として単語をつなげる。

```
call-with-current-continuation
```

一部内容でwikipediaを参考にしています。
詳細：https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A1%E3%83%AB%E3%82%B1%E3%83%BC%E3%82%B9
防衛省の新型コロナウイルスワクチン接種予約システムの問題を解決するには

#問題点
さんざん報道されている問題なので、今さらどういう問題なのかここで説明する必要もないと思います。誕生日をチェックしていないとか市町村コードのチェックすらしていないとかいろいろ問題はありますが、一番の問題点は存在しない接種券番号で予約ができることです。

なぜ接種券番号のチェックをやらないかと言うと、接種券番号は各市区町村で管理していて防衛省のシステムにはその情報が含まれていないからです。

接種券番号を各市区町村で管理するのは何故かというと、マイナンバーの利用が間に合いそうになかったからそうするしか方法がなかったのだと思われますが、市区町村が主体で接種をすすめる限りは特に問題ない制度でした。

しかし４月に突然、菅首相が自衛隊に大規模接種会場を運営させると言い出したことで状況が変わってしまいます。二重に摂取することを防ぐためには市区町村が発行した接種券を利用するしかありませんが、各市区町村が管理している接種券のデータを防衛省が集めるのは様々な問題があるからです。

#ダメな擁護論
- 期間がない中で接種数を増やすにはこの方法しかなかった

 → 不正予約が多ければ、接種数を増やすことができなくなり、本来の目的を達成できなくなってしまいます。

- 申し込む人の善意に依存したシステム

 → インターネットの世界は善意もあれば悪意もあります。悪貨は良貨を駆逐するという言葉もありますが、悪意に対処する最低限の仕組みは必要で、防衛省のシステムはそれすらも備えていません。

#ダメな対応案
- 全国の市区町村から接種券番号、生年月日、氏名のデータをリンクしてチェックの処理を組み込めば良い
 → 顧客の置かれている立場を考えず、単に技術的な方法だけで解決しようとする近視眼的な技術屋にありがちな対応案です。日本の歴史や国民性への認識等もう少し視野を拡げましょう。

- 不正入力は法律で厳しく罰すれば良い
 → 日本の法律で罰することができるのは日本国内だけです。インターネットは世界に繋がっています。世界のハッカーを相手にするには日本の法律は何の効力も持ちません。

- 海外からのアクセスを制限すれば良い
→ ハッカーの技術を舐め過ぎです


#発想の転換が必要
接種券番号のチェックには各市区町村がバラバラに保管している接種券番号のデータベースが不可欠なことは動かしようのない事実です。だったらチェックは各市区町村のシステムでやったらどうだろう。防衛省のシステムと市区町村のシステムを連携させて、防衛省のシステムに入力された値を各市区町村のシステムに送って、チェックした結果を返してもらって・・・

ううーーん、ちょっとシステムが複雑になりすぎるね・・・。

あれ？

これって最初っから市区町村の予約システムに入力してもらって、予約結果のリストだけを防衛省が貰えば良いんじゃね？

##市区町村のシステムで予約したら
ということで市区町村のシステムがどういう仕組みになっているかを想像してみる。このシステムを構成しているデータベースの構成要素はたぶん
・接種会場テーブル （場所、最大収容人数 …etc）
・接種実施日時テーブル　（会場、日時、人数枠、…etc）
・接種対象者　（接種券番号、パスワード：初期設定値は誕生日、…etc）
・予約結果

接種会場テーブルに防衛省の会場データを追加して、接種実施日時テーブルに防衛省から各市区町村に割り当てられた日時、人数枠のデータを入れるだけで問題なく動きそう。
これは各市区町村内で接種会場や接種日時が増えたときの対応と同じで、市区町村の予約システムでは簡単にできることでしょう

つまり、防衛省が各市区町村に人数枠を割り当てて通知し、各市区町村の予約システムで予約を受け付ける。あとは予約結果から防衛省分だけを抽出して一覧を防衛省に送り、防衛省は各市区町村から送られてきたデータをマージするだけです。

#お願い
たぶんこの方法しか解決方法はないと思うので防衛大臣に近い方がこの記事を読んだら伝えてあげていただけないでしょうか？

そして、政府に敵対する勢力の方はこの記事を見たとしても大きく取り上げないでいただきたいと思います。この方法を国会の場で野党から提案されたり、朝日新聞や毎日新聞に指摘されたりしたら官邸も防衛大臣も意固地になって絶対にこの案を採用せず、大規模接種会場の混乱は収束しないことでしょう。

いったん立ち上げた防衛省のシステムを停止するだけでも面目丸つぶれなことですが、その解決方法を敵から教えてもらうなど彼らにとってはあってはならないことです。せめて解決方法だけは自分たちで考えたように装ってあげたほうが良いでしょう。
Address already in use - bind(2) for "127.0.0.1" port 3000 (Errno::EADDRINUSE)がターミナルで出た

対処
-

起動しているポートを検索

```rb:起動している一覧表示コマンド
% lsof -i:3000
```

```rb:起動している一覧
COMMAND  PID       USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    7251 maedatakuo   13u  IPv4 0x523a717e82b7309f      0t0  TCP localhost:hbci (LISTEN)
ruby    7251 maedatakuo   14u  IPv6 0x523a717e7674b30f      0t0  TCP localhost:hbci (LISTEN)
ruby    7251 maedatakuo   25u  IPv6 0x523a717e82ea364f      0t0  TCP localhost:hbci->localhost:52804 (CLOSE_WAIT)
ruby    7251 maedatakuo   26u  IPv6 0x523a717e7674a64f      0t0  TCP localhost:hbci->localhost:52810 (CLOSE_WAIT)
ruby    7251 maedatakuo   28u  IPv6 0x523a717e82ea3caf      0t0  TCP localhost:hbci->localhost:52811 (CLOSE_WAIT)
ruby    7251 maedatakuo   29u  IPv6 0x523a717e8136d96f      0t0  TCP localhost:hbci->localhost:52817 (CLOSE_WAIT)
```

```rb:起動ポートを閉じるコマンド
% kill -9 (ポート名)
```
【GoolgeWorkspace】Gsuiteとのプランの比較

https://support.google.com/a/answer/6043385
https://tscloud.co.jp/gsuite/google-workspace-edition

connected sheetを使おうと思ったら、G Suite Business以上でないと使えないとのこと。
私の検証用アカウントも、スプレッドシート＞データ＞Bigqueryに接続の項目がなかったので、確認。

GoogleWorkspaceのコンソール画面で、お支払い＞定期購入管理で契約状況がみれます。

私のアカウントはGsuiteBasicだったので、やはり利用はできないみたい・・・
【Tkinter】非モーダルなサブウィンドウの作成方法

#通常

これを元に非モーダルなサブウィンドウを作成します

```python
import tkinter as tk
frame = tk.Tk()
tk.mainloop()
```

#サブウィンドウの作成

frame2にトップレベルウィジェットを作成し適当にタイトルを設定

```python
import tkinter as tk
frame = tk.Tk()
frame2 = tk.Toplevel().title("subwindow")
tk.mainloop()
```
#おわり + 例

```python
import tkinter as tk

frame = tk.Tk()
frame.title("main")
frame.geometry("150x150")
label1 = tk.Label(frame , text="main" , font=('',20))
label1.pack(anchor=tk.CENTER,expand=1)

frame2 = tk.Toplevel()
frame2.title("sub")
frame2.geometry("150x150")
label2 = tk.Label(frame2 , text="sub" , font=('',20))
label2.pack(anchor=tk.CENTER,expand=1)

frame3 = tk.Toplevel()
frame3.title("third")
frame3.geometry("150x150")
label3 = tk.Label(frame3 , text="third" , font=('',20))
label3.pack(anchor=tk.CENTER,expand=1)

tk.mainloop()
```
JavaScriptからPHPにデータを渡してjsonで保存する

クライアントからの情報をサーバーに残しておきたい。
でもデータベースを使うほどのものでもない。
そんな時は配列としてjsonでファイルを保存してしまう。

```js:script.js
async function save(num1, num2){
    let req = await fetch("./save.php",{
        method: "post",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify([num1,num2]),
    });
}

save(2,1);
```

```php:save.php
<?php
    $postData = json_decode(file_get_contents("php://input"),true);//jsからjson受け取り
    file_put_contents("./data.json", json_encode($postData));//jsonファイルを保存
?>
```

保存したjsonは頑張れば見られるし、リファラーのチェックとかも必要なので注意。
BigQueryで日付（YYYY-MM-DD）から年月（YYYY-MM-01）に変換する

#概要
BigQueryで日付（YYYY-MM-DD）から年月（YYYY-MM-01）に変換するためのメモ
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201533/94814f7c-db27-1a34-a625-098e8c0d3fd2.png)

#SQL
文字列にして、最初の７文字目までを抜き出して、`-01`を足して、最後にDATE型に戻す。

```sql
SELECT
  CAST(CONCAT(SUBSTR(CAST(date AS STRING), 1, 7), "-01") AS DATE) AS month
FROM
  from_table
```

上記でも出来ますが、`DATE_TRUNC` を使って、すっきり書く。

```sql
SELECT
  DATE_TRUNC(date, MONTH) AS month
FROM
  from_table
```
[勉強メモ]マイクロサービスを理解 Vol3

Vol1：マイクロサービスの概要->https://qiita.com/get-latest-information/items/e959d12a54a378ffceb2
Vol2: DDD(ドメイン駆動設計)を用いたマイクロソフトサービス設計アプローチ -> https://qiita.com/get-latest-information/items/5da2088e952c2562347f
Vol3: 戦術的DDDを使用したマイクロサービスの設計

##今回のフォーカス
MicrosoftのAzure Docsで色々な機能やアーキテクチャなどがまとまっているので、
今回は、マイクロサービス関連で下記ページをサマリーします。**`なるべく短い言葉を使って。`**
◆戦術的DDDを使用したマイクロサービスの設計
https://docs.microsoft.com/ja-jp/azure/architecture/microservices/model/tactical-ddd

##戦術的パターンの概要
- 戦術的DDDパターンは、ドメインモデルをより正確に定義する。１つのコンテキスト内で適用される。
- 戦術的DDDパターンの概要図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/284059/9c8a9ce7-d9c9-2c25-f5b6-9e8dd17abcdb.png)
 - エンティティ：ユニーク(一意)なIDをもつオブジェクト。変わる可能性はある。ex)顧客、銀行口座
 - 値(Value)オブジェクト:変更を管理する必要がないオブジェクト。ex)色、日付、時刻
 - 集約：一貫性の境界を定義。
        - 簡単にいうと、マイクロサービスA、B、Cがあって、一貫性が必要である場合、データ整合性をアプリケーション側で保つようにすること。(記事の中では、トランザクションのインバリアント(不変)をモデル化すること。)
        - モノリシックの場合は、データレイヤーで一貫性・整合性を保っている。
    - ドメインサービスとアプリケーションサービス：サービスは、データを保持せず、ロジック実装するオブジェクト。
        - ドメインサービス：ドメインロジックをカプセル化するもの
        - アプリケーションサービス：技術的な機能(ユーザ認証、SMSメッセージ送信etc)
    - ドメインイベント：ドメイン内で発生したイベントを外部へ通知すること。

##ドローン配送：パターンの適用
- ドローン配送シナリオでは、以下のエンティティが考えられる。
    - 配送
    - 荷物(Package)
    - ドローン
    - Account
    - 確認
    - 通知(Notification)
    - タグ
        - 集約は、４つ→配送、荷物、ドローン、アカウント
        - 確認と通知は、配送の子エンティティ
        - タグは、荷物の子エンティティ
- 値オブジェクト：Location,ETA(Estimated Time of Arrival),荷物の重さ,荷物のサイズ
- ドメインイベント：
    - ドローンが飛行中に、場所や状態(飛行中、着陸)を示すDroneStatusイベントを送る。ドローンエンティティが送る。
    - 配送エンティティが配送段階が変わる度に、DeliveryTrackingイベントを送る。


##感想
似た用語が多くて、パッとわかりづらい。ドメインモデル、ドメインロジックなど。
やっぱり一度、簡単でいいので体験してみるべきと感じた。
git 総まとめ

## ブランチの作成
### 「リモートブランチ→ローカルブランチ」
```
$ git checkout -b ローカルに作成するブランチ名 origin/リモートのブランチ名(元となるブランチ)
```
### 「ローカルブランチ→リモートブランチ」
```
$ git push -u origin リモートに作成するブランチ名  //リモートに登録
//　以後commit →　push で追加可能
```

## commit
### commit 履歴の確認
```
$ git log --oneline
```
### commit間の差分をみたい
```
$ git diff <コミットID1(古いcommit)> <コミットID2(新しいcommit)>
```

## 業務でやることが多い作業
### remoteとrocalの差分を確認
手順：　「fetch→diff」

```
$ git fetch origin リモートブランチ名
$ git diff ローカルブランチ名 origin/リモートブランチ名
```
### remoteとrocalのデータを揃える
自分以外の誰かがソースを編集したのでremoteのデータが最新で自分のPC内（ローカル）が古いコードの場合はよくあります。
そんな時は「fetch→merge」で最新のデータをローカルに持ってきましょう

```
$ git fetch origin リモートブランチ名
$ git merge origin/リモートブランチ名
```
※fetchしただけではローカルのファイルは更新されません。
.gitファイル内にある「origin/ブランチ名」が更新されます。なので「origin/ブランチ名　→　現在のブランチのファイル」をmergeで最新のデータに揃えてあげましょう！
【Scratch】Turbowarpを目安にプロジェクトを軽量化

今回は、Turbowarpを目安にプロジェクトの軽量化をしていきます。

#Turbowarp

Turbowarpは、ScratchのMODで、スプリクトの実行をより高速化させるためにプロジェクトをJavaScriptにコンパイルさせるものです。
<code>https\://turbowarp.org/ScratchのプロジェクトID/</code>でScratchのプロジェクトをTurbowarpで実行させることができます。
実行させると、〇〇個中〇個ダウンロード完了と表示されます。
![Screenshot 2021-05-21 13.15.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1525654/5a2ac614-61e6-f45e-a697-5dcaf17f1dad.png)
今回は、これを基準にして軽量化をしていきます。
軽量化できる部分を<b>Scratchで</b>直してはTurbowarpで数値を確認、直しては確認...という作業を繰り返して軽量化します。

#軽量化させよう
さて、次は重くなる原因となるブロックなどを紹介していきます。
下で提示するブロックを減らし、うまく軽量化させましょう。

<b>・「ずっと」ブロック</b>
![Screenshot 2021-05-21 16.04.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1525654/4e336153-b81b-e421-388f-4d3d23140b6e.png)
「ずっと」ブロックは停止ボタンを押さない限り∞に繰り返されます。
だからその分重くなります...
解決法 = できるだけメッセージブロックで代用!

<b>・「自分自身のクローンを作る」・「〇〇のクローンを作る」</b>
![Screenshot 2021-05-21 16.05.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1525654/d6aab5a1-6be6-2b94-afbc-24fc89c1db46.png)
クローンの役割は[Scratchwiki](https://ja.scratch-wiki.info/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3)を見てください。
クローンを大量(極限)まで作ると、クラッシュしてしまうほど重くなります。
このブロック自体を減らすのは難しいですが、作ったクローンに目を向けましょう。
解決法 = 不要な(クローンを使っていない)ときはクローンを削除!

<b>・長いBGM</b>
これは仕方ないかも...
あまりにも長いBGMをプロジェクトに使うのはやめておきましょう。

#軽量化は少しずつ
一気に数値を減らそうと思っても、難しいです。
少しずつでいいので、やっていきましょう。
CodePenで面白そうなテンプレート14選（Vue.js編）

#CodePenとは
CodePenはHTML、CSS、JavaScriptのコード入力画面と、その結果が表示される画面が一体で操作できるサービスです。
多くの作者がテンプレートを公開していて、自分の環境にフォークして使えます。

今回自分でWebアプリを作るにあたり参考にさせていただこうと調べた中で面白かったものを挙げていきます。

https://codepen.io/topics/

#お気に入りのVue
Vueの説明文は「The Progressive JavaScript Framework」とのこと。普通の説明ですね…

2021/5/21時点でFeaturedされた順に42個を調べてみました。（Like数順にソートする方法を知っている方、教えてくださいm(__)m）
個人的に直近で使えそう・使いたいものに◎をつけています。
**凄いな、と思ったものは太字にして上位に持ってきてます。**

https://codepen.io/Lewitje/pen/MVommB

**↑　お絵描きアプリ。基本的な機能は全部揃っていて、ここまでのことができるのか！**


https://codepen.io/jasesmith/pen/rZmKQG


**↑　カラーコードを指定した段階で刻んでいける。初めて見た形式だけど便利そう。**

https://codepen.io/rainner/pen/bjJYjp

**↑　仮想通貨の価格ダッシュボード。**

https://codepen.io/saawsan/pen/jayzeq

↑ ToDoリスト。チェックボックスで文字に取り消し線が引かれ、ごみ箱マーククリックで行ごと消える。デザインがお洒落。

https://codepen.io/YusukeNakaya/pen/ebLjde

↑　サイバーな感じのナビゲーション。

https://codepen.io/meodai/pen/xyqoEO

↑　色コードをトグルで動かして試せる。

https://codepen.io/iamjoshellis/pen/yaAXmr

↑　また違った感じのToDoリスト。完了タスクと未了タスクで画面切り替えがある。

https://codepen.io/ktsn/pen/wrxymV

↑　ページ遷移。びゅっと右から左に画面がかっこよく切り替わる感じ。

https://codepen.io/mjweaver01/pen/ggdmRo

↑　人名検索。検索文字を入れていくと段々対象が絞られていく。


https://codepen.io/shshaw/pen/EvgaRW

↑　パタパタとカード型の記事が切り替わっていくモーション。

https://codepen.io/shshaw/details/NbJgyw

↑　文字入力したり消したりすると派手なモーションで表示される。

https://codepen.io/gau/details/LjQwGp

↑　デジタル表示の時計。かっこいい。

https://codepen.io/shshaw/pen/BzObXp

↑　◎　パタパタ変わる昔の時計風。レトロ。

https://codepen.io/ispal/pen/LxjgEj

↑　◎　タイマー読み上げ。

#おわりに
うーむ、すごいものがいっぱいだ。コードが提供されているのはありがたいので読み進めていきたいです。
でもまずは簡単なものから、ね。。。

おすすめのテンプレートがありましたら是非コメントで教えてください。m(__)m
Try Journalism: Create, Validate & Share Local News Of Your Locality

JC gives its all user the right to validate, authorizing each one of them to decide the accuracy of news floating in their news feed by voting up or down. The percent of accuracy is calculated based on their votes and is shown with each news to help readers refine the information they take from this platform.
![social-networking-app.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1476834/2d63a580-8d1a-8df0-9016-31e3617e5909.jpeg)

<a href="http://jantachoupal.com/">Janta Choupal</a> is an effort to cater to all your location-specific mobile app requirements in one place, eliminating the need to hunt through individual applications. It is a local social media application but not confined to socializing. JC is more like a local social digital board, where apart from social gossiping, you can put down your locality-specific issues and news updates. And, the features do not end here.
![Local-Community-Application.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1476834/1abb7c0a-97b1-1b15-16ee-43e3707ea35f.jpeg)

1 Platform, 6 User Types
This choupal is not limited to citizens. From social workers, government officials to the elected and aspiring leaders, all the local, yet not-easily-reachable personalities can be found and contacted here.
Janta Choupal aims of benefiting all by binding all user types from the same location in the same social circle.
This way, where the citizens get to connect with the leaders for understanding them better before electing, the leaders get to reach and influence their actual voters. The citizens can directly convey their issues to the concerned government officials, while the local government can easily track the ongoing issues within a locality. The social workers get to motivate people, and the citizens get to seek direct help from activists.
 
Want to try journalism? <a href="https://play.google.com/store/apps/details?id=com.jantachoupal1">Download JC.</a>
 
We all want to stay informed on the local affairs but often fail to make time to check our location-specific news portals. And, if somehow, we manage that time, the news is either not genuine or not relevant to our specific needs.
 
What if you get all your local news on the same social media application that you randomly scrolls through every other hour? What if you get to know the genuineness of the news through an accuracy meter? What if you get to decide its rightness? And, what if you get an opportunity to be your locality’s journalist? <a href="https://www.youtube.com/watch?v=0hnHrx2eaQk
">Janta Choupal</a> gives you all of it.
<a href="http://blog.jantachoupal.com/articles/16015/try-journalism-create-validate--share-local-news-of-your-locality">Read More:</a>
STM32G031の独自のソフトウェアシリアルでHello Worldと出力 9600bps


目的
出来そうだから作ってみた
標準シリアルの場合は、SWDと衝突してフラッシュに書き込めなくなり
使い勝手が悪い。(リセット非対応の時)
ソフトウェアシリアルを使うとSWD,I2C,リセットが
同時使用出来て使い勝手がよくなるため
7,8 SWD
5,6 I2C
4 reset
2,3 電源
1 シリアル出力

とりあえず9600bps 　ウエートと102us

```c++

#define DW   digitalWrite

//10の割り算 0から1028までは、正しい。主に0から999
#define DVI10(n) ((n*205)>>11)

//104 9600
//208 4800
//416 2400
//832 1200

//#define UART_DELAY 832 //  1200bps ok 031
//#define UART_DELAY 418 //  2400bps ok 031
//#define UART_DELAY 202 //  4800bps ok 031
#define UART_DELAY 102   //  9600bps ok 031
//#define UART_DELAY 96 //  1/9600

//#define swdclk   PA14 // 8pin
//#define swdio    PA13 // 7pin
#define swdclk   PA12 // 6pin
#define swdio    PA11 // 5pin
//#define t_led1   PA12 // 6pin
//#define t_led2   PA11 // 5pin
#define en       PA0  // 4pin
#define in7      PB7  // 1pin

int q_st; //スタートタイム debug
int q_et; //エンドタイム   debug


//仮想シリアルへの一文字出力 9600bps
int pc_putc(char ch) {

  DW(in7, HIGH);

    q_st =  micros(); //debug

  DW(in7, LOW);//START
  delayMicroseconds(UART_DELAY);

  for(int ii=0;ii<8;ii++){
    DW(in7, (ch>>ii)&1  );
    delayMicroseconds(UART_DELAY);
  }//for

  DW(in7, HIGH);//Stop
  delayMicroseconds(UART_DELAY);

    q_et =  micros(); //debug 引いた数が0で1041usなら正解

  return(0);

}//pc_putc


void setup() {

  //仮想シリアルの出力ポート

  //ポートをhiにする 初期化
  pinMode(in7, OUTPUT);
  DW(in7, HIGH);

}//setup

char *ss1; //文字のポインター
int ii; //ループカウンター

//dbbug
int l_num,aaa,bbb,ccc; //debug

//debug
void jj_loop() {
        pc_putc('A');
        l_num = (q_et-q_st)/10;
        //l_num = 104;
        bbb=DVI10(l_num);
        ccc=l_num-(bbb*10);
        aaa=DVI10(bbb);
        bbb=bbb-(aaa*10);
        pc_putc( '0' + aaa  );
        pc_putc( '0' + bbb  );
        pc_putc( '0' + ccc  );
        pc_putc( 'X' );
        pc_putc( 'u' );
        pc_putc( 'S') ;
        pc_putc( '\r' );
        pc_putc( '\n' );
        delay(1000);
}//loop


//無限ループ
void loop() {

  ss1="Hello World !\r\n";
  ii=0; //ループカウンター
  while(ss1[ii]!=0){

    //一文字出力
    pc_putc(ss1[ii]);ii++;

  } //while

  //1秒の待ち
  delay(1000);

}//loop




```

![soft_serial_test_031_9600.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1ee36495-6dc6-d70f-049c-3809aca294da.jpeg)

![soft_serial_test_031_9600_h.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/d56e606d-cfe3-345a-1fa2-6ba0f66b9bd1.jpeg)
ノーコードでwebサイト作るサービスSTUDIOを使ってwebサイトを公開してみた

完成度は度外視して「作品を作った」という自信をつけることを優先
https://rsqaretoyou.studio.site/

レンタルサーバーを用意する必要もないらしい。

・STUDIOとは
https://studio.design/ja
「STUDIO」は、ブラウザ上で利用することができるコーディング不要のウェブサイト作成ツールです。日本語対応、インストール不要、基本無料。

・STUDIOの使い方
「STUDIO」はインストール不要で、ブラウザでログインして使えるツールです。

ログイン方法は２種類。
・名前、メールアドレス、パスワードを新規登録して専用アカウントでログイン
・Facebookのアカウントでログイン

「STUDIO」専用のアカウントを作成しなくても、Facebookアカウントをお持ちであればそれを使ってログイン可能です。

・まとめ
少しhtml/cssの知識があれば直感的にいじれるので非常におすすめ
EC-CUBE4でプラグインのEntityを拡張しようとしたらproxyの生成でコケて困った時の対応

買ったプラグインにちょっと機能的な不満があり、カスタマイズしようとしたら、proxyの生成でコケたのでその対応をメモしときます。

```shell
./bin/console eccube:generate:proxies
Exception trace:
  at /var/www/html/vendor/friendsofphp/php-cs-fixer/src/Tokenizer/Tokens.php:314
 PhpCsFixer\Tokenizer\Tokens->offsetSet() at /var/www/html/vendor/friendsofphp/php-cs-fixer/src/Tokenizer/Tokens.php:937
 PhpCsFixer\Tokenizer\Tokens->clearAt() at /var/www/html/vendor/friendsofphp/php-cs-fixer/src/Tokenizer/Tokens.php:1147
 PhpCsFixer\Tokenizer\Tokens->clearRange() at /var/www/html/src/Eccube/Service/EntityProxyService.php:301
 Eccube\Service\EntityProxyService->removeClassExistsBlock() at /var/www/html/src/Eccube/Service/EntityProxyService.php:84
 Eccube\Service\EntityProxyService->generate() at /var/www/html/src/Eccube/Command/GenerateProxyCommand.php:60
 Eccube\Command\GenerateProxyCommand->execute() at /var/www/html/vendor/symfony/console/Command/Command.php:255
 Symfony\Component\Console\Command\Command->run() at /var/www/html/vendor/symfony/console/Application.php:1005
 Symfony\Component\Console\Application->doRunCommand() at /var/www/html/vendor/symfony/framework-bundle/Console/Application.php:86
 Symfony\Bundle\FrameworkBundle\Console\Application->doRunCommand() at /var/www/html/vendor/symfony/console/Application.php:255
 Symfony\Component\Console\Application->doRun() at /var/www/html/vendor/symfony/framework-bundle/Console/Application.php:74
 Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /var/www/html/vendor/symfony/console/Application.php:148
 Symfony\Component\Console\Application->run() at /var/www/html/bin/console:37
```

何がおきてるかと言うと、proxy生成する際にphp-cs-fixer使ってコードを整形してるんだけど、そこでうまく整形できずにエラーになってる。たぶんEC-CUBE側のバグ

## php-cs-fixerをアップデート

```shell
composer update friendsofphp/php-cs-fixer
```
今回はここで

```shell
Compile Error: Declaration of EntityManager_9a5be93::getProxyInitializer()   
must be compatible with ProxyManager\Proxy\LazyLoadingInterface::getProxyIn  
itializer(): ?Closure 
```
が出てもう一手間増えた...

## 手動でfixする
proxyの中のEntityにTrait追加して、まとめてfixしようとしてコケてるので、proxy生成前に手動でそれぞれfixする。

```shell
./vendor/bin/php-cs-fixer fix ./app/proxy/entity
./vendor/bin/php-cs-fixer fix ./app/Plugin
```
プラグイン指定した方が良いかも。 面倒ならプラグインディレクトリまるっとfix

## 再度プロキシ生成
```shell
./bin/console eccube:generate:proxies
```

無事、プロキシ生成、スキーマの更新できました。
関数内でglobal宣言した変数にimportすると他の関数内でも当該変数を使うことができる

```py
def f():
  global sys
  import sys

def g():
  print(sys)

f()
g()
```

```
<module 'sys' (built-in)>
```

カッコ内こそ若干の差異はありますが、moduleと表示されることはCPython/PyPy/Jython/IronPython/Nuitka/Grumpyで確認。
https://docs.python.org/3/reference/simple_stmts.html#grammar-token-global-stmt [^1] には、

```
Names listed in a global statement must not be defined as formal parameters or in a for loop control target, class definition, function definition, import statement, or variable annotation.

CPython implementation detail: The current implementation does not enforce some of these restrictions, but programs should not abuse this freedom, as future implementations may enforce them or silently change the meaning of the program.
```

とありますが、多数の処理系で動作しているので、大丈夫なんだと思います。

これだけだとメリットが皆無ですが、例えば、`g()`がテスト関数で`f()`が(sys.pathを弄ることを含む)複雑なfixtureであるといったことができると思います。

[^1]: このリンク自体は https://stackoverflow.com/questions/11990556/how-to-make-global-imports-from-a-function/39941236 で見つけた
【AWS　最初に知っておきたい単語　VPC編】




**1.VPC　（バーチャルプライベートクラウド）**
・AWSアカウント内に構築できる仮想ネットワーク。AWSで作ったものを繋げるサービス。例えるなら、自分専用のバーチャルな土地！


**２.AZ　（アベイラビリティーゾーン）**
・地理的に独立したデータセンター。AZを束ねた地域のことをリージョンと呼ぶ。AZ・リージョンが一極集中していないのは、災害やトラブルが起こった時にサービス停止のリスクを減らすため。例えるなら、自分専用のバーチャルな土地が保管されている所。

**3.Subnet　（サブネット）**
・其の名の通り、サブのネットワーク。部署を分けるようなニュアンス。（これは自社内でするやつ、あれはお客さんに提供するやつ）、無駄な通信を減らし、セキュリティも向上する。例えるなら、専用のバーチャルな土地を使い方によって切り分けた土地のひとつ。

**4.Route Table　（ルートテーブル）**
・ネットワークの経路を設定するコンポーネント。サブネットに関連づけて使用する。例えるなら、専用のバーチャルな土地に行くまでの地図。

**5.Internet gateway　（インターネットゲートウェイ）**
・VPC内のAWSリソースとインターネットを繋げる。負荷の具合で自動でスケールしてくれる。
例えるなら、専用のバーチャルな土地をユーザーが行き来できるための玄関。


**６.Security group （セキュリティグループ）**
・AWSの仮想ファイアーウォールサービス。例えるなら、専用のバーチャルな土地を行き来を見張る門番。許可がないと進めない。


引用：AWSを体型的に学べる格安オンラインスクール　「AWS CloudTech」　VPC講座
URL：https://aws-cloud-tech.com/
PagespeedのLCPとTTIが遅い場合は font awesome を疑え

# このfontawesomeの記述を削除でスコア上昇
javascriptでfontawesomeのアイコンを読み込んでいる。

これがかなりスコアに悪影響を与えていたようだ。

```javascript
<script defer src="https://use.fontawesome.com/releases/v5.7.2/js/all.js" integrity="sha384-0+hogehogeWMzSS+hogehoge/5++hogehoge" crossorigin="anonymous"></script>
```

## SVG画像をインラインで読み込むように変更
実際には削除ではなく、SVG画像をインラインで読み込むように変更した

コードはこちらを参考にした。
https://coderwall.com/p/d1vplg/embedding-and-styling-inline-svg-documents-with-css-in-rails

```ruby:SVGのインライン読み込みをapplication_helperに実装
def embedded_svg(filename, options = {})
  file = File.read(Rails.root.join('app', 'assets', 'images', filename))
  doc = Nokogiri::HTML::DocumentFragment.parse file
  svg = doc.at_css 'svg'
  svg['class'] = options[:class] if options[:class].present?
  doc.to_html.html_safe
end
```


```erb:アイコンの読み込み
 <%= embedded_svg("common/font-awesome-icons/check-square.svg", class: 'check-square') %>
```

## fontawesomeの読み込み速度
ネットワークタブで見る限り全然負荷は高く見えない。
230msで読み込みが完了しているのでスコアに大きな影響を与えていそうではない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/3c53b4f0-94ad-9c0f-9f05-da6322959941.png)

## LCPとTTI
TTIとLCPはネットワークタブを見てもわからない、実際にレンダリングが完了したタイミング
ユーザーが操作可能になった状態とGoogleが判断したタイミングがスコアになるからだ。

特に後者のTTIは詳細が不明だ。

しかし今回スコアが上がったところを見ると、フォントやアイコンの読み込み完了タイミングまで見ていることは分かった。
もしくは font awesome の js の実行完了とともにアイコンが表示されるため
このjsが実行されないとユーザーはなにも操作可能にならないとGoogleが判断した可能性が高い。

## fontawesomeを消したらスコア爆上がりした。
TTIとLCPのスコアが大きく改善。

### 変更前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/759988c1-316f-f72c-7329-ac1b078b33ef.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/f030ea83-b5c9-81e0-5592-dd2eab42c55b.png)


### 変更後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69647/162b86c3-af87-2a25-b33f-b9056f1f63b4.png)
Pythonで月初、月末の平日の年月日を取得する

## 概要
月初、月末の平日の年月日を取得するサンプルコード。つまり土日、祝日でない月初日、月末日を取得する。
日本の祝日判定には、[jpholiday](https://pypi.org/project/jpholiday/)を使う。
slackなどでの月初、月末の社内通知などにどうぞ。

## 環境
- Python 3.7
- jpholiday==0.1.5
- pytz==2021.1

## サンプルコード

```python
import jpholiday
import datetime
import pytz
import calendar


# 今日の%Y-%m-%d
def fetch_today() -> datetime:
    return datetime.datetime.now(pytz.timezone('Asia/Tokyo')).date()

# 土日、祝日かどうか
def is_holiday(day: datetime) -> bool:
    # day.weekday() => 0=月曜日...5=土曜日、6=日曜日
    return day.weekday() >= 5 or jpholiday.is_holiday(day)

# 月の初日を取得
def first_day(today: datetime) -> datetime:
    return today.replace(day=1)

# 月の末日を取得
def last_day(year, month: datetime) -> datetime:
    day =  calendar.monthrange(year, month)[1]
    return datetime.datetime.strptime(f'{year}-{month}-{day}', '%Y-%m-%d').date()

# 月の平日の初日を取得
def fetch_first_bizdate(day) -> datetime:
    first_date = first_day(day)
    while is_holiday(first_date):
        first_date = first_date + datetime.timedelta(days=1)

    return first_date

# 月の平日の末日を取得
def fetch_last_bizdate(year, month) -> datetime:
    last_date = last_day(year, month)
    while is_holiday(last_date):
        last_date = last_date - datetime.timedelta(days=1)

    return last_date

if __name__ == '__main__':
  # 今日
  today = fetch_today()
  # 月の平日の初日
  first_bizdate = fetch_first_bizdate(today)
  # 月の平日の末日
  last_bizdate = fetch_last_bizdate(today.year, today.month)

  print(f'today: {today}')
  print(f'last_bizdate: {last_bizdate}')
  print(f'first_bizdate: {first_bizdate}')

  # 今日は月の平日の初日 or 末日かどうか
  today in [first_bizdate, last_bizdate]
```
RMD-X8をM5 Stackで動かす(4)

前回までで，基本的な使い方は全て確認しました．
今回は，クラスを使ってRMD-X8を簡単に制御する方法を確認しましょう．

# RMD-X8_M5クラス

https://github.com/bump5236/RMDx8Arduino
をもとに，M5 Stack用に改良したクラスが`RMD-X8_M5`クラスです．
RMD-X8を制御するために必要なメンバ変数と，メソッドが便利な形で用意されています．

## メンバ
|  名前  |  型  |  説明  |
| ---- | ---- | ---- |
| `posKp`	        | uint8_t	| 位置制御のPゲイン	|
| `posKi`	        | uint8_t	| 位置制御のIゲイン	|
| `velKp`	        | uint8_t	| 速度制御のPゲイン	|
| `velKi`	        | uint8_t	| 速度制御のIゲイン	|
| `curKp`	        | uint8_t	| 電流制御のPゲイン	|
| `curKi`	        | uint8_t	| 電流制御のIゲイン	|
| `MOTOR_ADDRESS` | uint16_t	| モーターのアドレス	|
| `tempreture`	| int8_t	| 読み取ったモーターの温度を保持しておく	|
| `present_current` | int16_t	| 読み取った現在の電流値を保持しておく	|
| `present_velocity` | int16_t	| 読み取った現在の速度を保持しておく	|
| `present_pos` | int64_t	| 読み取った現在の位置を保持しておく	|
| `present_angle` | int32_t	| 読み取った現在の角度を保持しておく	|

## メソッド
|  名前  |  型  |
| ---- | ---- |
| `canSetup`	        | CANの接続を確立する．`Setup()`内で使用	|
| `readPosition()`	        | 現在の位置を取得	|
| `writeCurrent(int16_t current)`	        | 電流値を指定し，電流制御	|
| `writePosition(int32_t position)`	        | 位置を指定し，位置制御 |
| `stop()`	        | モーターのトルクをOFFにする |


他にも使用頻度の低いものが用意されています．必要に応じて`RMDX8_M5.h`や`RMDX8_M5.cpp`を読んでみましょう．

# RMD-X8クラスを使用する準備

https://github.com/t-kamimura/RMDx8_m5stack に`RMD-X8_M5`フォルダがあります．これを`documents/Arduino/Libraries`フォルダにコピーしましょう．

# RMD-X8クラスを用いたプログラム

サンプルプログラムは`RMD_torsionSpringTest`です．モーターの電流制御によって，回転バネのように振る舞わせるプログラムです．
詳細を見ていきましょう．

```c
#include <RMDX8_M5.h>
```
`RMDX8_M5`クラスをインクルードして使えるようにします．

```c
RMDX8_M5 myMotor(CAN0, MOTOR_ADDRESS);
```
CANとモーターのアドレスを指定し，`RMD-X8_M5`クラスから`myMotor`インスタンスを生成します．
**クラス**と**インスタンスの**関係については，「MATLAB講習　オブジェクト指向のみ」のビデオ https://youtu.be/G4RlJvAidy8 を参照してください．

```c
myMotor.readPosition();
initial_pos = myMotor.present_angle;
```
`Setup()`内では，`readPosition`メソッドを利用してモーターが動き始める前の位置を取得しています．これは開始位置をバネの釣り合い位置とするために行います．
開始位置を目標位置とすることで，**プログラムの開始時に大きなトルクが加えられることなく，安全に実験を行う**ことができます．

```c
// read multi turn angle
previous_pos = myMotor.present_angle;
myMotor.readPosition();
delay(1);

present_vel = (myMotor.present_angle - previous_pos)*1000/LOOPTIME;
int32_t target_cur = - KP*(myMotor.present_angle - initial_pos) - KD * present_vel;

myMotor.writeCurrent(target_cur);
delay(1);
```
`loop()`内では，現在の位置と速度を計算し，そこからPD制御によって電流の指令値`target_cur`を決定しています．これは前回と同じです．
電流の指令値が決定したら，`writeCurrent`メソッドで電流指令を行います．

```c
// finishing
myMotor.writePosition(0);
delay(2000);
// stop command
myMotor.stop();
delay(500);
SERIAL.println("Program finish!");
```
終了処理として，モーター位置をゼロに戻し，`stop`メソッドを使用してモーターのトルクを切ります．

# まとめ

以上で，RMD-X8をM5 Stackで使用するためのチュートリアルは終了です．
クラスを使いこなせば様々なプログラムが簡単に作れますので，いろいろ試してみましょう．
連番は、圧縮率が高い？

# ソートと圧縮の関係が面白いなと思ってのポエムです。
馬鹿がくだらないことを書いていると思う人がほとんだと思いますが、そうでない人もいるかもしれないと思ってのポエムです。

## 連番の圧縮は、どこまでいけるか？　かなり大きな連番でも、20バイトあれば十分では

1000 の連番のCSV(?) を圧縮するとどうなるでしょうか？

```
n=1000
f = open('1000.txt','w')
for i in range(1000):
   f.write(str(i) + '\n')
f.close()
```

|状態|サイズ |
|--|--|
|そのまま|**4.77KB**|
|zip圧縮|**1.90 KB**|
|数式|**20バイト？**|

でも、連番って、1000　の連番なんで、1000の連番　と書けば、論理的には等価ではありませんか？
連番の圧縮は、アルゴリズムで表現していいなら**20バイト** ぐらい十分なはず。


## とりあえず、もう少しましな話　ax + b だとどうなるのだろうか？

```
a = 2**4096
b = 2**32768
f = open('ab_1000.txt','w')
for i in range(1000):
   f.write(str(a*i + b) + '\n')
f.close()
```
**n=1000**

|状態|サイズ |
|--|--|
|そのまま|**9.40MB**|
|zip圧縮|**669KB**|
|数式|**12KBぐらい** |

数式は、len(str(a)) + len(str(b)) が、11099　なんで、12kb 程度　圧縮率かなりよくありませんか？

**n=10000**

|状態|サイズ |
|--|--|
|そのまま|**94.0MB**|
|zip圧縮|**6.50MB**|
|数式|**12KBぐらい** |


# 回帰式をつくってみたらどうなるだろう。面倒だからやらないけど
以上は、人工的なデータなんで当たり前だと思われるでしょうけど、
実際にあるデータをソートして、回帰式をつくり　データごとに補正の配列で面倒みてあげたら、圧縮率はどうなるのだろう
ax + b + 補正[x]（ここは配列になりますね。）　だけど、十分に意味ある圧縮になるのではないだろうか？
**残念** 回帰式で、一般的に連番圧縮なみにするのは難しいみたいです。ソートされいているという状態のみでなんとかがんばりたいのですが、差分とると少し圧縮されるねぐらいみたいです。ただ、、もうちょいこの件は調べ見るといいかもしれません。


# 画像ファイルだって、バイナリでみれば自然数になりますね。
- ソートはファイルサイズでいいのでソートは簡単にできますね。


複数の大きな自然数が与えられているときに、どうやって圧縮するといいかという課題です。
複数の自然数は、N[0]....N[1]としましょう。自然数の個数は、m とします。
このとき、巨大な自然数の集合があって x[0]...x[k-1] k =< m があって、
N[i] = a[i] * x[どれか] + b[i] で表現できるとします。a,b も自然数ですが、N や x に比べるとぐぐっと小さいとします。参照テーブル LUT として、x をもっていいれば、各行は、a,b と x を指定する自然数　だけで指定可能になります。x が一個でオッケーなら、かなり圧縮率がよいことになります。圧縮的に意味ある x の集合が存在するかが問題です。例えば、100 個　超巨大な自然数のデータがありますが、x が一個で ax + b で表現できるなら、かなり高い圧縮率がありえます。どうやって x を見つけるかは壊滅的に難しいですが、x をデータ依存のLUT にするわけです。スマートなオーバーフィッティングをしているともいえます。a,b,c をどうやって選ぶかは不明ですし、理論的にも正しいかどうかも不明です。



# 何かおかしいと思うでしょう。でもアルゴリズム的には可能みたいですね。どこがポイントなんでしょうか？
Git 特殊文字が含まれるリポジトリをチェックアウトする方法

# 目的

- 特殊な記号文字がふくまれるリポジトリをチェックアウトする方法をまとめる

# 特殊文字

- 特殊文字を下記に記載する。
    - `!、#、$、&、'、(、)、*、+、,、/、:、;、=、?、@、[、]`

# 方法

- 例えば`#test`というブランチをcheckoutする際のコマンドを下記に記載する。（※リモートリポジトリは`origin`とする。）

    ```terminal
    $ git checkout -b \#test origin/\#test
    ```

- 特殊文字列の前に`\`を置く事により特殊文字が通常の文字列として扱われる。
- `##test`というブランチ名の場合、下記のようにcheckoutするコマンドを記載する。

    ```terminal
    $ git checkout -b \#\#test origin/\#\#test
    ```

- 特殊文字の個数だけ`\`を置く。
[UE4] ラムダ式の基本的な書き方

#【概要】
C++を書いていると、関数として定義する程でも無いが、普通に書くとコピペになってしまうような処理が出てくることがあります。
そういった場合、ラムダ式を使うことによって実装をスッキリさせることが出来ます。
よく書き方を忘れてしまうので、備忘録として書き残します。

#【書き方】
UE4ではTFunctionRefを使い、以下のように記述します。
※std::functionでもTFunctionでも可。違いについては参考サイトを参照。

```
const int32 Value1 = 10;
//定義
TFunctionRef<float(const int32)> TestLambda = [&Value1](const int32 Value2)
{ 
	return static_cast<float>(Value1 + Value2);
};
//呼び出し
float Result = TestLambda(1);
```

それぞれの項目の意味は以下のようになっています。

```
TFunctionRef<戻り値の型(引数の型)> TestLambda = [キャプチャリスト(ラムダ式の外から持ってきたい変数)](引数の変数定義)
```
キャプチャリストの詳しい記法に関しては、こちらのサイト様を参考にさせていただきました。
https://cpprefjp.github.io/lang/cpp11/lambda_expressions.html

#【参考サイト】
こちらのサイト様を参考にさせていただきました。
https://usagi.hatenablog.jp/entry/2017/12/01/ac_ue4_2_p5
https://cpprefjp.github.io/lang/cpp11/lambda_expressions.html
自分が使っている、表示される月を自動で変更してくれるようにする設定　Power BI

#　常に前月のデータを表示したい
僕がPower BI王子の記事に出会ったのはこれがきっかけでした
他のプロジェクトでも使いまわせるようにとにかくコードだけ備忘のために記載します。
特にグローバル（＋日本）で使えるようにカラム名を英語に変えるなど簡単にですが手を加えています。
日本語のほうが必要など、詳しくは元記事をご覧ください。

https://qiita.com/yugoes1021/items/339e41277d2502431b73

```
Calendar = 
var BaseCalendar = CALENDAR( MIN( Table名[Column名]), MAX( Table名[Column名] ) )
RETURN
    GENERATE(
        BaseCalendar,
        var BaseDate = [Date]
        var YearNumber = YEAR(BaseDate)
        var MonthNumber = MONTH(BaseDate)
        var DateNumber = DAY(BaseDate)
        var DayNumber = WEEKDAY(BaseDate, 1)

        RETURN ROW(
            "Year", YearNumber,
            "Year_JA", FORMAT(BaseDate, "yyyy年"),
            "Month_NUM", MonthNumber,
            "Month_JA", FORMAT(BaseDate, "mm月"),
            "Month_EN", SWITCH(MonthNumber, 1, "Jan", 2, "Feb", 3, "Mar", 4, "Apr", 5, "May", 6, "Jun", 7, "Jul", 8, "Aug", 9, "Sep", 10, "Oct", 11, "Nov", 12, "Dec", ""),
            "YearMonth_NUM", FORMAT(BaseDate, "yyyymm"),
            "YearMonth_JA", FORMAT(BaseDate, "yyyy年mm月"),
            "Day", DateNumber,
            "DayofWeek_JA", SWITCH(DayNumber, 1, "日", 2, "月", 3, "火", 4, "水", 5, "木", 6, "金", 7, "土",""),
            "DayofWeek_EN", SWITCH(DayNumber, 1, "SUN", 2, "MON", 3, "TUE", 4, "WED", 5, "THU", 6, "FRI", 7, "SAT",""),
            "Weeks", FORMAT(WEEKNUM(BaseDate, 1), "00th"),
            "Diff_year", DATEDIFF(TODAY(), BaseDate, YEAR),
            "Diff_month", DATEDIFF(TODAY(), BaseDate, MONTH)
        )
    )

```

上のコードでモデリングしたあとにカラム追加する際に使用するのは

```
"Month_filter" = IF( [Diff_month] = -1, "Last month", [YearMonth_NUM] ) 
```

```
"Fiscal_year" = if([Month_NUM]>3,[Year],[Year]-1)
```

```
"Year_filter" = IF( [Diff_year] = 0, "This FY", [Year] ) 
```
yum からレスポンスが全くなくなる

# 事象
`yum` で何のコマンドを叩いてもレスポンスを返さず、 Ctrl+C も効かなくなるときがある。
この場合、 verbose (`-v` オプション) しても何も出ない。

# 解消方法

## ほかにハングしてる yum プロセスを全部やっつける

`$ ps aux|grep yum`

`$ kill -9 [PID]`

ref: https://www.linux.com/training-tutorials/how-kill-process-command-line/

## yum のキャッシュファイルをすべて手動削除
この事象の場合 `yum clean all` もハングしてしまうので、手動でファイルを消しちゃいます。

`rm -rf /var/cache/yum`

## rpm のキャッシュDBファイルをすべて削除

`rm -rf /var/lib/rpm/__db*`

ref: https://community.oracle.com/tech/apps-infra/discussion/4179725/yum-install-hangs

# いけるはず

ここまで来たら動くはず
JSONからdata class をらくらく作成

## 概要

APIを叩いてJSONを受け取る際、data classで予め型を決めると思います。
その作業を楽にするプラグインを使っているので紹介。

## 使い方

Android StudioのPluginからこれをDLしてください。
![Screen Shot 2021-05-21 at 15.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/4f6e09f6-8959-69ed-1885-cbbf477fe236.png)

Code からGenerateを選択
![Screen Shot 2021-05-21 at 15.09.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/21a641ca-706b-affb-9df3-7858a1c3aeef.png)

Kotlin data classes from JSONを選択
![Screen Shot 2021-05-21 at 15.09.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/4d4e83fa-5216-cf38-5936-0414c3fc9888.png)

JSONを貼り付け
![Screen Shot 2021-05-21 at 15.13.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/54ca7fc4-d89e-025a-bc0e-e5b0ce97ca6d.png)

val or var, Nullableとかも設定できます
![Screen Shot 2021-05-21 at 15.10.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/58668d73-1012-72e2-5b8a-983ed7cf17a3.png)
![Screen Shot 2021-05-21 at 15.15.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/5fe09010-4b1d-654e-70bd-91fa4bbc9fc6.png)

Data class が出来上がりました！楽ですね。

[Github プラグインのソース](https://github.com/wuseal/JsonToKotlinClass)
【DataSpider(Dr.Sum Connect)】当月データ入替え（当月データのみ削除し、当月データを新たにインポート）

#目標
ELTツールDataSpider Servista（OEM提供でConnectと呼ばれてることもある）で、以下を行う。
★前提：テーブルA内の一部データを、テーブルBに日々貯めてるイメージ
(1)本日日付から当月を取得
(2)テーブルBから当月データを削除
(3)テーブルBにテーブルAの当月データをインポート

#前提
書き込み先はDr.Sumのテーブルです。

#経緯
テーブルAには日々データが増える。テーブルAはデータ量が多いので、テーブルBの全削除⇒全インポートはやりたくない。このような、当月データ単位で入れ替えたいことがよく発生する。
インポート対象データが増えるパターンの場合、全削除⇒全インポートは始めはいいが時間の経過に伴いメモリ不足エラー発生の可能性が高まるぅ。

#手順
####今回はこの部分の説明になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/474d48ad-11a3-001c-55fd-db3383b524a3.png)

####スクリプト変数（P_現在月）を作成。
※このあと、この変数に「現在月」を保持します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/ee0b9152-9c18-9d1a-e06d-942187bab570.png)

####現在月取得
「変数代入」を使います。
右の枠内から真ん中の広いエリアにドラッグしてきてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/21df2801-c7cf-3cb0-3122-e8300eb1102d.png)

####variable(変数代入のアダプタ）を開く
variableをダブルクリックしてください。このあと、緑のエリアに作っていきますよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/6c7a72d6-5f2f-ac98-9ebc-d47248ac73d3.png)


####まず、右のツールパレットから、「現在日時」をもってきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/d2d40cdd-dcba-2046-f1bc-35451627285e.png)

####次に、現在日時から年月（yyyy/MM）だけ欲しいので「日付フォーマッティング」で抜き出す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/70efbba6-157f-3cc1-ef97-f5d9a827cbbd.png)
この例では「yyyy/MM」になってますが、検索するデータが「202104」の形であれば「yyyyMM」でOKですよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/6ccd7efc-06c8-7724-6933-3b2ae9463918.png)

####「日付フォーマッティング」から右側の「P_現在月」まで線をひっぱりましょう。
これで「P_現在月」に今日の年月が入ります。2021/05 みたいなかんじです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/d906b5e4-d7e3-450b-6789-aba8a079f2e8.png)

####テーブルから当月データを削除する
変数は `${変数名}` で使えます。
注！変数が文字列型の時は'(シングルクオーテーション）で囲むことを忘れずに！
以下例でも' 'で囲んでいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/07d16e1f-ac51-2382-67dc-1d1ec7fdd7fa.png)

####当月データを抽出する
前述のデータ削除と同じノリです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/6a5d42de-fbb0-679f-9c22-d5c4a6ef09ba.png)

####このあとは
ご自分の環境の書き込み先に合わせて選んでくださいね。
私の場合はDr.Sumテーブルなのでこのへん。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278265/394e9ed2-8d5f-fc11-de1b-171bd01753c8.png)

#つぶやき
Connect使い始めた当初、部品（アダプタって呼ばれてるのかな、ツールパレットから選ぶ一個一個のパーツみたいなやつ）はどこにあるのか？で悩むことが多かったです。。
アダプタを置いて、クリックし選択した状態でF1をクリックするとそのアダプタのヘルプが立ち上がって便利ですよ！

以上
【Python】RTXのリリースノートから情報を自動取得してCSVに書き出す

<h2>このプログラムの趣旨</h2>
お客様へYamahaのルータであるRTXシリーズのリリース情報をお客様に提供する機会があり、毎回コピペしてエクセルに貼り付けるのは非効率だと思って自動化してみた。

今回はSeleniumのWrapperであるHeliumというものを使用。
~~~Heliumっぽいことあんまりしてないけど。~~~
※一部Seleniumの機能を呼び出して使用

[Python : Heliumを使用したブラウザ操作
](https://zenn.dev/nakaie/articles/3ab2c36dcc12cb "Python : Heliumを使用したブラウザ操作")

<h2>プログラム処理のざっくり概要</h2>
・あるバージョンのリリースノートのURLをプロンプトから受け取る
・受け取ったURLへアクセス
・バグ情報内のli要素を取得して、テキスト情報をリスト化
・リストの中身をCSVへ出力

<h2>プログラムの中身</h2>
```RTX_Bug_Fixed_Info.py
from helium import *
import csv

# バージョン情報の入力をユーザーに促す
version_info = input("該当のバージョンを入力してください：")

# リリースノートURL入力をユーザーに促す
target_url = input("該当バージョンのリリースノートのURLを入力してください：")

# バグ修正情報内のli要素のXPATH入力をユーザーに促す
bug_xpath = input("バグ修正情報箇所のXPATHを入力してください：")

# Chromeをヘッドレスモードで起動
# Seleniumの機能を呼び出すために、一旦変数に格納
driver = start_chrome(target_url, headless=True)
driver

# CSV書き込みの準備
file_name = version_info + "_bug_fixed_info.csv"
with open(file_name,'a', newline='') as result_csv:
    writer = csv.writer(result_csv, lineterminator='\n')

    # バグ修正情報記載箇所からli要素を全て取得しリスト化
    bug_fix_info_cells = driver.find_elements_by_xpath(bug_xpath)

    # li要素からテキスト情報を取得しリスト化
    bug_fix_info = [_bug_fix_info.text for _bug_fix_info in bug_fix_info_cells]

    # CSV書き込み時のインデックス番号準備
    index_num = 1

    # 上記リストを1行ずつCSVへ書き込み
    for info in bug_fix_info:
        writer.writerow([index_num, info])
        index_num +=1

print(file_name + "への書き込みが完了しました。")
```

XPATHの入力をユーザーに促すのは、ユーザビリティ的にはよくない。
しかしながら、YamahaのリリースノートのHTML構造が毎度異なるので致し方なく。。。

バグ修正情報の一覧を取得する場合、まずはli要素を右クリックしてFULL XPATHをコピーする

```
/html/body/ol[3]/li[1]
```

そして、li[1]の[ ]部分を削除する。

```
/html/body/ol[3]/li
```

上記をプロンプト画面で入力すれば情報一覧を取得可能になる。

できあがったCSVファイルは以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316676/591cbbe8-a2ef-e8c3-a66e-8a84e2eb3e1e.png)

そのままだと見にくいので、適宜幅の調整などをしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316676/d4e13552-6d3f-3793-c4d9-3e2ea81cd597.png)

一部つまって作成に数時間かかってしまったけど、今後のコピペ作業がだいぶ削減できたので問題なし。