OmniAICreator/Qiita-1.07M huggingface dataset:
ラズパイでOMRONのPLCのメモリの値をロギング

# OMRONのFINSコマンドを使ってPLCのメモリをロギング
ラズパイで定周期でOMRON PLCのメモリを読み出してファイルに保存します
(Windowsでも動作します)

https://github.com/OkitaSystemDesign/memlog/tree/main/OMRON

## 環境
Python3.9  
PLC OMRON CJ/NJ/NX series

#### ディレクトリ構成
```
.
├─ finsudp.py
├─ memlogFINS.py
└─ log
   └ ログファイル
```
## 実行例
```bash
~ $ python3 memlogFINS
```
## ファンクション
### memlogFINS(interval, recnum, logpath)

##### interval : 収集周期
収集する周期を入力します 単位は秒  
例：0.1

##### recnum : 収集個数
設定した個数を収集するとプログラムを終了してファイルを閉じます

##### logpath : 収集ファイル出力ディレクトリ
ロギングするファイルの出力先です
ここで指定したディレクトリの下にファイル名は"logYYYYMMDD_HHSS.csv"で出力します

### logwrite(filename)
メモリの値を読み出してcsvファイルに出力します

##### filename
出力先のファイル名

## 収集するメモリの設定方法
### まずPLCのD0から20ワードにロギングするデータを下表のように揃えてあると想定します

| D0,1 | 2 | 3 | 4 | 5,6 | 7,8 | 9,10 | 11,12 | 13.14,15,16 | 17,18,19 |  
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |  
| BitData | INT16 | INT16 | UINT16 | INT32 | INT32 | UINT32 | FLOAT | DOUBLE | STRING |

### インスタンス作成
```python
plc = finsudp.fins(host, srcFinsAdr, dstFinsAdr)
```
### 読み出すメモリのアドレスはlogwrite()のここで指定します
```python
data = plc.read('D0', 20)           # 読み出すメモリのアドレスとワード数
```

### 次に書き込むデータを順番に作っていきます  
最初の列には読み出した日付時刻
```python
now = datetime.now()
f.write(now.strftime('%Y/%m/%d %H:%M:%S.') + "%03d" % (now.microsecond // 1000) + ',')
```

次はビットデータ32ビット (0-3 byte目)
```
f.write(','.join(list(plc.WordToBin(data[:4]))) + ',')
```

次はINT16データ2個 (4-7 byte目)
```python
data16 = plc.toInt16(data[4:8])
datastr = [str(n) for n  in data16]
f.write(','.join(datastr) + ',')
```

次はUINT16データ1個 (8-9 byte目)
```python
data16 = plc.toUInt16(data[8:10])
datastr = [str(n) for n  in data16]
f.write(','.join(datastr) + ',')
```

次はINT32データ2個(10-18 byte目)
```python
data32 = plc.toInt32(data[10:18])
datastr = [str(n) for n  in data32]
f.write(','.join(datastr) + ',')
```

次はUINT32データ1個 (18-21 byte目)        
```python
data32 = plc.toUInt32(data[18:22])
datastr = [str(n) for n  in data32]
f.write(','.join(datastr) + ',')
```

次は単精度浮動小数点1個 (22-25 byte目)
```python
dataFloat = plc.toFloat(data[22:26])
datastr = [str(n) for n  in dataFloat]
f.write(','.join(datastr) + ',')
```

次は倍精度浮動小数点1個 (26-33 byte目)
```python
dataDouble = plc.toDouble(data[26:34])
datastr = [str(n) for n  in dataDouble]
f.write(','.join(datastr) + ',')
```

最後に文字列6文字 (34-39 byte目)
```python
dataStr = plc.toString(data[34:40])
f.write(dataStr + '\n')  
```
最後には'\n'で改行を入れます

### 出力データ例
```
2022/05/21 11:24:08.342,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,341,351,33795,-286250,161303021,100444,34.200050354003906,37.24000000000026,ABC341
2022/05/21 11:24:08.445,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,341,351,33795,-286250,161303021,100444,34.200050354003906,37.24000000000026,ABC341
2022/05/21 11:24:08.542,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,341,351,33795,-286250,161303021,100444,34.200050354003906,37.24000000000026,ABC341
2022/05/21 11:24:08.642,0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,341,351,33795,-286250,161303021,100444,34.200050354003906,37.24000000000026,ABC341
```
ラズパイで三菱シーケンサのデバイスの値をロギング

# 三菱のMCプロトコルでデバイスの値をロギング
ラズパイで定周期で三菱シーケンサのデバイスを読み出してファイルに保存します
（Windowsでも動作します）

https://github.com/OkitaSystemDesign/memlog/tree/main/MITSUBISHI

## 環境
Python3.9  
Frame3E UDP/IP connection

#### ディレクトリ構成
```
.
├─ mcp.py
├─ memlogMCP.py
└─ log
   └ ログファイル
```
## 実行例
```
~ $ python3 memlogMCP
```
## ファンクション
### memlogMCP(interval, recnum, logpath)
定周期でlogwriteを実行します

#### interval : 収集周期
収集する周期を入力します 単位は秒  
例：0.1

#### recnum : 収集個数
設定した個数を収集するとプログラムを終了してファイルを閉じます

#### logpath : 収集ファイル出力ディレクトリ
ロギングするファイルの出力先です
ここで指定したディレクトリの下にファイル名は"logYYYYMMDD_HHSS.csv"で出力します

### logwrite(filename)
デバイスの値を読み出してcsvファイルに出力します

#### filename
出力先のファイル名

## 収集するデバイスの設定方法
### まずシーケンサのD0から20ワードにロギングするデータを下表のように揃えてあると想定します

| D0,1 | 2 | 3 | 4 | 5,6 | 7,8 | 9,10 | 11,12 | 13.14,15,16 | 17,18,19 |  
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |  
| BitData | INT16 | INT16 | UINT16 | INT32 | INT32 | UINT32 | FLOAT | DOUBLE | STRING |


### 読み出すデバイスのアドレスはlogwrite()のここで指定します
```
data = plc.read('D0', 20)           # 読み出すデバイスのアドレスとワード数
```

### 次に書き込むデータを順番に作っていきます  
最初の列には読み出した日付時刻
```python
now = datetime.now()
f.write(now.strftime('%Y/%m/%d %H:%M:%S.') + "%03d" % (now.microsecond // 1000) + ',')
```

次はビットデータ32ビット (0-3 byte目)
```python
dataWordToBin = plc.WordToBin(data[:4])
f.write(','.join(list(dataWordToBin.rjust(32,"0"))) + ',')
```

次はINT16データ2個 (4-7 byte目)
```python
data16 = plc.toInt16(data[4:8])
datastr = [str(n) for n  in data16]
f.write(','.join(datastr) + ',')
```

次はUINT16データ1個 (8-9 byte目)
```python
data16 = plc.toUInt16(data[8:10])
datastr = [str(n) for n  in data16]
f.write(','.join(datastr) + ',')
```

次はINT32データ2個(10-18 byte目)
```python
data32 = plc.toInt32(data[10:18])
datastr = [str(n) for n  in data32]
f.write(','.join(datastr) + ',')
```

次はUINT32データ1個 (18-21 byte目)        
```python
data32 = plc.toUInt32(data[18:22])
datastr = [str(n) for n  in data32]
f.write(','.join(datastr) + ',')
```

次は単精度浮動小数点1個 (22-25 byte目)
```python
dataFloat = plc.toFloat(data[22:26])
datastr = [str(n) for n  in dataFloat]
f.write(','.join(datastr) + ',')
```

次は倍精度浮動小数点1個 (26-33 byte目)
```python
dataDouble = plc.toDouble(data[26:34])
datastr = [str(n) for n  in dataDouble]
f.write(','.join(datastr) + ',')
```

最後に文字列6文字 (34-39 byte目)
```python
dataStr = plc.toString(data[34:40])
f.write(dataStr + '\n')  
```
最後には'\n'で改行を入れます

### 出力データ例
```
2022/05/20 17:05:07.803,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,-1,3,4,-5,7,268435455,0.10999999940395355,-0.13,ABCD12
2022/05/20 17:05:07.902,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,-1,3,4,-5,7,268435455,0.10999999940395355,-0.13,ABCD12
2022/05/20 17:05:08.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,-1,3,4,-5,7,268435455,0.10999999940395355,-0.13,ABCD12
2022/05/20 17:05:08.012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,-1,3,4,-5,7,268435455,0.10999999940395355,-0.13,ABCD12
```
【Python】Instagramで自動検索して、投稿のURLを取得する

# 初めに
　ことすたぐらむいいね機能の実装コードとなります。記事のまとめとして、以降の記事で、完成コードを記載したいと思います。

# 特定のキーワードで検索する
　Instagramdeはキーワード検索のurlが決まっており、以下のものになります。
```python:検索URL
https://www.instagram.com/explore/tags/{特定のキーワード}/
```
　こちらの検索urlを用いて検索を行います。検索には時間がかかるため、10秒ほどスリープしていますが、最終コードではWebdriverで実装したいと思います。
```python:instagram.py
    #特定のキーワードで検索を行い、最新の投稿10件のurlを取得
    def search(self, keyword):
        self.get('https://www.instagram.com/explore/tags/{}/'.format(keyword))
        sleep(10)
```
# 投稿のURLを取得する
　検索画面で投稿のリンクurlはInstagramでは以下のブロックに入っています。
```python:リンクのあるブロック
main->article->a
```
　したがって記事のaタグを取得するコードは以下になります。
```python:instagram.py
articles = self.find_element_by_tag_name('main').find_element_by_tag_name('article').find_elements_by_tag_name('a')
```
　リンクはaタグのhrefの値なので、そちらをfor文で回して取得しましょう。
```python:instagram.py
        links = []
        i=0
        for article in articles:
            links.append(article.get_attribute('href'))
            i = i+1
            if i==10:
                break
        return links
```
　これでリンクを10件上から取得できました。全体のソースは以下になります。
```python:instagram.py
    #特定のキーワードで検索を行い、最新の投稿10件のurlを取得
    def search(self, keyword):
        self.get('https://www.instagram.com/explore/tags/{}/'.format(keyword))
        sleep(10)#検索には時間がかかるため、ここはwebdriverを用いたい10秒でも結構ぎり
        articles = self.find_element_by_tag_name('main').find_element_by_tag_name('article').find_elements_by_tag_name('a')
        links = []
        i=0
        for article in articles:
            links.append(article.get_attribute('href'))
            i = i+1
            if i==10:
                break
        return links
```
# 終わりに
　seleniumは非常に便利なライブラリです。これからも使用機会があると思うので勉強していきます。
　現在はサーバー側のことすたぐらむを実装中です。こちらの方も後日記事を載せたいと思います。
　最後までご覧くださりありがとうございます。
　ご質問などございましたら、コメントよろしくお願いいたします。
【iOS, Swift】CIFilterで点描してみた

## 環境
* Xcode:13.3.1
* Swift:5.6

## 点描のソースコード例

点描するには、CIFilterの「CIPointillize」を使う

```swift
class SampleViewController: UIViewController {

    @IBOutlet weak var testDisplayImage: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // AssetsにDummyって名前の適当な画像を置く
        let dummyImage = UIImage(named: "Dummy")!
        let pointillizeImage = addPointillizeEffect(for: dummyImage)
        self.testDisplayImage.image = pointillizeImage
    }

    func addPointillizeEffect(for image: UIImage) -> UIImage {
        // どこらへんに描画するか（今回は中央に描画されるように設定）
        let center: CIVector = CIVector(x: self.testDisplayImage.bounds.midX, y: self.testDisplayImage.bounds.midY)
        // 丸の大きさ
        let radius = 40.0
        
        if let inputImage = image.toCIImage() {
            let filter: CIFilter = CIFilter(name: "CIPointillize",
                                            parameters: [kCIInputImageKey: inputImage,
                                                         kCIInputRadiusKey : radius,
                                                         kCIInputCenterKey : center])!
            // CIPointillizeになった画像が出力される
            if let outputImage = filter.outputImage {
                // CIContextを通してCGImageに変換
                let context = CIContext(options: nil)
                if let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
                    let filteredImage = UIImage(cgImage: cgImage)
                    return filteredImage
                }
            }
        }
        // 変換できなかったら元の画像を返す
        return image
    }
}

extension UIImage {
    
    func toCIImage() -> CIImage? {
        if let ciImage = self.ciImage {
            return ciImage
        }
        if let cgImage = self.cgImage {
            return CIImage(cgImage: cgImage)
        }
        return nil
    }
}
```

※ UIImageViewは、SafeAreaいっぱいになるような制約をつけてる

## 実行結果
* 左：結果
* 右：元々の画像

![IMG_0102.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/827233/e50d9731-3419-e32a-fffe-8a1cf55fcc9b.png) ![IMG_0101.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/827233/3cdc947c-c92b-3df3-729d-213b10da7fb8.png)


## 補足

* パラメータの設定

加工したい画像は、`kCIInputImageKey`で指定する。必須。

どこらへんに描画するかは、`kCIInputCenterKey`で指定する。
なにも設定しなければデフォルト値が設定される。

丸の大きさは、`kCIInputRadiusKey`で指定する。
なにも設定しなければデフォルト値が設定される。画像サイズに対しての丸の大きさ。

CIFilter.setValue(Any?, forKey: String) でもパラメータを設定できる

* リリース処理

古い記事だと、ContextとCGImageのリリース処理をしているが、Swift5でリリース処理を書くと、
`'CGImageRelease' is unavailable: Core Foundation objects are automatically memory managed `
というメッセージがでる。もうリリース処理はいらないのかも。


### 参考になった記事
* Appleのリファレンス

https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIPointillize

* UIImageのextensionについて
  * Assetsに置いてある画像からCIImageを取得しようとすると、CIImageがnilになってしまった。これを回避する方法を以下の記事を参考にした。

https://qiita.com/RyoWakabayashi/items/d3e954e83adad7f36719

* 実行結果からわかるとおり、もともとの画像より小さくなって出力されてしまう。
  * どうにかしたい時に参考になる。

https://qiita.com/imk2o/items/25209b4bc7449d85409f
標準正規分布の期待値、分散

## 前提確認
・確率密度関数から期待値、分散を導出することを目的とする
・その他の知識に詳しくは触れない

## 確率密度関数

X ~ N(0,1)に従うとき、確率変数Xの確率密度関数は、

```math
f_X(x)=\frac{1}{\sqrt{2\pi}}\exp(-\frac{x^2}{2})\qquad(-∞<x<∞)
```
である。

## 期待値導出

```math
\begin{eqnarray}
E[X] &=& \int_{-∞}^{∞}xf_X(x)dx \\
&=& \int_{-∞}^{∞}x\frac{1}{\sqrt{2\pi}}\exp(-\frac{x^2}{2})dx \\
&=& 0
\end{eqnarray}
```
奇関数なので期待値は0

## 分散導出

```math
V[X] = E[X^2] - {E[X]}^2
```
```math
\begin{eqnarray}
E[X^2] &=&  \int_{-∞}^{∞}x^2f_X(x)dx \\
&=& \int_{-∞}^{∞}x^2\frac{1}{\sqrt{2\pi}}\exp(-\frac{x^2}{2})dx \\
\end{eqnarray}
```
偶関数なので、
```math
\begin{eqnarray} 
E[X^2]　&=& 2\int_{0}^{∞}x^2\frac{1}{\sqrt{2\pi}}\exp(-\frac{x^2}{2})dx \\
\end{eqnarray}
```
ここで、
```math
t = x^2/2\qquad(0<t<∞)
```
と置換する。

```math
\begin{eqnarray}
E[X^2] &=& \frac{2}{\sqrt{\pi}}\int_{0}^{∞}t^{1/2}e^{-t}dt \\
\end{eqnarray}
```
ここで、ガンマ関数の定義、性質は、
```math
\begin{eqnarray}
\Gamma(s)&=&\int_{0}^{\infty}t^{s-1}e^{-t}dt\qquad(s>0), \\
\Gamma(s)&=& (s-1)\Gamma(s-1) \\
 \Gamma(s)&=& (s-1)!,\\
\Gamma(1/2) &=& \sqrt{\pi}
\end{eqnarray}
```
であるので、
```math
\begin{eqnarray}
E[X^2] &=& \frac{2}{\sqrt{\pi}}\Gamma(3/2) \\
&=& \frac{2}{\sqrt{\pi}}*1/2* \Gamma(1/2) \\
&=& \frac{2}{\sqrt{\pi}}*1/2* \sqrt{\pi} \\
&=& 1
\end{eqnarray}
```
よって、
```math
\begin{eqnarray}
V[X] &=& E[X^2] - {E[X]}^2 \\
&=& 1-0^2 \\
&=& 1
\end{eqnarray}
```
必要な項目だけをSPOから取り出して別CSVに書き出す方法



```powershell:qiita.ps1
Connect-SPOService 

$exportCsvFilePath = "Export.csv"

$results = New-Object System.Collections.ArrayList

$sites = Get-SPOSite -Limit All

foreach($site in $sites){

    $users = Get-SPOUser $site.Url -Limit All

    foreach($user in $users){

        $result = [PSCustomObject]@{

            SiteTitle = $site.Title
            SiteUrl = $site.Url
            UserDisplayName = $user.DisplayName
            UserLoginName = $user.LoginName
        }
    }

    $results.Add($result) > $null
}

$results | Export-Csv $exportCsvFilePath -Encoding Default -NoTypeInformation

Get-SPOSite -Limit All | ForEach {Get-SPOUser -Limit All -Site $_.Url}
```

メモ

"$" は最初に登場した時点で "$null" が格納されている
Vite環境で環境変数が使われているファイルをテストする

今回構築した環境でのライブラリのバージョンは以下のとおりです。
```
- react: 18.0.0
- typescript: 4.6.3
- vite: 2.9.9
- jest: 28.1.0
- ts-jest: 28.0.2
- vite-plugin-env-compatible: 1.1.1
```

## Vite + React + TypeScript のテンプレート作成

```
$ yarn create vite vite-test-sample --template react-ts
$ cd vite-test-sample
$ yarn install
```


## テストツール導入

jest を導入し、config ファイルを作っていきます。
ts-jest は TypeScript で書かれたファイルを jest に認識させるため使います。

```
$ yarn add -D jest ts-jest @types/jest
$ yarn ts-jest config:init
```

## テストコード

まずはテストができる確認をするため通るテストを書いてみます。

``` ts
// add.ts
function add(x: number, y: number): number {
  return x + y;
}

// add.spec.ts
describe("add", () => {
  test("1 + 2 = 3", () => {
    expect(add(1, 2)).toBe(3);
  });
});
```

add.ts と add.spec.ts の 2 つのファイルを作り、package.json に test のスクリプトを追加し、yarn test してみるとテストが通る事を確認できます。

次に環境変数を含むコードのテストを書いていきます。

### Viteで環境変数を使う

Vite で環境変数を使うにはプロジェクトのルートに。env とういファイルを作成しその中に `VITE_` のプレフィックスを付けて環境変数を宣言し、`import.meta.env. + 環境変数名` で使用できます。
詳しくは[こちら](https://ja.vitejs.dev/guide/env-and-mode.html)を参照してください。

``` ts
// .env
VITE_SOME_KEY=abcd

// testingWithEnvironmentVariable.ts
function testingWithEnvironmentVariable(): string {

  // 環境変数を使う処理
  const env = import.meta.env.VITE_SOME_KEY;

  return "この関数では環境変数が使われています";
}


// testingWithEnvironmentVariable.spec.ts
describe("testingWithEnvironmentVariable", () => {
  test("環境変数を使った関数をテストする", () => {
    expect(testingWithEnvironmentVariable()).toBe(
      "この関数では環境変数が使われています"
    );
  });
});

```

上記のコードでテストを走らせるとテストは失敗します。

``` エラーログ
The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node12', or 'nodenext'.
```

こちらのエラーの原因は以下の様子です。
- import.meta は ESModule でしか動作しない
- es2020 以降でしか動作しない
- ts-jest を使ってテストファイルを CommonJS 形式に変換してからテストしている

つまり、ts-jest によって CommonJS 形式に変換されたテストファイルでは import.meta での環境変数を使えないようです。

当記事では、環境変数の呼び出し方を import.meta ではなく process.env を使うという解決方法を試してみます。

[vite-plugin-env-compatible](https://github.com/IndexXuan/vite-plugin-env-compatible)というプラグインを使用して process.env の形式で環境変数を使えるようにしていきます。

プラグインをインストールし、vite.config.ts に変更を加えます。
```
$ yarn add -D vite-plugin-env-compatible
```

```vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+ import env from "vite-plugin-env-compatible";

// https://vitejs.dev/config/
export default defineConfig({
    plugins: [
    react(),
+   env({ prefix: "VITE",  mountedPath: "process.env" }) 
  ],
});
```

9 行目の prefix で環境変数名のプレフィックスを指定します。デフォルトでは VUE_APP となっていますが、Vite で使うので VITE と指定します。

環境変数を使っている箇所を process.env の形式に修正します。

```testingWithEnvironmentVariable.ts
function testingWithEnvironmentVariable(): string {

  // 環境変数を使う処理
-  const env = import.meta.env.VITE_SOME_KEY;
+  const env = process.env.VITE_SOME_KEY;

  return "この関数では環境変数が使われています";
}
```

この状態で yarn test を走らせると無事テストは成功します。

## おわりに

当記事では `テスト対象ファイルで環境変数が使われているとテストできない -> import.metaではなくprocess.envを使う` という方法を取りましたが、環境変数に型をつける方法や、テストをする際に ESModule のままテストできる方法、または他の解決策など何かご存知の方いらっしゃいましたらコメントを頂けると幸いです。
Teamsのリストに追加された変更をチャット投稿で知らせる方法（M365 PowerAutomate）

![qiita用.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601332/4e57f996-77c5-3a0f-6cbd-e51b943d0cf8.jpeg)

Teams上のリストが変更されたら、メンションや本文をつけてTeamsのチャットに投稿することができます。

【PowerAutomate上の記述】

トリガー：アイテムが作成または変更されたとき
-> ここからリスト上の変更をキャッチできます

条件
-> ここにキャッチしたいリスト上の変更を記述します

はいの場合
-> チャットまたはチャネルでメッセージを投稿する

いいえの場合
-> 必要があれば記述できます

以上になります。使い方によっては幅広く応用できそうですね！
aku

１．print関数を使って配列の中身を出力する場合は、以下に添付しています。
list = [1, 2, 5, 9]
print (list)
２．配列にデータを追加する場合は、appendメソッドというものを使います。
list = [1,2, 5, 9]
list.append(10)
print(list)
＃list.insert
配列の要素を削除したい場合はpopメソッドが便利です。
list = [1, 3, 4, 6]
list.pop(0)
print(list)
python

演算には掛け算、割り算、のafter 足し算。引き算が計算されます。
for exmaple:10+20-10/5*100**0
     ans:28.0
2.ある文字列を変数に代入するには、
　　　　name='大阪'

3.print 関数を使って配列の中身を出力したい場合
list =(1,2,5,9)
print(list)
.ipynbをのぞいてみよう【シリーズ：ファイルの中身1】

## .ipynbって？
IPython notebook のこと

## IPythonって？
Pythonを対話的に実行するためのシェル
https://ja.wikipedia.org/wiki/IPython

## .ipynbの中身
まず、適当なipynbファイルを用意↓
![capture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2013882/665bf36c-fcf5-6b36-ff73-4082e946ebc9.png)

vimでそのipynbファイルを開いてみた様子↓
![capture 4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2013882/0b75542a-4000-9edb-f7b9-970b687ccfed.png)
![capture 5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2013882/69231a78-5472-2aaa-8909-cf924213221b.png)

### 解析
まず、json形式になっている。
ファイル全体で大きなkeyが2つある。
cells:ipynbの各セルがそのvalueの配列の各要素に対応している。
meta_data:kernelの言語やそのversionなどのメタ情報

cellsの中身
|  key  |  value  |
| ---- | ---- |
|  cell_type |  code, markdownなど  |
| execution_count   |  実行のカウント（直訳、セルの横に書いてる数字）  |
|id|セル固有の数字|
|meta_data|わからない|
|outputs|そのセルの実行結果（結果を出力している場合）|
|source|そのセルのコード|

## まとめ
いかがでしたか？ipynbファイルについてまとめてみました。
最近いろんな拡張子のファイルの中身の構造をみるのがマイブームでこれからも連載する予定です！
チャンネル登録・高評価よろしくお願いします！
https://www.youtube.com/channel/UC4hL9ayu79SP6WrxauSoylw
API

import requests
import json
import pprint

import random
url = "https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042"

result = requests.get(url)
data = json.loads(result.text)
pprint.print(data)
print(type(data))
pprint.pprint(data["results"])

{
        "message": null,
        "results": [
                {
                        "address1": "大阪府",
                        "address2": "大阪市阿倍野区",
                        "address3": "丸山通",
                        "kana1": "ｵｵｻｶﾌ",
                        "kana2": "ｵｵｻｶｼｱﾍﾞﾉｸ",
                        "kana3": "ﾏﾙﾔﾏﾄﾞｵﾘ",
                        "prefcode": "27",
                        "zipcode": "5450042"
                }

result = requests.get(url)
data = json.loads(result.text)
##pprint.print(data)
print(type(data))
json_data = data["results"][0]

print(f'郵便番号は{json_data["zipcode"]}です。住所は{json_data["address1"]}{json_data["address2"]}{json_data["address3"]}です。')

result :
郵便番号は5450042です。住所は大阪府大阪市阿倍野区丸山通です。
API と　JSON

みにくい
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
print(data)
print(type(data))

{'message': None, 'results': [{'address1': '大阪府', 'address2': '大阪市阿倍
野区', 'address3': '丸山通', 'kana1': 'ｵｵｻｶﾌ', 'kana2': 'ｵｵｻｶｼｱﾍﾞﾉｸ', 'kana3': 'ﾏﾙﾔﾏﾄﾞｵﾘ', 'prefcode': '27', 'zipcode': '5450042'}], 'status': 200}
```
みやすいくなるとき
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
pprint.pprint(data)
print(type(data))

{'message': None,
 'results': [{'address1': '大阪府',
              'address2': '大阪市阿倍野区',
              'address3': '丸山通',
              'kana1': 'ｵｵｻｶﾌ',
              'kana2': 'ｵｵｻｶｼｱﾍﾞﾉｸ',
              'kana3': 'ﾏﾙﾔﾏﾄﾞｵﾘ',
              'prefcode': '27',
              'zipcode': '5450042'}],
 'status': 200}
```
形に変わります
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
pprint.pprint(data)
print(type(data))
pprint.pprint(data["results"])

[{'address1': '大阪府',
  'address2': '大阪市阿倍野区',
  'address3': '丸山通',
  'kana1': 'ｵｵｻｶﾌ',
  'kana2': 'ｵｵｻｶｼｱﾍﾞﾉｸ',
  'kana3': 'ﾏﾙﾔﾏﾄﾞｵﾘ',
  'prefcode': '27',
  'zipcode': '5450042'}]
```
"[]" なくなりました
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
pprint.pprint(data)
print(type(data))
pprint.pprint(data["results"][0])

{'address1': '大阪府',
 'address2': '大阪市阿倍野区',
 'address3': '丸山通',
 'kana1': 'ｵｵｻｶﾌ',
 'kana2': 'ｵｵｻｶｼｱﾍﾞﾉｸ',
 'kana3': 'ﾏﾙﾔﾏﾄﾞｵﾘ',
 'prefcode': '27',
 'zipcode': '5450042'}
```
郵便局を印刷された
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
pprint.pprint(data)
print(type(data))
json_data=data["results"][0]
print(f'郵便番号は{json_data["zipcode"]}')

'results': [{'address1': '大阪府',
              'address2': '大阪市阿倍野区',
              'address3': '丸山通',
              'kana1': 'ｵｵｻｶﾌ',
              'kana2': 'ｵｵｻｶｼｱﾍﾞﾉｸ',
              'kana3': 'ﾏﾙﾔﾏﾄﾞｵﾘ',
              'prefcode': '27',
              'zipcode': '5450042'}],
 'status': 200}
<class 'dict'>
郵便番号は5450042
```
場所とかインフォメーションを印刷されるように
```python
import random
url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=5450042'

result= requests.get(url)
data=json.loads(result.text)
pprint.pprint(data)
print(type(data))
json_data=data["results"][0]
print(f'郵便番号は{json_data["zipcode"]}です。場所は{json_data["address1"]}{json_data["address2"]}{json_data["address3"]}です')

<class 'dict'>
郵便番号は5450042です。場所は大阪府大阪市阿倍野区丸山通です
```
docker+php5系でxdebugを使う方法

# はじめに
仕事でphp5.5の環境構築をすることになったのですが、xdebugが動作しませんでした。
解決するのに結構嵌ってしまったので、対処法を残します。

# 環境
- VSCode
- Docker
- php5.5
- mysql5.7

# 結論
xdebug3を使用していたため、動作しませんでした。

xdebug2.6.0からphp5系がサポート対象外になったようです。
http://xdebug.org/announcements/2018-01-29

php5.5を使用する場合はxdebug2.5.5を使用しましょう。

:::note warn
php5.3以前を使用する場合は、xdebug2.2.7をインストールしてください。
xdebug2.3.0からphp5.3以前がサポート対象外のようです。
http://xdebug.org/announcements/2015-02-22
:::

# フォルダ構成
```
├── .vscode
│   └── launch.json
├── docker
│   ├── Dockerfile
│   └── php.ini
├── docker-compose.yml
```

# 設置ファイル
## docker-compose.yml
```
version: '3'

services:
  php:
    build: ./docker/php 
    volumes:
      - .:/var/www/html
    ports:
      - 8000:80

  mysql:
    image: mysql:5.7
    volumes:
      - ./mysql:/var/lib/mysql
    command: mysqld --sql-mode=NO_ENGINE_SUBSTITUTION
    ports:
      - 3306:3306
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_USER=user
      - MYSQL_PASSWORD=password
      - MYSQL_DATABASE=mysql
```

## Dockerfile
```Dockerfile
FROM php:5.5-apache

RUN apt-get update -qq \
  && apt-get install -y \
  && pecl install xdebug-2.5.5 \
  && docker-php-ext-enable xdebug

COPY ./php.ini /usr/local/etc/php/

WORKDIR /work
```

## php.ini
```ini
[XDebug]
xdebug.remote_port = 9000
xdebug.remote_host = host.docker.internal
xdebug.idekey = "VSCODE"
xdebug.remote_autostart= 1
xdebug.remote_connect_back = 0
xdebug.remote_enable = 1
```

:::note warn
xdebug2と3では記述が異なります。
3の記述では動作しないのでお気をつけください。
:::

## launch.json
```json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "VSCODE",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "pathMappings": {
                "/var/www/html":"${workspaceRoot}/"
            }
        }
    ]
}
```
CCNA合格体験記(4週間)

CCNA試験に4/28に合格しました。

筆者の知識レベル：
前職でオンライン英会話のレッスンを行う部署で働いていたため、接続が怪しいみたいなトラブルに対しては原因を突き止めて対応することは出来る。
でも、それぞれの言葉の意味はぼんやりとしかわからない。バッファ、ジッタとか、IPアドレス、MACアドレス、物理だ論理だといった部分。
PCは人並みに扱える。機械は苦手ではない。

学習方法：
1週間で基礎を学ぶ的な本
黒本
Udemy Business(会社が貸与してくれるアカウント)でCCNAに関する動画を見て学ぶ
Ping-Tで問題対応

学習の感覚：
知識を詰め込むだけならUdemyの解説が一番わかりやすく、何回でも見られるので重宝した。
問題演習はPing-Tと黒本。Ping-Tは模擬試験での正答率が90%をマークするまで繰り返し回答した。
Ping-t利用においては、正解と思われる選択肢についてはだんだん覚えてきてしまうので、不正解の選択肢について「なぜ異なるのか」という説明が可能なレベルにしておくとよい。
サブネット利用・IPアドレッシングに関する計算問題は確実にしておくこと。
また、問題集は1つのものをやり込むのも良いが、回答の文言などを覚えてしまうことと、編纂者の傾向がどうしても出てしまうので、もう一冊ほど組み合わせると良いと思う。
会社が4週間という時間をくれたが、正直そこまでなくても範囲をマスターすることは可能。
筆者の場合、集中力が薄かったためいくばくか時間が無駄になっていたところもあるので、4週間フルに利用して合格できた。

受験：
実際の試験はCBT方式と言って、小部屋にPCが設置してあり、係員が操作して問題が開始される。あとはクリックとドラッグ＆ドロップで100問答えていく。
合格基準点は最初に示される。筆者の場合は835/1000点で合格だといわれた。
難易度は黒本程度だったが、問題の形式的にはPing-tのほうが近い。というかPing-tで掲載されていた問題そのものが出題されたこともあった。
用語や記号の意味を問う問題はほとんど脊髄反射で回答できた。
コンソール画面のコマンド入力などについては苦手だったが、何となくキーワードを追いかけることで回答。
120分与えられたが、75分ほどで回答終了。1問答えたら戻れないのでそのまま試験終了。
終わった後に簡単なアンケートが出てきたので、もう少し日本語が柔軟に使えるといいですねと書いた。

結果：
結果は937点で合格。
自身のない問題もいくつかあったが、ハードルは余裕で越えている状態だった。
何を落としたのかは分からないがとりあえず受かったのでその日は昼からビールを飲んで自分をねぎらった。
HTTPの機能改善

# HTTP/1.1のやりとり
前はコネクションの確立をしてからHTTPプロトコルのネットワーク通信ができることを学んだ。
## そもそもHTTP/1.1とは何なのか
/1.1はバージョンの違いである。
最初HTTPは/0.9から始まり、1.0,1.1,2と来ている。
0.9~1.0は通信一回一回にコネクションの確立をして確かめていた。なので効率が悪く、無駄な通信が発生してしまう。
そのため1.1からその欠点を克服するために```HTTPキーアライブ```と言う機能が利用され始めた。
## HTTPキープアライブ
初めにコネクションが確立した後はこの```要求と確認応答の通信をせず```コネクションを継続して利用できるようになったのだ。
そして1.1でもう一つの機能```HTTPパイプライン```がある。。

## HTTPパイプライン
今までは一つのリクエストに対するレスポンスが帰ってこないと次のリクエストができなかった。
しかしこの機能はそれを待たずに```複数のHTTPリクエストを送信できる。```

# HTTP/2
1.1以降やり取りされるデータ量は飛躍的に増えています。
それに対して機能改善がされた。
それがSPDYだ。
### SPDY(プロトコル）
Googleが提案した通信の高速化を目的としたプロトコルがベースになっている。
通信の高速化を担う部分は、ほぼSPDYの機能を継承している。
そのうちの機能の一つがストリームだ。

## ストリーム（仮想的な通信経路） 
HTTPパイプラインで複数のリクエストが可能になったが、それには欠点があった。
それは、リクエストの順番によるレスポンスの遅延だ。
最初のリクエストに対する処理の完了に時間がかかる場合それが終わるまで次のリクエストを受け付けられないから全体的に時間がかかる。
仮想的な通信経路を生成することによって並行してリクエストとレスポンスのやり取りが可能となった。
そのほかにも改良点がある。

## バイナリ形式の利用（そのままで送信）
HTTP/1.1以前ではHTTPリクエストとHTTPレスポンスのやり取りにテキスト形式のフォーマットを使っていました。
やり取りにバイナリ形式のフォーマットを```テキスト形式のフォーマットに変換```していた。
そのままバイナリ形式でやり取りをできるようになった。
それにより```サーバーへの負担と返還処理にかかる時間を軽減```した。

## ヘッダー圧縮（重複部分の省略）
HTTPメッセージのメッセージヘッダーの無駄な情報を減らした。
複数のリクエスト、レスポンスに重複したデータを転送するハメになるので```そこの部分を省いて差分（違ったところだけ）を送る```
HPACKと呼ばれる圧縮方法を利用する。

## サーバープッシュ（サーバー側が忖度する）
サーバー側が必要なファイルかどうかを判断し送信する。
HTMLファイルに画像が埋め込まれていた場合、画像データに対するHTTPリクエストを受けなくても```事前に転送```する。

出典 Web技術の基本基本（SBcreative)
Prologでの素数判定プログラムを理解する

# はじめに
大学でPrologという論理型プログラミングの存在を知り、練習として素数判定プログラムを作りました。
完成までに試行錯誤したことでかなりPrologへの理解につながったので、足跡として残しておきます。
# 目次

[1.素数判定プログラム](#1-素数判定プログラム)
[2.実際に動かしてみる](#2-実際に動かしてみる)
[3.仕組みを考える](#3-仕組みを考える)
[4.おわりに](#おわりに)

# 1. 素数判定プログラム
とりあえず、コードだけ知りたい人の為に今回作ったコードを最初に張っておきます。
```
fact1(X, Y) :- 0 is X mod Y, !.
fact1(X, Y) :- X > Y+1, fact1(X, Y+1).

fact2(2) :- true, !.
fact2(X) :- X < 2,!,false.
fact2(X) :- not(fact1(X,2)).
```
なんでこれで素数判定ができるか知りたい人は続きをどうぞ。

# 2. 実際に動かしてみる
まずは本当に素数判定ができるか動かしてみましょう。
Prologにはオンライン上で実行させられるツールがあります。

[SWISH](https://swish.swi-prolog.org/)

今回はこれを使って動作を確認していきたいと思います。

### 実行方法
#### 1. 上記のリンクを開いたらProgramをクリック
![prolog1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365485/e987f213-9cd5-c990-14e9-b6bf2bafe8c5.png)

#### 2. コードを貼り付ける
![prolog2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365485/b7bea5c8-f013-44e2-7b9a-fee185ea7c46.png)
ここまででPrologにおけるプログラムの部分は完成です。
ここから実行していきます。

#### 3. 右下のスペースにfact2(7)と入力してみる
![prolog3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365485/eb1d5731-2c40-d29a-bde2-eef03fcb9202.png)

#### 4. Run!
![prolog4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2365485/f0b50c86-90fe-c78b-a6f4-82978908e794.png)
trueと出たらOK!

# 3. 仕組みを考える
Prologの独特な部分として、**すべての内容が真になるものを、すべて探そうとします。**
これを前提として以降を読み進めてください。

## fact1
まずはfact1から考えます。
```
fact1(X, Y) :- 0 is X mod Y, !.
fact1(X, Y) :- X > Y+1, fact1(X, Y+1).
```
この部分ではfact1(X, Y)という関数を定義しているのですが、処理は1行目から始まりXをYで割った余りが0になるかを確認します。
本来は`,`で複数の条件を書いていくのですが、ここでは2つ目の条件として`!`が入っていることが分かります。
これは前の条件が真になった時、**以降の処理を行わない**という意味になります。(カットと言います)
例えば`X=4,Y=2`の時は`true`が返答されて、2行目から下は実行されません。

次に2行目です。
ここでは他のプログラムでいう**for**を実現しています。
1行目と同様に考えていきます。まずは`X > Y+1` で大小関係の比較を行います。これは、Y+1でもわかる通り、次の周回に行くべきかどうかを決定しています。つまり**継続条件**です。
そして継続条件が真であったときは`,`の後の`fact1(X, Y+1)`を評価します。そして`Y+2`、`Y+3`とYを1ずつ増やしながら再帰していきます。
**Prologではforを再帰関数で表現するのです**。

以上の説明から考えると`fact1`は**YからX未満のすべての数の中で1つでもXを割り切れるものがあればtrueを返す**というプログラムだと分かります。

## fact2
ここまでの内容が理解できれば`fact2`は簡単です。
```
fact2(2) :- true, !.
fact2(X) :- X < 2,!,false.
fact2(X) :- not(fact1(X,2)).
```
1行目では2が入った時はtrueを返して処理を終了させています。これは、3行目で`fact1(X,2)`と呼び出しているのですが、`fact1`の1行目の条件より`fact1`が`true`になって素数ではなくなってしまうため、例外的に処理しています。
2行目も同様に`true`となってしまうのを回避するために2より小さい数では常に`false`を返しています。
最後に3行目で、`fact1`の逆、つまり素数ならば`true`を返して終了です。

## 以上
これでプログラム全体を理解できました。一つ一つ分解して考えるとそこまで難しくもないのかなと思います。
軽い練習として、53も素数として判定するようにしてみても面白いかもしれません。

# おわりに
リアルタイムで知識を増やしながら書き進めたので、間違いやより良い表現があれば、コメントでお知らせください。

### 参考文献
- [Prolog入門](https://qiita.com/a163236/items/c99cd16ddf77e2228719)
- [Prologのカットについて](https://qiita.com/kataware/items/111f16f695b73aa4589f)
Python で文字列暗号化のやりかた　覚え書き

```
>>> from cryptography.fernet import Fernet
>>> key = Fernet.generate_key()
>>> f = Fernet(key)
>>> str1 = f.encrypt(b'STRING')
>>> str2 = f.encrypt(b'STRING')
>>> str3 = f.encrypt(b'STRING')
>>> f.decrypt(str1)
b'STRING'
>>> f.decrypt(str2)
b'STRING'
>>> f.decrypt(str3)
b'STRING'
>>> str1
b'gAAAAABihzIr-f-8aNMSgmJOLFldYudDl_UR1poI9WXFhT2xHWuaWnEBtUoggxVufyAf2s27uxbpw_0uv6kiuxfmZfPOUs8HyA=='
>>> str2
b'gAAAAABihzIwApIVnYqTZ5Ltilmmheegl6H1uf4mNZNIaV_bu_T7aSawYvkd73qlQ3YhSKNYh-59a-BLxT4x8JpHsjBqb5n_Ug=='
>>> str3
b'gAAAAABihzI1neTjCSlJ6SJ2D2kkfYT67xhmtJNPyUEpiatVE70oVhbAgX9ln9dr749elm3VUSWA7XyDvGwB7xNJ4e_dMQj0Qw=='
>>>
```
```
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>> key = get_random_bytes(32)
>>> key
b'\xa5)\x8d\xcc@\x97\xe1\x0ec\xac\x07\xec\x92\r"q\xc8L\xe4t^\xb6U\x1e\x02\x9a(=d\xc8H\xc5'
>>> cipher = AES.new(key, AES.MODE_GCM)
>>> enc_str1, tag = cipher.encrypt_and_digest(b'STRING')
>>> enc_str1
b'\xa6\xf1\xdb\x91\x92\xbb'
>>> tag
b'VW\xf8\xac\xb5\x17@K\xa3\xf0\x7f\x8ad\xa7~\xb3'
>>> cipher.nonce
b'f9\xd5\x0e\x99\x96\xd0\xba\xd9\x83Y\xa1\xfe\xcaX\xfd'
>>> f_decrypt = AES.new(key, AES.MODE_GCM, cipher.nonce)
>>> dec_str1 = f_decrypt.decrypt_and_verify(enc_str1, tag)
>>> dec_str1
b'STRING'
>>>
```
```
>>> import subprocess, sys
>>> subprocess.check_output('echo "STRING" | openssl enc -e -aes128 -base64 -k "KEY" -pbkdf2', shell=True)
b'U2FsdGVkX1+h4OhHGpWuRBud15Ihdr7kjlOSnBleldY=\n'
>>> subprocess.check_output('echo "U2FsdGVkX18tBZwU+04ED55MKCOzWppPynVL6wZWCrA=" | openssl enc -d -aes128 -base64 -k "KEY" -pbkdf2', shell=True)
b'STRING\n'
>>> 
```
suで普通にはログインできないユーザへのログイン方法と、sudoers.d の書き方

# nginx, jenkins などの普通にはログインできないユーザへのログイン方法と、sudoers.d の書き方

jenkins ユーザに`sudo su`したいという依頼が。
`sudo 権限を、 jenkins ユーザに su するだけの権限を付与したい`時のsudoersファイルの書き方をメモしておきます。

## nginxやjenkinsユーザについて

これらのミドルウェアをインストールすると、nginx ユーザや jenkins ユーザが作成されます。

```
# cat /etc/passwd
nginx:x:993:988:Nginx web server:/var/lib/nginx:/sbin/nologin
```

ただし、このようなユーザは ログイン禁止のシェル`/sbin/nologin`や`/bin/false`が設定されており、
su コマンドでログインできなくなっております。

```
$ sudo su - nginx
最終ログイン: 2022/05/20 (金) 15:19:17 JST日時 pts/0
This account is currently not available.
```

- 【参考】: [suできないユーザに切り替える方法](https://qiita.com/hypermkt/items/25d99d0086a5419e5b35)

## nginxユーザにsuさせる方法

参考の記事の通り、suコマンドのオプションに`-s /bin/bash`を指定することで、bashのシェルでログインできます。

```
# su -s /bin/bash - nginx
最終ログイン: 2022/05/20 (金) 14:59:31 JST日時 pts/0
$ whoami 
nginx
```

## sudo で実行できるようにする

ただし、nginxなどのユーザはpasswordが設定されていないので、
基本的には全コマンドノーパスワードで実行できる**rootユーザでしかsuできない**状態です。

そこで、`sudoers.d/`配下のファイルををいじって、**一般ユーザでもsudoでnginxやjenkinsユーザにsuできる**ようにできます。

### visudo で sudoers.d 配下にファイルを作成する

:::note alert
**必ず**`visudo`コマンドで設定変更するようにしましょう。
設定を間違えた状態で保存してしまうと、**sudoが使えなくなってしまいます。**
:::

```
# visudo /etc/sudoers.d/nginx_sudoers
```

```:nginx_sudoers
Cmnd_Alias NGINX = /bin/su -s /bin/bash - nginx
%グループ名   ALL=(ALL) NGINX
```

:::note info
- ポイント:
  - Cmnd_Alias で sudo を許可するコマンドのエイリアスを指定します。
  - 指定したエイリアスに対する sudo 実行を %グループ名 のグループに対して許可します。

※ %グループ名 はご自身の環境に合わせて変更してください。
:::

`:wq`で保存して、エラーメッセージが出なければOKです。

### 一般ユーザでログイン

```
$ sudo su -s /bin/bash - nginx
[sudo] user のパスワード:
最終ログイン: 2022/05/20 (金) 15:25:51 JST日時 pts/0
[nginx@imade-gcp01 ~]$ whoami 
nginx
```

ログインできるようになりました！
PYTHONについて

変数の使い方

>>>price = 100
>>>price
100
>>>price + 10
110

文字列を扱う

>>>name = 'ヘスティ'
>>>name
'ヘスティ'

append<inplace>
リストの末尾に追加する。
my_list.append(10)

clear<inplace>
リストを空にする。
my_list.clear()

copy
リストのコピーを返す。
[1,2,3].copy()
=>[1,2,3]

count
リスト内に何個あるかを返す。
[1,1,2,1,2,3].count(1)
=>3

extend<inplace>
リストに別のリストを追加する。
my_list.extend([4,5,6])

index
リスト内の位置を返す。
['burger','steak','chiken'].index('steak')
=>1

insert<inplace>
リストの指定した位置に追加する。
my_list.insert(0,'追加データ')

pop<inplace>
リスト内の要素を削除する。
「引数なし」はー１と同義
my_list.pop()
=>10

remove<inplace>
リスト内の要素を削除する。
popと違い、値で指定する。
my_list.remove('削除したい値')

sort<inplace>
リストを並べ替える。
reverse=Trueにすると、結果を逆にする
my_list.sort(reverse=True)
[AWS][Terraform] WAF v2 のデフォルトアクションを variable で指定したい

Terraform で AWS WAF を作成するとき default_action を variable で指定したいと思ったんですよ。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl

デフォルトアクションを指定するには `default_action{}` ブロックの中に `allow {}` または `block {}` って書くのね。
こんな感じかな？

```terraform
variable acl_name           { }
variable acl_scope          { default = "REGIONAL" }
variable acl_default_action { default = "allow" }

resource aws_wafv2_web_acl waf {
    name  = var.acl_name
    scope = var.acl_scope

    default_action　{
        var.acl_default_action == "allow" ? allow {} : block {}
    }
```

おふぅ、エラーになりますね...

```
│ Error: Argument or block definition required
│ 
│   on waf.tf line 10, in resource "aws_wafv2_web_acl" "acl":
│   10: var.acl_default_action == "allow" ? allow {} : block {}
│ 
│ An argument or block definition is required here. To set an argument, use the equals sign "=" to introduce the argument value.
```

ここには、ブロックを書きなさいとのお達しです。まぁ、ドキュメントにもそう書いてありますしね。

こういう時は[ダイナミックブロック](https://www.terraform.io/language/expressions/dynamic-blocks)と[ローカル変数](https://www.terraform.io/language/values/locals)の出番です。
`var.acl_default_action` が `allow` だったらローカル変数 `acl_allow` に一つだけ要素をもった配列をセット、`allow` 以外だったらローカル変数 `acl_block` に一つだけ要素をもった配列をセットしておきます。
で、`dynamic` ブロックで配列の要素数分回すことで `allow {}` または `block {}` ブロックを `default_action {}` ブロックに入れ込むわけです。

ちょっと何言ってるかよくわからないすかね？ソース見たほうがわかりやすいかも。

```terraform
variable acl_name           { }
variable acl_scope          { default = "REGIONAL" }
variable acl_default_action { default = "allow" }
locals {
    acl_allow = var.acl_default_action == "allow" ? [true] : []
    acl_block = var.acl_default_action != "allow" ? [true] : []
}

resource aws_wafv2_web_acl waf {
    name  = var.acl_name
    scope = var.acl_scope

    dynamic default_action {
      for_each = local.acl_allow
      content {
        allow {}
      }
    }
    dynamic default_action {
      for_each = local.acl_block
      content {
        block {}
       }
    }
```

ちょっとトリッキーですが、現状、この解しかないかなーと。


追記
WAF v1 の時は、こうやってかけたから、こんなトリッキーなことしなくても良かったんすけどね
```terraform
variable acl_name           { }
variable acl_default_action { default = "ALLOW" }   # ALLOW or BLOCK

resource aws_waf_web_acl waf {
    name  = var.acl_name

    default_action {
        type = var.acl_default_action
    }
```
M5stack Core2でBluetooth接続　PCのpythonプログラム

先日の続きです。
Chrome経由での接続確認はできたのですが、Pythonでの実装をポチポチと
（かき集めた内容なので、間違っている箇所があったらご指摘下さい:bow_tone1:）

https://qiita.com/ganmodoki_oishii/items/e0aad88ba3c4a20ce9b8

Bluetooth接続と書いておきながらも、実際はBluetooth Low Energy（BLE）というエコモードなBluetoothです。
Pythonのライブラリは`pybluez`はインストールが上手くいかなかったので、`Bleak`を使いました

https://bleak.readthedocs.io/en/latest/index.html

```python
from bleak import BleakScanner
from bleak import BleakClient
```
Scannerがデバイス検索
Cliantが送受信に使われています

### Python側から通信先を見つける
`M5Stack`は前回のプログラムを実行して、BLEを起動しておきます
Scannerを使って接続するデバイス名（UIFLOWで設定した"FIVE_STACK"）でMacアドレスを取得する
```python
import asyncio
from bleak import BleakScanner
from bleak import BleakError

device_name = "FIVE_STACK"

async def device_scan():
    devices = await BleakScanner.discover()
    idx = list(filter(lambda x: x.name == device_name, devices))

    # 検出デバイスを表示するだけの場合は以下
    # for d in devices:
    #     print(d)

    ADDRESS = idx[0].address # Macアドレスが出力される
    print(idx[0].name) # デバイス名が表示される
```
ここで表示されたMacアドレスは以降たくさん使います
変数に入れておきました

Cliantで使ったのは以下メソッド
```python
cliant.read_gatt_char(UUIDを指定)
cliant.write_gatt_char(UUIDを指定)
```
BLEでは`UUID`という値を使って通信します

### M5Stack Core2のUUIDはどれか
Bleakサイトのサンプルコードの「servies_explorer.py」を実行してみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2660324/160841b2-1755-b5d2-6194-763fc29fe669.png)
ServiceとCharavteristic、DescriptorのUUIDが表示された

上二つが`(read)`とあるから受信用UUIDってことかな
そうなると一番下の`(write)`が送信用かな
それをもとにやってみる

```python
import asyncio
from bleak import BleakClient

address = "" # 通信先のMacアドレス
MODEL_NBR_UUID = "" # 受信したいUUID

async def main(address):
    async with BleakClient(address) as client:
        model_number = await client.read_gatt_char(MODEL_NBR_UUID)
        print("Model Number: {0}".format("".join(map(chr, model_number))))

asyncio.run(main(address))
```
addressとUUIDを試しに一番上のHandle:11という項目のものでやってみた
`
00002a00-0000-1000-8000-00805f9b34fb (Handle: 11):  (read), Value: b'ESP32'
`
するとどうでしょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2660324/c5862759-a3c2-233d-8035-a8f102fede99.png)
M5Stackで使われているマイコンESP32と表示されました
`b'ESP32'`と同じなので、通信はできている！ということですかね

さらに双方向通信ができるようにしたいのですがまだ辿り着けていません。。

【追記　5/25】
BleakでCliantとする場合、PCはperipheralになる
M5StackCore2はBLEブロックを組み立てるとperipheralになっている？
【python】playwrightの実行中の操作を録画する方法

# 1. やりたいこと
Webブラウザのテストはしばしばテストの実行操作を後々見返したい時があります。
これを実現するためにplaywrightのtracing機能を活用し、実行中の操作を録画します。

# 2.　環境
playwrighとpytestが入っている環境を用意します。環境構築方法は、[別記事][link-9-1]に記載しています。
　・macOS 12.4(monterey)
　・Python 3.7.6
　・pip 22.1
　・pytest 6.2.5
　・playwright 1.16.2

| フレームワーク|コンテンツ|
|:----------- |:------------|
|[playwright][link-1-1]| マイクロソフト社が実行ブラウザを自動化する|
|[pytest][link-1-2]|Pythonで、このプログラムをする|

# 3. 環境設定
rootフォルダの直下に実行ファイルとエビデンスを保管するためのフォルダを作成します。
※ 実行ファイルのファイル名は、pytestの仕様上「test_xxx.py」としてください。
>root
> |- evidence
> |- test_sample.py

# 4. コード
実行ファイルに下記のようなプログラムを書きます。
自動操作対象のWebサイトは、[UI Test Automation Playground][link-4-1]を利用します。

```ruby:test_sample.py
from playwright.sync_api import Page, Playwright

def test_click(context,page):
    # 操作記録の開始
    context.tracing.start(screenshots=True, snapshots=True)
    # テスト操作
    page.goto("http://www.uitestingplayground.com/") 
    page.click("text = Click")
    assert page.inner_text("xpath = /html/body/section/div/h3") == "Click" 
    # 操作記録の完了 
    context.tracing.stop(path = "./evidence/evi_test_click.zip")

```
# 5. 実行 
ターミナルを起動後rootフォルダーに移動し、「pytest」を実行します。
```ruby:./root
pytest
```

実行後、evidenceフォルダーにevi_test_click.zipが生成されます。このzipファイルが録画データです。
>root
> |- evidence
> | |- evi_test_click.zip
> |- test_sample.py

ターミナルで、「evi_test_click.zip」に対して「playwright show-trace」を実行します。
```ruby:./root
playwright show-trace ./evidence/evi_test_click.zip
```
<dt>実行結果</dt>

test_sample.pyの操作記録が表示されました。
![スクリーンショット 2022-05-20 15.12.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2667601/6077d47b-e60e-3f26-ef12-2808046be72d.png)

# 6. 関連記事
・[【python】playwrightでE2E評価環境を作る][link-9-1]
・[【python】playwrightの実行中の操作を録画する方法][link-9-2]
・【python】playwrightでログイン処理省略する方法（作成中）

[link-1-1]: https://playwright.dev/
[link-1-2]: https://docs.pytest.org/
[link-2-2]: http://qiita.com/
[link-4-1]: http://www.uitestingplayground.com/
[link-9-1]: https://qiita.com/ryuusen999/items/4642cc03095e29bed0e9
[link-9-2]: https://qiita.com/ryuusen999/items/4e9a1c9a16c6819e3f9c
Dart 2.17のEnhanced enumsを使いenumのJSONエンコードをスマートに書く

## TL;DR

- enumにtoJsonメソッドを生やすと、enumを任意の値にJSONエンコードできる
- Enhanced enumsは最高

## Dart 2.17 でenumが大幅に強化された

enumが通常のclassのように扱えるようになりました。
任意のコンストラクタをもたせたり、フィールドやメソッドを生やしたり、既存メソッドのオーバーライドなどができるようになりました。

詳しくは下記の記事がわかりやすいです。
https://medium.com/dartlang/dart-2-17-b216bfc80c5d

## enum -> JSONは今まで簡単にできなかった

Humanクラスを考えてみます。
Humanクラスは2つのフィールドを持ちます。int型の`age`と、Gender型の`gender`です。
Gender型というのがenumになります。

```dart
enum Gender {
  female,
  male
}

class Human {
  final int age;
  final Gender gender;
  
  Human({required this.age, required this.gender});
  
  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }
}
```

カスタムクラスなのでtoJSONメソッドを追加しています。

これをjson.encodeすると、以下のようなエラーになります。

```
Uncaught Error: Converting object to an encodable object failed: Instance of 'Human'
```

Humanオブジェクトのエンコードに失敗しています。
なぜならenumのGender型である`gender`フィールドをエンコードする方法を知らないからです。

例えば、Gender.female -> 1, Gender.male -> 2 という値にエンコードしたいとしましょう。
以下のように書くことで、取り急ぎやりたいことは実現できるでしょう。

```
  toJson(){
    return {
      "age": age,
      "gender": gender == Gender.female ? 1 : 2　// 追加
    };
  }
```

ただ、Humanクラスの中でGenderの値をどうエンコードするかというビジネスロジックが入ってしまっているのは気持ち悪いです。また、Gender型を含むクラスが複数あったら、そのJSONエンコードの全箇所で同じ変換処理を書かなければ行けません。避けたいですよね。

このように、今まではenumを変換する際に悩ましい問題がありました。

## Dart 2.17のenumではこう書く

そこでDart 2.17のEnhanced enumsの出番です！
Dart 2.17では、Gender型を以下のようにアップデートできます。

```dart
enum Gender {
  female(1),　// enumを独自コンストラクタで初期化できる！
  male(2);
  
  final int value; // enum値ごとにカスタム値を持てる！
  
  const Gender(this.value); // 独自コンストラクタが定義できる！
  
  dynamic toJson() => value; // フィールドメソッドを生やせる！
}
```

female = 1, male = 2 であることがコードから明確に読み取れるのも良いですね。

このアップデートにより、Humanクラスの`toJson`メソッドは以下のように書くことができます。

```dart  
  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }
```

最高にシンプル！美しいです！
gender型を変換するための処理がきれいに無くなりました。

ではこのクラスを使ってみましょう。

```dart
void main() {
  final me = Human(age: 37, gender: Gender.male);
  final jsonStr = json.encode(me);
  print(jsonStr); // -> {"age":37,"gender":2}
}
```

enum型がちゃんと数字に変換できています。
Enhanced enums、最高！

## コード全体

```dart
import 'dart:convert';

enum Gender {
  female(1),
  male(2);
  
  final int value;
  
  const Gender(this.value);
  
  dynamic toJson() => value;
}

class Human {
  final int age;
  final Gender gender;
  
  Human({required this.age, required this.gender});
  
  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }
}

void main() {
  final me = Human(age: 37, gender: Gender.male);
  final obj = json.encode(me);
  print(obj); // -> {"age":37,"gender":2}
  assert(obj == '{"age":37,"gender":2}');
}
```
intへの変換の範囲について

# 概要
int型変換で遊んでいて気づいたので備忘録。

半角数値の文字列をint()で数値型にできるのは当然だが、
全角数値型の文字も半角数値にしてくれることにちょっと感動した。

# やっていたこと

```python: 文字列（半角数値）を数値へ
>>> num = "123"
>>> int(num)
123
```

まあ、当然そうなる。

```python: 文字列（全角数値）を数値へ
>>> num = "１２３"
>>> int(num)
123
```
おおお、優秀。

```python: 文字列（漢字で単位と数値）を数値へ
>>> num3 = "一億"
>>> int(num3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '一億'
```
期待したけどダメでした。まあ、当然。漢字をほいほいできる訳無し。

```python: 文字列（漢字（数字のみ））を数値へ
>>> num4 = "一"
>>> int(num4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '一'
```
期待したけどダメでした2。まあ、当然。漢字をほいほいできる（ｒｙ

```python: 文字列（ローマ数字）を数値へ
>>> num5 = "ⅠⅡ"
>>> int(num5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'ⅠⅡ'
```
おまけ。

# その他
- こんなどうでもいいコトを記事にすべきか迷った。

～以上～
どんなページでもエルデンリングの気分を味わえるブックマークレット

![qiita.com_(Desktop).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248832/0956aa6d-253c-a96b-8633-5a9e79692ecf.png)
エルデンリング（もしくはダークソウル）のゲームオーバー時に表示される「YOU DIED」をWebページ上で再現するブックマークレットを作りました。この先、時間の無駄があるぞ。

```javascript
javascript: (() => { const s = document.createElement('style'); document.head.appendChild(s); s.sheet.insertRule('.eldenring { all: initial; display: flex; align-items: center; opacity: 0; pointer-events: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; transition: opacity 2000ms linear; user-select: none; z-index: 65536; }'); s.sheet.insertRule('.eldenring[data-start="true"] { opacity: 1.0; }'); s.sheet.insertRule('.eldenring > div { background-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%, rgba(0, 0, 0, 0.75) 80%, rgba(0, 0, 0, 0) 100%); color: #c00000; flex-grow: 1; font-family: serif; font-size: 10vmin; letter-spacing: 0.5vmin; padding: 2vmin 0; text-align: center; transform: scale(1.0); transition: transform 2000ms ease-out; }'); s.sheet.insertRule('.eldenring[data-start="true"] > div { transform: scale(1.25); }'); const c = document.createElement('div'); document.body.appendChild(c); c.className = 'eldenring'; const e = document.createElement('div'); c.appendChild(e); e.innerText = 'YOU DIED'; setTimeout(() => { c.setAttribute('data-start', true); }, 250); })(); void 0;
```

[Gistはこちら](https://gist.github.com/mimonelu/957bf5e0be3d6826f4e7b303f9ba4e40) 。

## ミソ

* `stylesheet.sheet.insertRule()` で動的にスタイルシートを生成。どうしてもJSでCSSセレクタを書きたい時に便利です。
* `all: initial;` で全プロパティをリセット。こういった「不特定のWebページ上で表示されるビジュアル」を作る際には便利です。

## たわごと

* 一度表示したら消えません。
* 音は鳴りません。脳内再生してください。
* 実際のアニメーションとは若干異なります。
* このブックマークレットを起動してもルーンはロストしません。
Python : 配列をpickleで保存する・読み込む

作成日：20220520
言語：Python3.6
記事の用途：自分用メモ

Pythonで出力した変数を一度ファイルに保存し、あとで別のプログラムから使いたい時には、pickleモジュールを使うと便利。

下のコードでは、
まずaというリストをdata.pickleというファイルとして保存し、
次に上で保存したdata.pickleをbという変数に読み込んでいる。


```python:pickle_save_and_load.py
import pickle

### 保存したい配列
a = ['a', 'b', 'c'] # リストやnumpy.ndarrayなどの配列

### pickleで保存（書き出し）
with open('data.pickle', mode='wb') as fo:
  pickle.dump(a, fo)

### pickleで保存したファイルを読み込み
with open('data.pickle', mode='br') as fi:
  b = pickle.load(fi)

```


## 参考記事など
1. pickleについて
python公式 （※私には難しかった） https://docs.python.org/ja/3/library/pickle.html
分かりやすいブログ記事 https://blog.amedama.jp/entry/2015/12/05/132520

2. with文の使い方について
別記事を参照されたし。
【moment.js】Dateを扱うとき、Safariには気をつけよ。

日付の入力フォームに「バリデーションを実装している時のこと。
Safariだけ、存在しない値を入力してもバリデーションに通ってしまうという謎現象に遭遇。

## 結論
フォーマット時の書き方をSafariに合わせる必要がある
```javascript
❌ var now = moment("2022 5 20").format("YYYY-MM-DD");
⭕️ const valDate = moment("2022 5 20", "YYYY MM DD").format("YYYY/MM/DD");
```

## 現象
例えば "1999年9月90日" と入力された際、このような日付は存在しません。
今回、moment.js のisInvlid関数を使用して、日付の存在チェックを行っていたのですが、
何故かSafariではこの日付が存在することになってしまうのでした。（怖）

consoleにフォーマット後の値を出してみたところ、以下の値が。
```
Date: 1999/11/29
```
なるほどフォーマットがうまくいってない。全然違う値が返ってきてるやないかいっっ
90日分足しちゃったんですね〜、

調べる過程で、SafariのDate関係が他ブラウザと違う動きをするというのは有名な話ということを知りました。
無知の知。
Java17変更点メモ

## 前提
Java17は2021/9にLongTermSupportバージョンでリリース。
Java8のEOLがJava11より後ろ、身の回りはJava11よりJava8利用が多いため、Java8から変更有無に特化。

## モジュール管理
パッケージをまとめて管理⇛モジュール管理の導入。
とあるパッケージに含まれるライブラリが、とあるライブラリに依存して、更にそのライブラリが他のライブラリに依存するといった依存関係の解決が難しい。
そこでモジュール（module-info.java）を使用して、依存関係の定義を行えるようになった。

### module-info.java
例： aはcは利用不可、aはdを利用可
```python
module module.a {
  requires module.b;
}
module module.b {
  requires module.c;
  retuires transitive moduke.d;
}

module module.c {}
module module.d {}
```

## HttpClient導入
HttpURLConnectionハマること多い？
HttpClientによって楽になる？
http2サポート、非同期通信サポート。

## Sealedクラス
Sealedを指定することで指定されたタイプにだけ継承ができる仕組み。

## Recordクラス
決まったデータ構造を持つ値の集合。
https://programming-tips.jp/archives/202106/6/index.html
setter()はない。

## JVM変更
よくわからない。勉強します。

## jshell
javaコードの確認。（インタプリタ言語のように）

他にも機能追加はあるのですが、アプリ開発を行う際のメインどころとしては抑えれたのかなと。
追って情報アップデートしてきます。