OmniAICreator/Qiita-1.07M huggingface dataset:
mac標準でパスワード付きzipの作成（zipcloakは危険の巻）

# まえがき
パスワード付きZIPファイルってなんだか懐かしさも感じる響きなんですが、大人の事情的なもので作成する必要が出てきまして。ご察しください。
何かツールをダウンロードするのも考えたんですが、信頼できるソフトを探すのも面倒だなと思って（情報漏洩ダメ絶対）、
適当にググってやろうとしたら意外と一発でできなかったのでメモがてら。

# ダメだった方法
※正しい方法を知りたい人は読み飛ばしてね
とりあえず「mac zip パスワード」とかでググると、アプリをダウンロードする以外だと、ターミナルで zipcloak のコマンドを実行する方法が出てくる出てくる。
とりあえず検索結果の上位5位くらいは全部これだった気がする。

実際に、ファイルを右クリックから圧縮した後に、
```
zipcloak [パスワードを付けたいZIPファイル名]
Enter password: [設定するパスワード]
Verify password: [設定するパスワード(再)] 
```
を入力すると、なんやらCRCがマッチしないやらとwarningが出ていますが、一応パスワード付きZIPファイルが生成されます。一応。

ただこのZIPファイル、なぜか設定したパスワードを入力しても解凍できません。
何回正しいパスワードを入力しても、一切に解凍してくれません。

これ、パスワードができた時点で確認せずに、元ファイル削除しちゃってたり、お客さんに送付しちゃってたらだいぶ終わってしまいます・・・


# おすすめする方法
（ここからが本題）
そもそもこのzipコマンドでパスワード付きZIPファイルに変換するのが一番楽で簡単だと思う。
```
zip -e [圧縮後のパスワード付きファイル名].zip [圧縮前のファイル前] 
Enter password: [設定するパスワード]
Verify password: [設定するパスワード(再)]
```
この１行でファイルの圧縮から暗号化まで一発でOK。

# 環境情報

```
OS: macOS Big Sur 11.1
チップ: Apple M1
```

# （補足）
このzipcloakのCRCの問題は、特定の方法でZIPファイルを作成した場合に発生するようです。

finder上で右クリックして、[ファイル名]を圧縮 からZIPファイルを作成すると発生する可能性が高いようです。

細かく確認できていませんが、zipコマンドを使ってzipファイルを作成していれば、このzipcloakで暗号化してもきちんと解凍できるみたいです。
ご注意下さい。

# 参考

https://qiita.com/uhooi/items/949c23d3a20d89413ba5
SQLで行を列に展開する方法(集約関数+CASE)

# はじめに
自宅に設定してある複数の温度計センサーの値をDBに格納しているのですが、いざBIツールに読み込ませてグラフ化を行おうとしたところ全てのセンサーの温度/湿度が同じ列に格納されていてグラフ化には適していないことが判明しました。
しかしながら既に数日分の温度情報を蓄積済みであり今更DBの構成を変更したくはなかったため、SQLでテーブル構成を分析に適した形に変換することで対処できましたので、その備忘です。

# やりたいこと
DBのテーブル構成は下図の左「元のテーブル構成」の通りです。
温度測定用のデバイスが増えてもテーブル構成を変えなくて済むようにデバイスID列で対象を区別できるような列構成としています。

ただし、このテーブル構成では全ての部屋の温度/湿度は同じ列であるため分析には不向きであり、右「行を列に展開した構成」のようにデバイス毎に列を分けることが今回やりたいことになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2509771/cc83de54-eda6-af04-8d2c-80637f908f72.png)

# 行を列に展開する(集約関数+CASE)
集約関数を利用することでGROUP BYで指定した列で集計を行いますが、この時CASEで条件を付与することで一意の対象のデータのみ返すことができます。（下の例ではデバイスIDで対象を指定することで、特定のデバイスIDに対する温度/湿度のみ抽出）

```sql:
SELECT
日時,
MAX(CASE WHEN デバイスID = '書斎のデバイスID' THEN 温度 END) AS 書斎の温度,
MAX(CASE WHEN デバイスID = '書斎のデバイスID' THEN 湿度 END) AS 書斎の湿度,
MAX(CASE WHEN デバイスID = '寝室のデバイスID' THEN 温度 END) AS 寝室の温度,
MAX(CASE WHEN デバイスID = '寝室のデバイスID' THEN 湿度 END) AS 寝室の湿度
FROM テーブル名
GROUP BY 日時
ORDER BY 日時
```

このデータをBIツールに読み込ませることで、以下のような複数系列のグラフを作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2509771/b2fb7788-ff67-e855-5191-c6b11583f555.png)


# おわりに
SQLで行を列に展開する方法について記載しました。
集計用の中間テーブルの作成が不要で、SQL1クエリで実現できるため使えるシーンが多そうです。
100日でSQLの達人になる@LeetCode! Day26 <重要：連続する数値を見つける>

## 1285. Find the Start and End Number of Continuous Ranges （問題レベル: Medium）要課金
今日の問題はこれ。
数値が昇順に並んでいる時、連続している数値を見つけてリストアップするという問題。

この問題は`SQL`に関わらず、日毎データからある行為を継続した日数などの算出にも使える重要な問題です。
最初は`LAG(変数,1)`などを使うのかと考えてしまい、自力ではたどり着けなかった。
初見でこのコードが書ければ素晴らしいと思います。

今回のポイントは`連続する数値を見つける`SQLのクエリだった。

まず解答
```:SQL
SELECT MIN(a.log_id) AS start_id, MAX(a.log_id) AS end_id
FROM
(SELECT log_id, ROW_NUMBER() OVER(ORDER BY log_id ASC) AS num FROM logs) a
GROUP BY log_id - num
```
なかなか理解が難しいと思うがこのコードの肝は`GROUP BY log_id - num`
実際に下記のように数値をリストアップすると理解しやすい。
```
log_id, num, difference
1, 1, 0
2, 2, 0
3, 3, 0
7, 4, 3
8, 5, 3
10, 6, 4
```
これは全て`Discuss`からの情報をまとめたものです。
重要な手法なので自力でクエリが書けるよう復習しよう。

* [LeetCode](https://leetcode.com/)の問題は、`MS SQL Server`で解いています。
プログラミング初心者が稼げないはウソ！実際稼げます

- プログラミングは稼げないって本当？
- どうやったらプログラミングで稼げるようになるの？
- 初心者や未経験から稼げるようになるのはどうしたらいいの？

というお悩みを解決する記事になっています。

僕は現役エンジニアでプログラミングスクール講師でもあります。

副業としてプログラミングだけで最高85万円ほどの実績もあります。

その経験も踏まえた上で、解説していきます。

## 【結論】プログラミング初心者でも稼げます

結論から言うと、プログラミング初心者でも稼ぐことはできます。

金額的に言えば、フリーランスとして活動するとした場合、15万〜30万円は稼げると思います。

ただし、プログラミングスクールなどで正しくプログラミングを学んだ場合に限ります。

## 初心者や未経験からプログラミングで稼げるようになる方法

プログラミングスクールを受講して、正しくプログラミングスキルを身につけましょう。

初期投資として、受講料がかかりますが、それ以上のメリットがあります。

- 分からないところを分かりやすく現役エンジニアに教えてもらえる
- 現役エンジニアの実際の仕事の話が聞ける
- 未経験からエンジニアになるために必要なことを直接聞ける
- 短期間である程度の知識が付くため、すぐに就職・転職ができる
- 短期間で就職・転職できるため、スクール代の回収が余裕で早い
- 完全オンラインなので、場所と時間を問わず学習できる
- 正しい学習方法が分かる

## どうやったらプログラミングで稼げるようになるの？

稼ぐ方法は、フリーランスの案件サイトを活用することです。

以下のようなプログラミング案件が沢山あります。

![crowdworks-ruby.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960209/f136726f-8d15-ad3a-1d99-4c88a38f115b.png)

単価によって変わりますが、

![lancers-ruby.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960209/5838c82d-7a4b-480d-d301-ce92ff32c5a4.png)

数万円から数十万円、100万円を超える案件もあったりするので、十分に稼げるようになりますね。

## プログラミングで稼ぐことやスクールについて、もっと深い内容を知りたい方へ

さらに詳しくプログラミングスクールや副業について詳しく知りたい方は、僕のブログを覗いてみてください。

https://asalworld.com/
重量センサーを使いたい

## 重量センサーを使ってみた

重量センサーで遊びたいと思い、とりあえずobnizに繋いでみました
![IMG_6145.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/143e6ba7-16d2-4418-ed9e-57a16adf0cc7.jpeg)


試しにちゃんと測れるか確認してみます
↓「12グラム」です。ちなみにこのプラモデルはヘキサギアの「ビアンコ」というものです。
![IMG_6144.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/b66dbe87-8d29-4f5c-73fc-470397b2adad.jpeg)

```01.js

const Obniz = require('obniz');
const obniz = new Obniz('xxxx-xxxx');

obniz.onconnect = async () => {
    //ロードセルを使用
    var sensor = obniz.wired('hx711' , {gnd:0, dout:1, sck:2, vcc:3} );
    var value = await sensor.getValueWait(1); //1 times average
      console.log('grams:' + value );
};

```


↓「301971」と計測されました
ちなみに何も置いていないと「297300」になりました。
![IMG_6146.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/02b8a82e-bae3-31b3-d4c1-bac98edac84e.jpeg)


どうやらそのままでグラムにはならないようです。
何も置いていない状態で0グラムになる＆何か置いたらちゃんとグラムで測れるようにしたいです。


調べてみると、
setOffset
setScale
を設定しないとだめのようだったので試してみます。

まずsetoffsetを設定しました。
何も置いていない状態での計測結果をそのまま設定すると、計測値がゼロに近くなりました。
計測器が少なからず歪んでいるのでその補正をしなければいけない。と理解しました。
この状態で先ほどの「ビアンコ」を載せると、5020という数字が出ました。

```02.js

const Obniz = require('obniz');
const obniz = new Obniz('xxxx-xxxx');

obniz.onconnect = async () => {
    //ロードセルを使用
    var sensor = obniz.wired('hx711' , {gnd:0, dout:1, sck:2, vcc:3} );
    sensor.setOffset(297300);
    var value = await sensor.getValueWait(1); //1 times average
      console.log('grams:' + value );
};

```

次にsetScaleを設定しました。
12グラムのものが5020を計測されるので、按分して係数を求めて「418」という数字が出しました。
この数字をsetScaleに設定します。
小数点がたくさんついたので小数点の処理も追加しました。

```03.js

const Obniz = require('obniz');
const obniz = new Obniz('xxxx-xxxx');

obniz.onconnect = async () => {
    //ロードセルを使用
    var sensor = obniz.wired('hx711' , {gnd:0, dout:1, sck:2, vcc:3} );
    sensor.setScale(418); 
    sensor.setOffset(297300); 
    var value = await sensor.getValueWait(1); //1 times average
    var grams = Math.ceil(value);
    console.log('grams:' + grams);
};

```

↓何とか「12グラム」と測定されるようになりました。
![IMG_6146 - コピー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/90d8c775-67f8-9ee6-33df-bbf1dc1e47d2.jpeg)


別のものを載せても正しくグラムが測れたので
ややブレが生じることがあるものの、ひとまずはこれでよしとしました。


## まとめ

重量センサーを使うときは、「setOffset」「setScale」を設定する必要がある。
①何も置いていない状態で計測する。
②計測された数字を「setOffset」に設定する。
③何か適当な重さのモノを計測した数字と、そのモノのグラム数を案分する
④按分した結果を「setScale」に設定する。

とりあえず、この流れをやればブレはあるもののグラムを計測することができそうです。
こういうのをキャリブレーションというみたいです。

多少ブレが生じることもあるので、もっと正しい使い方がありそうです。
数グラムの誤差があってもやりたいことができないことをなさそうなのでこのまま行こうと思います。

どなたか正しいやり方があれば教えてください。
yukicoder contest 333 途中参戦記

# yukicoder contest 333 途中参戦記

## [A 1850 Rewrite Product](https://yukicoder.me/problems/no/1850)

A = X - 1, B = 1 とすれば N を1減らす操作は常に行えるため、N を Y 以上の値にできれば必ず N = Y にすることができる. N が4より大きければ、半々に分ければ値を無限に大きくできる. (例えば A = 3, B = 2 として N = 5 から6に遷移し、A = 3, B = 3 として N = 9 に遷移できる). 以上を踏まえれば、`Y <= X or X > 4` で判定できることが分かる.

```python
from sys import stdin

readline = stdin.readline

T = int(readline())
for _ in range(T):
    X, Y = map(int, readline().split())
    if Y <= X or X > 4:
        print('Yes')
    else:
        print('No')
```
100日でSQLの達人になる@LeetCode! Day25 <IDが一行に２つ並んでいてどちらかに特定のIDがある場合もう片方のIDを取り出す>

## 1264. Page Recommendations （問題レベル: Medium）要課金
今日の問題は設定がやや、ややこしい。
まず次の２つのテーブルがあります。
１． 一行に２つのID(`user1_id`, `user2_id`)が並ぶ友達関係を示す`friendship`テーブル
2. ユーザー（`user_id`）毎に好きなページの番号がある`likes`テーブル
この時、ユーザー１の友達が好きなページをリストアップするという問題。

ややこしいのは、ユーザー１は、user_1にもuser_2にも登場するということ。

Middle問題もEasy問題と比較して格段に難しいという訳ではない。
ひとつずつ考えれば自力で解けた。
最初の私の解答は次の`union`を使ったものです。
```:SQL
SELECT DISTINCT page_id AS recommended_page FROM likes
WHERE user_id IN 
    ((SELECT user2_id FROM friendship WHERE user1_id=1
     UNION
    (SECECT user1_id FROM friendship WHERE user2_id=1)) 
AND 
    page_id NOT IN (SELECT page_id FROM likes WHERE user_id=1)
```
`Discuss`を見るとほぼ私と同じ答えも上位にあったが、最も`Vote`が多かったのは、`CASE WHEN`を使ったもの、
シンプルで賢い記載法だと思いました。
```:SQL
SELECT DISTINCT page_id as recommended_page
FROM (SELECT CASE WHEN user1_id=1 THEN user2_id
                  WHEN user2_id=1 THEN user1_id
                  END AS user_id FROM Friendship) a
JOIN likes b
ON a.user_id=b.user_id
WHERE page_id NOT IN (SELECT page_id FROM Likes WHERE user_id=1)
```

ポイントは`IDが一行に２つ並んでいてどちらかに特定のIDがある場合もう片方のIDを取り出す`２つの方法とした。

* [LeetCode](https://leetcode.com/)の問題は、`MS SQL Server`で解いています。
【Python】並び順を無視してリストを比較するテスト(DeepDiff)

# やりたいこと

```python
>>> dict_in_list1
[
 	{'name': 'Reimu', 'spells': ['Musouhuin', 'niju-kekkai']},
 	{'name': 'Marisa', 'spells': ['non-directional laser', 'star-dust reverie']},
    {'name': 'Alice', 'spells': ['hourai-doll', 'shanghai-doll']}
]

>>> dict_in_list2
[
	{'name': 'Marisa', 'spells': ['star-dust reverie', 'non-directional laser']},
 	{'name': 'Reimu', 'spells': ['Musouhuin', 'niju-kekkai']},
 	{'name': 'Alice', 'spells': ['hourai-doll', 'shanghai-doll']}
]

```

1. dictを要素として持つlistがあり、「同じデータ」かを比べたい.
	- ただし**並び順は異なっていても良い**とする。
2. dictのあるvalue(下例ではspell)もlistとなっている。
	- こちらの要素も**並び順は異なっていても良い**とする。

2の要件は、name: Marisa 要素のspells部分のことです。順番が逆になってますが、これは「同じデータ」として扱いたいです.

```
'spells': ['non-directional laser', 'star-dust reverie']
'spells': ['star-dust reverie', 'non-directional laser']
```

# 解決方法

DeepDiffを使うと、以下のようにして同じデータであるかを比較できる.

https://github.com/seperman/deepdiff

```sh
$ pip install deepdiff
```

## pytest
 
```python
assert not DeepDiff(dict_in_list1, dict_in_list2, ignore_order=True)
```

## unittest

```python
self.assertEqual(DeepDiff(dict_in_list1, dict_in_list2, ignore_order=True), {})
```

# その他の方法

もし、要素であるdictのvalueがstrなどだった場合は、**sortedで並び替える(おすすめ) or assertCountEqualメソッドを使う**のもアリだと思います。

以下のような単純な例だと、key引数を指定したsortを使う方が明瞭なのでおすすめです。

```python
names1 = [
    {"name": "Reimu"},
    {"name": "Marisa"},
    {"name": "Alice"},
]
names2 = [
    {"name": "Alice"},
    {"name": "Reimu"},
    {"name": "Marisa"},
]
```


```python
>>> from unittest import TestCase
>>> case = TestCase()
>>> case.assertCountEqual(names1, names2)  # OK!!
```

assertCountEqualはその名前から↑のような比較ができるようには見えないですが、問題なく機能します。


# 余談


assertCountEqualメソッドの本来の使い方は、おそらく以下のようなケース(要素ごとの数え上げ)だと思います.(多分)

```python
>>> fruits1 = ["りんご", "みかん", "みかん", "りんご", "りんご"]
>>> fruits2 = ["りんご", "みかん", "みかん", "りんご", "みかん"]
>>> case.assertCountEqual(fruits1, fruits2)

AssertionError: Element counts were not equal:
First has 3, Second has 2:  'りんご'
First has 2, Second has 3:  'みかん'
```

メッセージもわかりやすいですね。
【R】 DNA配列データの読み込みと変換


### FASTA形式のDNA配列を読み込む

```R
library(tidyverse)
library(ape)
library(Biostrings)
naseq1 <- readDNAStringSet("seq.fasta")         # -> DNAStringSet
naseq2 <- read.dna("seq.fasta", format="fasta") # -> DNAbin
naseq3 <- read.FASTA("seq.fasta")               # -> DNAbin(list)
```

FASTA形式で書かれたDNA配列を読み込む関数はいくつかありますが、それぞれ出力されるクラスが異なります。class()で調べることができます。naseq2とnaseq3は共にDNAbinですが、後者は内部がlist形式になっていてデータ構造が異なります。便宜的にDNAbin(list)と書き区別することにします。str()でどちらかが分かります。

### FASTQ形式のDNA配列を読み込む

```R
naseq4 <- readDNAStringSet("seq.fastq", with.qualities=TRUE, format="fastq") # -> DNAStringSet
naseq5 <- read.fastq("seq.fastq")                       # -> DNAbin(list)
```

Quality Scoreを反映させることができます。

### NEXUS形式のDNA配列を読み込む

```R
naseq6 <- read.nexus.data("seq.nex") # -> list
```

こちらはlistクラスになります。1文字ずつのベクトルのリストになっています。
次に、系統樹を書くために距離行列を計算してみます。

```R
dist.dna(naseq2)
dist.dna(naseq3)
```

DNAbinとDNAbin(list)クラスを受け付けてくれます。
DNAStringSetとlistはそのままではエラーになります。

### データの変換

以下の関数で変換できます。

```R
as.DNAbin(naseq1)    # DNAStringSet -> DNAbin(list)
nexus2DNAbin(naseq6) # list -> DNAbin(list)
as.DNAbin(naseq6)    # list -> DNAbin(list)
as.list(naseqs2)     # DNAbin -> DNAbin(list)
as.matrix(naseq3)    # DNAbin(list) -> DNAbin
as.character(naseq3) # DNAbin(list) -> list
```

次にDNAStringSetクラスへの変換です。
DNAStringSetには、DNA配列の文字列(1文字ずつのベクトルではない)のベクトルを渡す必要があります。

```R
# DNAbin -> DNAStringSet
DNAStringSet(map_chr(as.character(as.list(naseq2)), ~str_c(.x, collapse="")))

# DNAbin(list) -> DNAStringSet
DNAStringSet(map_chr(as.character(naseq3), ~str_c(.x, collapse="")))

# list -> DNAStringSet
DNAStringSet(map_chr(naseq6, ~str_c(.x, collapse="")))
```


### ベクトル、リストデータとの変換
(2022.8.5 追記)

プログラム中の配列データの場合は、一旦DNAStringSetクラスに変換すると上記の変換ができます。

```R
naseq.vec <- c(seq1="ATGCATAGT", seq2="ATGAAAGGC")
# 名前付きベクトル -> DNAStringSet
naseq7 <- DNAStringSet(naseq.vec)
# DNAStringSet -> 名前付きベクトル
as.character(naseq7)

naseq.list <- list(seq1="ATGCATAGT", seq2="ATGAAAGGC")
# リストデータ -> DNAStringSet 
naseq8 <- DNAStringSet(unlist(naseq.list))
# DNAStringSet -> リストデータ
as.list(as.character(naseq8))
```

### データフレームとの変換
(2022.8.5 追記)
```R
naseq.df <- data.frame(name=c("seq1", "seq2"), naseq=c("ATGCATAGT", "ATGAAAGGC"))

# data.frame -> DNAStringSet
naseq9 <- DNAStringSet(naseq.df$naseq)
names(naseq9) <- naseq.df$name
# DNAStringSet -> data.frame
data.frame(name=names(naseq9), naseq=as.character(naseq9))
```
Hardhat と mocha でテストをした時に pending で終了してしまう問題

hardhat でテストをしている時に `pending` が発生してテストが実行されませんでした。

```sh
  Todo CRUD
    - Should return the new TODO once it's changed


  0 passing (2ms)
  1 pending
```

使っていたテストコードはこちら。

```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Todo CRUD", function() {
  it("Should return the new TODO once it's changed"), async function () {
    const Todo = await ethers.getContractFactory("todo");
    const todo = await Todo.deploy();
    await todo.deployed();

    // don't have todo
    expect((await todo.getTODO()).length).to.equal(0);

    const createTodoTx = await todo.createTODO("do my homework");
    await createTodoTx.wait();

    expect((await todo.getTODO()).length).to.equal(1);
  }
});
```

しかしこのコードは間違っていて It 句が正しく閉じられていませんでした。

```diff
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("Todo CRUD", function() {
- it("Should return the new TODO once it's changed"), async function () {
+ it("Should return the new TODO once it's changed", async function () {
    const Todo = await ethers.getContractFactory("todo");
    const todo = await Todo.deploy();
    await todo.deployed();

    // don't have todo
    expect((await todo.getTODO()).length).to.equal(0);

    const createTodoTx = await todo.createTODO("do my homework");
    await createTodoTx.wait();

    expect((await todo.getTODO()).length).to.equal(1);
  }
- });
+ }));
```

これでうまく動くように！
【Bash】DNA相補鎖を出力する方法

tr と rev を駆使すると簡単に書くことができます。
trで{A,T,G,C}をそれぞれ{T,A,C,G}に変換し、revで文字列を反転します。

```{Bash}
#! /bin/bash
DNA="ATTGAATGATAGA"
echo ${DNA} | tr ATGC TACG | rev # TCTATCATTCAAT
```
続 Linuxについて


## シェルスクリプト

<dl>
  <dt>シェルスクリプトとは</dt>
  <dd>毎回コマンドラインに入力するのが手間だ😟<br>ってときにファイルにコマンドラインを 
              記述しておくことで、<br>あとはファイルを実行するだけでよく作業の自動化、効率化が 
              可能。 
 </dd>
</dl>

### シェルスクリプトを作成するメリット

- 同じ処理を実行したい際に再利用が可能
- 他社に配布することができる
- コマンドを打ち間違える事による作業ミスをなくすことができる

### シェルスクリプトの作り方

1. 拡張子は慣習的に`.sh`をつけているがなくてもOK
1. ファイル内の先頭に必ず`シェバン` = `#!/bin/bash`を記述
1. ファイル内にスクリプトを記述

> `シェバン` = `#!/bin/bash`を記述

<dl>
<dt>シェバンって?</dt>
<dd>Linuxカーネルにこのシェルスクリプトを<em>/bin/bash</em>で動かしてくださいね。と伝えるためのおまじない</dd>
</dl>

### シェルスクリプトの実行
1. 作成したシェルスクリプトに実行権限を追加 -> `chmod +x <ファイル名>`
1. ファイル名を指定して実行。※この時単にファイル名の指定ではエラーになる
```
 $ example.sh
 -bash: example.sh: command not found
 $ ./example.sh
 シェルスクリプトの処理内容 
```

### 制御構文
`if`,`for`,`case`,`when`などのプログラムを制御するための便利な構文。

#### if文
条件分岐を行える。

##### 記述例
```
#!/bin/bash
if [ "$1" = "bin" ]; then
   echo "OK"
else
   echo "NG"
fi 
```
`if`の後の`[`は実はコマンドです。
コマンドなので前後にスペースが必要。

##### [  コマンドについて
引数に与えられた条件式を判定して正しければ0,そうでなければ0以外の終了ステータスを返す。

<dl>
<dt>終了ステータスとは</dt>
<dd>全てのコマンド終了時に帰ってくるステータスで整数値で返ってくる</dd>

<dt>正常終了時</dt>
<dd> 0 </dd>
<dt>エラー時</dt>
<dd> 0以外の値</dd>
</dl>

シェルスクリプトで使用する`if`はこの終了ステータスによって条件分岐をしているんだね💡


##### 条件式に使用する比較演算子一覧

###### 文字列の比較
| 記号 | 意味 |
| -----|-----|
| =       | 左辺と右辺が等しい |
| !=       | 左辺と右辺が等しくない |
| -n      | 引数に与えた文字列ではない |

###### 数値の比較
| 記号 | 意味 |
| -----|-----|
|  -eq       | 左辺と右辺が等しい |
| -ne       | 左辺と右辺が等しくない（not equal） |
| -lt      | 左辺が右辺より小さい（less than） |
|  -le       | 左辺と右辺以下（less equal） |
| -gt       | 左辺が右辺より大きい（greater than） |
| -ge      | 左辺が右辺以上（greater than） |

## まとめ
シェルスクリプトはカスタマイズ性が高いことに感心しました。
実務においてはよく使用する操作、一連の流れみたいなのができると思うので、
そういった際にシェルスクリプトを組んで作業を効率化することで、
開発に多くの時間を割くことができ、高い生産性を発揮できるのだと実感しました🧐  🧐
マイパソでLaTexを使うために、独自に作成したクラスのパスを通す

## はじめに
私の研究室では LaTex を使って卒論や論文を執筆します。研究室の PC には元々 LaTex が入っていますが、わざわざ学校の PC を使わずとも、マイパソで作業したい！
そんな要望に応えるため、（主に研究室の後輩を想定読者として）LaTex の準備方法について書き落としておきます。
~~なお、Overleaf みたいにクラウドでやればいいじゃんとは思うのですが、先生へ Overleaf を使う許可を求められずにいます。学校に行きたくない意思表示と取られると嫌なので…~~

## 1. インストール
[こちら](https://www.tug.org/texlive/acquire-netinstall.html)の「For typical needs, we recommend starting the TeX Live installation by downloading install-tl-windows.exe for Windows (~20mb)」をクリックして、install-tl-windows.exeをダウンロードします。これはインストーラーであり、インストールをするためだけの実行ファイル。ダウンロードが終わったら消して大丈夫です。誘導に従って LaTex をインストールしましょう。かなり時間がかかりますので、時間が取れるときにやるのを推奨。
なお、先生の言いつけを守っている後輩なら AVG をPC に入れているはず。AVG が大丈夫？と途中で聞いてきますが、大丈夫です。また、LaTex のファイルを置く場所はデフォルトのままで大丈夫です。
LaTex を入れてみたら、ひとまずテキトーにファイルを作ってコンパイルしてみましょう。\begin{document} とかのあれです。

## 2. 先輩の LaTex ファイルをコンパイルしてみよう
先輩と同じフォーマットを使って LaTex を使っていくかと思います。そこで、まずは先輩のファイルをコンパイルしてみましょう。おそらくエラーとなるはず。先生が昔作ってくださったフォーマットファイル(jarticle_zemi2008)が無いからです。
そこで、**先輩から jarticle_zemi2008 をもらってください。**フォルダごともらうのが手っ取り早いと思います。USBを使うと便利です。

C:\texlive\2021\texmf-dist\tex\xelatex\jarticle_zemi2008 

ここに多分入っているはず。但し、texlive を使っていなかったり、バージョンによって2021でなかったりするので注意。texlive でないなら、My style みたいなフォルダにあったと思います。
そして、下の写真のエクスプローラーのように、xelatex というフォルダ内に入れましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530290/a008fdd9-b002-c54d-3fa1-71d5880e85a6.png)

##  3. jarticle_zemi2008 の path を通そう
このままでは，LaTex が jarticle_zemi2008 があるとみなし，紐付けてくれません。そこで、path を通す必要があります。余談ですが、path って何か分からなくても LaTex は使えるものの、自分で一応調べ、理解することを推奨しておきます。
本題へ。以下のjarticle_zemi2008フォルダ内にて、Alt + D をします。すると、以下のように現在のフォルダの場所（ディレクトリといいます）が青色で選択されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530290/17029083-1c91-0bc3-c08d-b852cf9e6200.png)
そして、cmd と打ち込みましょう。Mac でいう、ターミナルが開かれます。ターミナルは、講義で習いましたよね？以下のようなのが開かれれば OK です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530290/f05cb0e7-1961-8ad9-85f7-b8b25d26beb8.png)
さて、ここで mktexlsr と入力し、Enter です。処理が終わるのを待ちましょう。あとは、普通に LaTex が使えるようになります。ただし、以下に注意です。

・コンパイラーは pLaTex(ptex2pdf) で。このあたり、詳しくは私も理解できていないので明文化は避けておきます。
・文字化けしているときは、エンコーディングを shift-jisに変えれば OK です。

## 4. 最後に
以上、LaTex をマイパソで使うための流れを書きました。ただ、LaTex は色々なバージョンがあるようですし、暫定的に使えているから紹介したものの、これが最適解とは言えません。あくまで、私の試行錯誤の結果、これに行き着いたって感じです。LaTex を入れるときは、とりあえず先輩に相談しましょう。多分、なんとかしてくれるはず。多分。

## 5. 参考文献
・[Installing TeX Live over the Internet](https://www.tug.org/texlive/acquire-netinstall.html)
・[styファイルを置く場所・置いた後にすること](https://ossyaritoori.hatenablog.com/entry/2016/10/13/032502)
・[TeX Wiki LaTeX入門/各種パッケージの利用](https://texwiki.texjp.org/?LaTeX%E5%85%A5%E9%96%80%2F%E5%90%84%E7%A8%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E5%88%A9%E7%94%A8#tc299bdb)
二次元連想配列 foreachで取り出す(Javascript)

Javascriptの二次元連想配列の要素をforeachで１つずつ取得する流れについて自身の理解を整理。

```Javascript
let NBA =[
            {region:'oklahoma', team:'Thunder', player:'KD'},//キー値=[0]
            {region:'sanantonio', team:'Spurs', player:'parker'},//キー値=[1]
            {region:'washington', team:'Widzards', player:'hachimura'}//キー値=[2]
            ]
for (let count = 0; count < NBA.length; count++) {
            Object.keys(NBA[count]).forEach(function(key) {
            console.log(key + '=' + NBA[count][key]);
            });
        }
```
変数countを二次元連想配列NBAのindex番号(キー値)として使用する。ループする条件としてcountが二次元連想配列NBAのlength(=3)未満とすることでキー値が[0]~[2]までの各配列を扱える。
Object.keys(NBA[count])によって[0]~[2]までの各配列毎にキー値を取り出してforEachで１つずつ受け取り処理をする。
### 例.count=0の時 ###
Obkect.keys(NBA[0])となる事から、
配列[0]{region:'oklahoma', team:'Thunder', player:'KD'}のキー値である**region, team, player**が取り出される。
配列[0]において**region**が取り出された時にはコールバック関数の仮引数に**region**が渡される為、console.logで以下のように表示される。
#### region=oklahoma ####
上記のように**oklahoma**となるのはcount=0によってNBAから連想配列[0]が取得されており、且つその配列のキー値がregionである要素の値に該当する為。
残りのNBA[1], NBA[2]も同じように一つずつ取得された連想配列からそれぞれのキー値に対応する要素の値が出力される。
AWSロードマップ(CloudFormation)

#目次
[1.はじめに](#1-はじめに)
[2.用語](#2-用語)
[3.ドローンでクラウドを利用](#3-ドローンでクラウドを利用)

# 1.はじめに
ドローン✖︎クラウドの記事をよく見るけど、どうゆう仕組みで連携してるのか分からなかったので
[AWSロードマップ](https://qiita.com/KurokawaKouhei/items/4e9aa3b526f3f233bf85 "Qiita Home")で勉強をしたいと思います！
(まずはざっくり覚えて徐々に深堀していく、仕事でAWS少し触ったぐらい)

#### CloudFormationとは
##### 例えてみると
1. カレーのレシピを作る
1. 1のレシピをCloudFormationに入れるとカレーを作ってくれる
みたいなイメージ！

1のレシピはテンプレートと呼ばれててテンプレートはスタックと呼ばれるもので管理されてる。
もしシチューを食べたいなーと思ったら、
1のレシピのルーをシチューのルーに変えると簡単にシチューを作れる。

##### きっちり言うと
- プログラミング言語やテキストファイルを使用してAWSリソースを自動で構築するサービス
    - リソース(AWSが提供してるサービス,EC2とか)作成時のテンプレートを作成でき、これをもとに新しい環境を構築できるので時間や手間を削減できる


# 2. 用語
![スクリーンショット 2022-02-23 11.04.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2181084/a83cde02-20a3-d530-6beb-240293ee5fc1.png)

#### Parameters
- 構成はほとんど同じやけど一部だけ異なる複数の環境を複数作りたい時に使う
    - インスタンスタイプが違うEC２とか
  
#### Mappings
- キーとそれに関連する値のマッピングを作成することができる
- 同じテンプレートでも場合によって設定を変えれる
    - ステージング環境と本番環境を切り替えるとか

#### Conditions
- 条件式を記載する場所
    - Parametersセクションで作成した変数を呼び出し、本番環境を選択しているかどうかを判定するとか

#### Resources
- スタックに含めるAWSリソースを宣言

#### Outputs
- 別スタックで値を参照( ImportValue )する
- CloudFormation コンソールに値を出力する

# 3. ドローンでクラウドを利用
- 最近[ドローンでLTE通信](https://news.kddi.com/kddi/corporate/newsrelease/2022/02/15/5885.html "ドローンでLTE通信")とかが増えてきた
    - そうなると遠隔地からリアルタイムでドローンのカメラの映像を監視
        - AWS IVSを使った事あるけど簡単にリアルタイム通信できた
    - ドローンから直接クラウドに映像・画像をアップして何かしらの処理
  
とかが必要になるやろうからドローンの開発する場合、クラウドの知識もあった方がいいんかなと思う！
【Rails】クラスメソッドとインスタンスメソッドの使い分け

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## クラスメソッドとインスタンスメソッドの使い分け

### クラスメソッドとインスタンスメソッドの違い
クラスメソッドとインスタンスメソッドの違いは

- メソッドの記述方法
- メソッドの呼び出し方法

です。

#### メソッドの記述方法
クラスメソッドの場合には以下のようにメソッド名の頭に`self`を記述します。

```ruby
  def self.exists_no_name_user?
    no_name_user_list = User.where(name: nil)
    no_name_user_list.exits?
  end
```

インスタンスメソッドの場合には以下のように`self`のない形になります。

```ruby
  def full_name
    self.first_name + self.last_name
  end
```

#### メソッドの呼び出し方法
クラスメソッドは`クラス名.クラスメソッド`のように記述し、クラスに対してメソッドを呼び出します。

```ruby
  User.self.exists_no_name_user?
```

インスタンスメソッドは`インスタンス名.インスタンスメソッド`のようにインスタンスに対し、メソッドを呼び出します。

```ruby
 kato = User.new(name: 'kato')
 kato.full_name
```

### クラスメソッドとインスタンスメソッドの使い分け
クラスメソッドの特定のインスタンスに対して実行したいメソッドはインスタンスメソッドで定義し、クラス全体に対して何等かの処理を行いたい場合に使用します。
ActiveJob でジョブのメモリ使用量がしきい値を超えた場合に通知する

# やりたいこと

- ActiveJob でジョブのメモリ使用量がしきい値以上だった場合に通知したい。
  - 僕の具体的なユースケース: Sidekiq で大量にメモリを使用して、かつ解放されない場合が結構ある。そこで、具体的にどのジョブが関係しているのかを調べたい。

# 方法

- [around_perform](https://api.rubyonrails.org/v7.0/classes/ActiveJob/Callbacks/ClassMethods.html#method-i-around_perform) メソッドを使う。
  - 今回は [Rollbar](https://rollbar.com) というエラーモニタリングサービスで警告を通知する。
  - Rollbar.warning の部分は、環境に応じて任意の通知方法に置き換えること。

```app/jobs/application_job.rb
class ApplicationJob < ActiveJob::Base
  around_perform do |job, block|
    MonitorMemoryUsage.call(job, &block)
  end
end
```

```app/jobs/monitor_memory_usage.rb
module MonitorMemoryUsage
  extend ActionView::Helpers::NumberHelper

  class HighMemoryUsage < StandardError; end

  THRESHOLD = 100.megabytes

  def self.call(job)
    before_memsize = ObjectSpace.memsize_of_all
    results = yield
    after_memsize = ObjectSpace.memsize_of_all

    usage = after_memsize - before_memsize

    if usage > THRESHOLD
      message = "#{job.class.name} が #{number_to_human_size(usage, locale: :en)} のメモリを消費しました。"
      Rollbar.warning(HighMemoryUsage.new(message), arguments: job.arguments)
    end

    results
  end
end
```

こんな感じ :eyes: 

<img alt="Rollbar warning" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6697/5cdef5eb-e497-e5ab-3670-67c3f2a9d23c.png" width=600>
form_withについて

## はじめに
form_withは初学者にとって理解しずらい部分だと思います。
modelオプションとurlオプションのところで、よく分かってなかったことがありました。
## フォームとは
ユーザーが情報を入力する入力欄のこと。
アカウント登録ページに出てくる名前とかemailとかを入力する四角い箱の事です。
## form_withとは
フォーム作りを助けてくれるヘルパーメソッドです。
ヘルパーメソッドとはRailsにおける、主にビューでHTMLタグを出現させたりテキストを加工するために使うメソッドの総称です。

例えば、HTMLで以下のように書くのを

```html
<form action="/tweets" method="post">
    <input type="text" name="content">
    <input type="submit" value"送信">
</form>
```

以下のように書きます。

```ruby
<%= form_with url: "/tweets", method: :post, local: true do |form| %>
  <%= form.text_field :content %>
  <%= form.submit '送信' %>
<% end %>
```

ヘルパーメソッドはRubyとして取り扱われるので、＜％＝　％＞で囲んで表示します。
doからendまでの間がフォームの記述になります。

## form_withのオプションについて

```ruby
form_with(model: 〇〇, url: 〇〇, method: 〇〇・・・)
```
form_withの後に続く引数（オプション）がたくさんあります。
それぞれフォームがどのような動きをするか決めています。
## modelオプションとurlオプションについて
ここの理解が不十分でした。
urlオプションはフォームでいうaction属性と同じ役割で、modelオプションはインスタンス（データを保存する変数）をフォームに渡す役割だと思っていました。
間違いではないです。しかし、これだけの認識だとハマります。

なぜならurlオプションの記述がないform_withがあるからです。
データの送り先はどうやって決まるのか。
なぜurlオプションがないのか。

```ruby
<%= form_with model: @tweet, local: true do |form| %>
```
実は上記のコードは
```ruby
<%= form_with　model: @tweet,　url: tweet_path, local: true do |form| %>
```
と同じ意味です。

このように記述できるのには理由があります。
## form_withの賢さ
form_withは`@tweet`という変数の中身があるか、ないかで次のアクションを予測しています。
`@tweet`が空の場合はtweets_path(createアクション)へデータを送る。
`@tweet`が何かデータを持っていた場合tweet_path(updateアクション)へデータを送る。
このようなことを勝手に行ってくれます。
つまりurlオプションで送り先を指定しなくてもいいのです。

| オプション名   | 意味 |
|:-----------:|:-----------------------------:|
|modelオプション |入力値を格納するインスタンス（form_withによるaction属性の指定）|
|urlオプション   |フォームで入力した情報の送信先を指定  |
|methodオプション|情報の転送方法を指定。主にgetかpost|
|localオプション |リモート送信を無効にするかどうかを指定。trueにすると無効になる  |

## おわりに
form_withはまだ分からないことがあります。
また間違いなどございましたら、コメントいただけると幸いです。
お手数お掛けします。よろしくお願い致します。

## 参考
https://zenn.dev/atsushi101011/articles/d7cedfa463dffc

https://skillhub.jp/courses/158/lessons/839
[JavaScript]日付、時間の書く順番をその国、言語圏の記法にする

# toLocaleDateString()
国や言語圏によって年、月、日を書く順番は違います。
```toLocaleDateString.js
//現在時刻の取得
let date = new Date();

//アメリカ式表記
console.log(date.toLocaleDateString("en-US"));
//  2/25/2022と月・日・年の順に表示される

//イギリス式表記
console.log(date.toLocaleDateString("en-GB"));
//  25/02/2022と日・月・年の順に表示される

//アラビア語圏
console.log(date.toLocaleDateString("ar"));
//  25‏/2‏/2022と日・年・月の順に表示される(日と年の間に/がないことと一番最後に/があることに注意)

console.log(date.toLocaleDateString("ar-EG"));
//  ٢٥‏/٢‏/٢٠٢٢とアラビア数字になる

//対応していないかもしれない言語を要求した場合
//下は日本語での表記方法を優先し、もしそれが出来なかった場合アメリカ式表記で表示するという意味
console.log(date.toLocaleDateString(["ja", "en-US"]));
//  日本語には対応しているので2022/2/25と表示される
```

# toLocaleTimeString()
```toLocaleTimeString.js
//現在時刻の取得
let date = new Date();

//アメリカ式表記
console.log(date.toLocaleTimeString("en-US"));
//  10:20:40 PMと12時制で表示

//イギリス式表記
console.log(date.toLocaleTimeString("en-GB"));
//  22:20:40と24時制で表示

//アラビア語圏
console.log(date.toLocaleTimeString("ar"));
//  10:20:40 مと12時制でAM/PMがアラビア語で表示

console.log(date.toLocaleTimeString("ar-EG"));
//  ١٠:٢٠:٤٠ مとアラビア数字になる

//対応していないかもしれない言語を要求した場合
//下は日本語での表記方法を優先し、もしそれが出来なかった場合アメリカ式表記で表示するという意味
date.toLocaleTimeString(["ja","en-US"])
//  日本語には対応しているので22:20:40と表示される
```

# 参考資料
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString
MySQL  参考サイト

### アクセス権限システムの基礎

https://japanrock-pg.hatenablog.com/entry/20090430/1241081836

### root ユーザがNGな理由　

https://style.potepan.com/articles/18695.html

### ユーザ管理　作成・パスワード・権限

https://www.karakaram.com/mysql-user-grant/

### ユーザー削除　コマンド：DROP USER

https://uxmilk.jp/50835

### 匿名ユーザ削除　コマンド：mysql_secure_installation

https://tex2e.github.io/blog/database/mysql-secure-installation

### root のパスワードを忘れた時 

https://beyondjapan.com/blog/2020/07/mysql-password/

### root@%とroot@localhostのパスワードが同じかどうか確認する方法

https://style.potepan.com/articles/18389.html

### lower_case_table_names

https://moznion.hatenadiary.com/entry/2015/04/13/005641

## Docker で MySQL を使う場合に

### Docker コンテナ間 接続

https://mseeeen.msen.jp/connection-from-php-to-mysql-on-docker/
AWS SAAに一回落ちたヤツが語る合格への道

# 筆者について
WEB系は3年目くらいのエンジニア。バックエンド担当。
AWSは仕事上触るが、既に組み上がったものを設定を変更するくらいだった。

# 受験しようと思った理由
触っていたAWS環境に納得いかない設定や構成があった。
でも何も知らないヤツに改善はできない、だから勉強しようと考えた。

# １回目の受験編
■勉強期間
平日2時間、土日3時間くらいを３週間続けた。
ネットの合格体験記を見ると大体そんなところが多いように思ったからだ。

■学習方法
まずは、いわゆる`オレンジ本`
をメルカリで買って試験概要とサービスの概要を知ることにした。

つぎは、`ネット無料問題サイト`
問題をもっと解かないと受からんだろと思い、無料で200問くらいできるサイトで満点になるまで何周も繰り返した。
正解が多くなるにつれて自信がついてきた。

最後に、`Udemy`
有名だと思うが、`これ一本でOK`のやつをハンズオン編は気になるところだけ視聴し、あとは模擬試験を満点になるまで繰り返した。
この時は「この模擬試験は難易度が高いな...こんなの出るのか？」と思っていた。
（これは2回目の試験時にも使っていたので買ってよかったと思う）


### いざ受験
この時はもう結構自信がついていて、ほぼ受かるだろうと思っていた。
なにせあれだけ問題を繰り返してサービス内容を頭に叩き込んだのだから。

### 結果
680点で落ちました。（合格ラインは720点以上）
問題が本やネットで見た試験内容よりもかなり難しかった...
本やネットの問題よりも深くサービスの設定方法を聞かれたり、最新のサービスについての問題も出て完全に見たことない単語がかなり出ました。


# ２回目の受験編
## 敗因と対策を考えた
## 敗因
■サービスについて隅々まで知らなかったこと
「サービスを知ってる　＝　細かい設定から最新の情報まで知っている」という事なのだ。
例えば、EC2のスポットインスタンスは途中でターミネートされるサービスと思っていたが、スポットフリートを使えばキャパシティを維持できるといったまで知っている必要がある。

■「ソリューションアーキテクトだったらどう考えるべきか」が抜けていた
そもそも「課題に対してどうすれば解決できるか」をベースに考えなければいけないのだ。
試験問題と捉えるのではなく、`Well-Architected`の考えを持って、どのようにすれば課題解決できるかで考えるべきだった。

## 対策
### できるだけ難しい問題を解きまくる
本屋やUdemyで売ってる問題集をできるだけ解きまくって、あらゆる角度からサービスについての知識を深めまくった。

■ おすすめ問題集
細かい問題や最新の問題も出てるし、解説も詳しい。
・[【2022年版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/share/101rfM3@b_euaLQM6mPFuDYps7g5A5pmeIzjzombakOTWm9ss9himtmXQRxb3POIOmX-br6Z/)
・[AWS認定ソリューションアーキテクト-アソシエイト問題集](https://www.amazon.co.jp/AWS%E8%AA%8D%E5%AE%9A%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88-%E3%82%A2%E3%82%BD%E3%82%B7%E3%82%A8%E3%82%A4%E3%83%88%E5%95%8F%E9%A1%8C%E9%9B%86-%E5%B9%B3%E5%B1%B1-%E6%AF%85/dp/486594303X)

### メモする
自分の場合は、notionに各サービスのことを`自分の言葉`でまとめてました。
「このサービスは例えばこんな時に使うやつ」みたいな感じで、自分が考えた言葉や例えでノートにまとめてました。
合格して振り返ってみるとこれが結構当たりだったと思います。

## 結果
もう３週間勉強して、見事合格することができました。
２週間で再度受験できるのですが、考えを改めてゆっくり定着させてから受験しました。

## まとめ
受かりましたが、試験中は合格できるか結構不安な感じでした。
どれだけ勉強していても、答えが全くわからない問題は少なくともありました。
やはり継続して勉強していくことが大事だと思います。
ただ実際詳しく勉強したことで、実務でもコスト削減したりできたので、当初の目的は達成できたと思います。

## 余談
■ ネットの無料問題はオススメしない
模擬問題などをやっていて個人的に重要だなと思ったのは、`問題の解説が信頼できるか`だと思います。
ネットの無料のやつをやっていて、解説に納得行かないのがあったりして、「これ正解も合ってんのかな...？」と疑心暗鬼になる。
上記のおすすめは、問題出してる人が元AWSの人だったり、それと同等に詳しい人だったりするので、解説も信用しやすかった。

※ネット無料問題のやつが悪いわけではない。勉強し始めはいいかもしれないが、これに時間を割くのは勿体ない気がする。
AWSのS3で画像が保存できない

簡易的なフリマアプリを作成中。

AWSのS3に画像を保存するための実装を実施。
必要なファイルや記述なども終え、まずローカル環境で画像がS3に保存できるかの挙動確認を行う。


が、__エラー発生。__

エラー文は以下の通り。

```
NoMethodError (undefined method `upload' for nil:NilClass): 
```

「スペルミスでもしたかな？」

まずは今回新たに追記した`config/environments/development`と`config/storage.yml`のファイルを見直す。
しかし特に間違っている様子はない。

次にエラー内容をgoogleにて検索。
すると、同じ境遇の方々がたくさんいらっしゃいました笑
それぞれの記事を見ていると「:が抜けている」や「""を忘れている」などの内容が多かったので、再度その辺りを確認するため、各コードを見直し。
が、やはり間違いが見当たらない・・・

S3に入る直前に挙動確認を行った際は、問題なく画像が保存されていたので、コードの書き間違いは考えにくい。
間違いがあるのはconfigの箇所であることはほぼ確定している。
でもどこが間違っているのかが全くわからない・・・

するととある記事を発見。そこには「余計なスペースが入っていた」と書かれている。

「スペース？そういえばそこは意識してなかったかも」

再度コードとにらめっこ開始。

すると・・・

__発見！！！__

```:config/storage.yml
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

 amazon:
  service: S3
  region: ap-northeast-1
  bucket: バケット名
  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
  secret_access_key:  <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
```
`amazon:`の前に半角スペースが！
調べてみると、半角スペースがあるせいで、上記の`local:`の続きと見なされてしまうのだとか。

早速この余計なスペースを削除して、再度挙動確認！
が、まだ保存できない・・・

さらに１時間考えた末、出した結論は

__PC再起動__

すると無事にエラー解決できました笑
複数のドメインが稼働しているかを監視してChatWorkに通知してくれるシェルスクリプトを作成した件

# 概要

SSL証明書を監視する対象ドメイン（400件)が稼働しているかを調べる必要があったのでシェルスクリプトで確認できるようにした

https://qiita.com/Kakimoty_Field/items/754df3a181187fbb517c


```bash
#!/bin/bash

domains=(
  https://example.com/
  https://www.yahoo.co.jp/
)

MAXCOUNT=${#domains[@]}

for v in "${domains[@]}"; do
  count=$((count + 1))
  code=$(curl -m 2 -o /dev/null -s -w %{http_code} $v)
  TITLE="HTTPレスポンスが返ってきたドメイン"

  # ステータスが返ってきているドメインを取得
  [ "$code" -ne 000 ] && MESSAGE+="
    $v"

  # ステータスが1つも返らない場合・監視ドメイン終了するまで実行
  if [ -n "$MESSAGE" -a ${count} == ${MAXCOUNT} ]; then
    echo 'END'
    curl -X POST -H "X-ChatWorkToken: xxxx" -d "body=[info][title] $TITLE [/title] [toall]$MESSAGE [/info]" "https://api.chatwork.com/v2/rooms/xxxx/messages"
    exit
  fi
done
```

![スクリーンショット 2022-02-25 22.14.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555632/081b4b2d-9622-d3e4-f30f-5ec93483d4c3.png)


# 参考

https://qiita.com/Kakimoty_Field/items/754df3a181187fbb517c

https://qiita.com/Nelson605/items/4f01db78dbd1c6e14410

https://stackoverflow.com/questions/48260566/curl-does-not-work-in-script

https://qiita.com/naoki_haba/items/12b18731393110da8700
Nginx  参考サイト

https://heartbeats.jp/hbblog/2012/01/nginx01.html

https://heartbeats.jp/hbblog/2012/02/nginx02.html

https://heartbeats.jp/hbblog/2012/02/nginx03.html

https://heartbeats.jp/hbblog/2012/04/nginx04.html

https://heartbeats.jp/hbblog/2012/04/nginx05.html

https://heartbeats.jp/hbblog/2012/06/nginx06.html


https://heartbeats.jp/hbblog/2014/07/3-tips-for-nginx-on-docker.html

https://heartbeats.jp/hbblog/tech/nginx/

## 設定ファイル

https://hack-le.com/nginx/
VSCODEでstreamlitのパスが認識されないエラー

VSCODEで以下のように実行すると「could not be resolved（reportMissingImports）」とエラーが発生した。（streamlit、numpy、pandasすべてで）
なんとか、解決できたため備忘録として記載しておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544095/b9eca192-191d-9023-8d09-b8aacf337419.png)

エラー内容「reportMissingImports」を調べてみると、以下のように表示されstreamlitを探せていないように感じた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544095/c3a268ef-e2db-2575-57c3-6290bf0e3d40.png)

ターミナルでpythonと打ち、対話モードへ
import streamlitと
print(streamlit.__ file __)も入力。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544095/fa479321-9b19-43f8-5c55-99597388cecf.png)

そうすると、streamlitの場所（パス）が表示される。
C:\anaconda3\lib\site-packages\までをコピー

VSCODE→ファイル→ユーザー設定→設定→設定の検索→extra pathと入力

Python › Analysis: Extra Pathsを見つけて、項目の追加を選択し
先ほどコピーしたC:\anaconda3\lib\site-packages\を貼り付ければok
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2544095/ff4cf0ec-47fd-3eb9-8ece-db50a89f78ba.png)

エラー内容を調べて理解し、自分が欲しい情報を探し出す力が必要だと感じた。

以下、参考にしたサイト
参考：Visual Studio Codeでライブラリやモジュールが could not be resolved になる時の対処法
URL：https://startlab.jp/learning-python/vscode-settings/
[JavaScript]数字をカンマ区切りにすることができる地味に便利な機能

# toLocaleString()
toLocalString()は数字をカンマ区切りにし、文字列に変換します。
```toLocaleString.js
let a = 10000;
console.log(a.toLocaleString());
//'10,000'とカンマ区切りで表示される

let b = 1000.12;
console.log(b.toLocaleString());
//'1,000.12'と表示
```
Scratchのスプライト大きさ変更

# Scratchのスプライト大きさ変更

scratchなどの子供向けプログラミングの知識は基本Qiitaなどの記事にしないかもしれませんが、
___
自分で気づいたことなので書いておきます。
___
1:謎の大阪弁の子供2:謎の大阪弁の大人

1:何言ってんのお父さん、スプライトの大きさは大きさブロックとか、
  スプライトの設定画面とかなんかで変えれるやん。
___
2:でもな、たまに大きさを300くらいにやろうとしてもな、
  100に自動で設定されてもうて、大きさを思うように変えられへんことがあんねん。
___
1:なんでそうゆうことがあんの？
___
2:どうやらスプライトのもともとの大きさがデカかったらそうなるらしいねん。
  やけど、変えられへんかったら、スクロールするゲームとかで困るやろ。
___
1:じゃあどうすんの？
___
2:パパがそれの解決法を昨日見つけてん。
___
2:それはな、そのスプライトに適当にちっちゃい
  1×1くらいの大きさのコスチューム追加してな、
___
  ほんでそのコスチュームの状態でやりたい大きさに変えて、
  ほんで元のコスチュームにもどす。これでいけんねん。
___
2:大きさだけの問題らしいから、
  バグ？みたいなことでいけるんやって。
___
1:へー。でもバグみたいなんを使ってもいいのかな？
___
2:分からんｗ
PCを新調してやったことメモ

まずはedgeを使ってchromeを入れてから・・・
VSCodeをインストール。その他の話
# Windowsターミナルを入れる
Microsoft Storeから入れると簡単。
（すでに入ってたけど）
# WSL2を入れる
```bash
wsl --install -d Ubuntu-20.04
```

## WSL?
Windows Subsystem for Linux
Windows上でLinuxを動かせる。Dockerでの開発に必要
Linux系のOSの一つにUbuntuがあって

## Ubuntuも一緒に入れる
### Ubuntu？
Windows上でLinuxコマンドを使えるようになる。

# githubの認証情報を設定する
とってもわかりやすかった↓
https://qiita.com/shizuma/items/2b2f873a0034839e47ce
## 公開鍵と秘密鍵をつくる

ホームディレクトリに移動し、
```bash
ssh-keygen -t rsa
```
このコマンドで、ホームディレクトリに`.ssh`フォルダができる。
.sshフォルダの中は、
```bash
~/.ssh$ ls
id_rsa  id_rsa.pub  known_hosts
```
この３つのファイルで、`id_rsa.pub`ファイルの中身が公開鍵
## githubで公開鍵を設定する
sshkeyはPCの買い替えなどではこの設定をするので、
設定時のsshkey titleにはPCの名前を入れたりした。
keyの中身は、id_rsa.pubからコピペする。
##きちんと接続されたかチェック
```bash
~/.ssh$ ssh -T git@github.com
```
上記コマンドの結果が
`Hi 〇〇!`と続くメッセージなら成功！
ストーカーウェア とは

## 勉強前イメージ

ずっと追ってくる的なやつ？

## 調査

### ストーカーウェア とは

スマートフォンのアプリで悪意を持った人が被害者を監視して追跡するための監視用アプリの一種になります。
被害者のパートナーや家族など様々考えられますが、物理的にデバイスにアクセスできる人によってインストールされることがほとんどです。
ペアレンタルコントロールの意味で子供の見守るためのアプリなどがありますがこれを利用してストーカーウェアを行います。
正しい用途のために販売されているので、
違いは導入の意図が子供の安全を守るという目的があります。

### ストーカーウェアで送信する情報

ストーカーウェアで送信数情報は以下があります。
また、遠隔操作が可能なものもあり他の不正なアプリや情報が抜かれる可能性があります。

- 位置情報
- メッセージ
- 連絡帳
- カレンダー
- 画像

### ストーカーウェアの対策

- 画面のロックを行う

ストーカーウェアは身近な人の可能性があります。
そのため、目を離したすきにインストールされることを防ぐよう、指紋認証や顔認証などの生体認証を設定しておくのが良いです。

- アプリの定期確認

見覚えのないアプリがインストールされていないか定期的に確認するのも有効です。

- セキュリティソフトのインストール

Androidのスマホの場合、セキュリティソフトのインストールも有用です。


## 勉強後イメージ

これってIT系のセキュリティというか・・・・
まぁこういうのもあるって知った。

## 参考

- [ストーカーウェア](https://eset-info.canon-its.jp/malware_info/term/detail/00159.html)
- [ストーカーウェアから身を守るための自衛策とは](https://www.is702.jp/special/3628/)
BigQueryで請求先アカウントを登録したのにBilling has not been enabled for this projectというエラーが出る

BigQueryでデータの挿入するには請求先アカウントの登録が必要なのですが、そうしたにもかかわらずタイトルのエラーが出てしばらく沼りました。同じエラーが出た人のために、自分が確認したポイントを共有します。

## 確認ポイント
- https://console.cloud.google.com/billing で請求先アカウントが有効になっている
- https://console.cloud.google.com/billing/projects にプロジェクトが表示されている(プロジェクトと請求先アカウントの紐付けができている)
- リクエスト送信側で指定しているプロジェクトのIDが合っている

結局　https://console.cloud.google.com/billing/projects にしばらくプロジェクトが表示されなくて、翌日同じ画面にアクセスしたらプロジェクトが表示されてエラーが消えました。キャッシュだったのかな？
Cypressで別オリジンのページに遷移させる

## これはなに？

別オリジンのページ遷移ができなく、見積もり1日のタスクに2日を要してしまったので同じ轍を踏まないように記録として残しておきます。

この記事が同様の問題で悩んでいる人の助けになれば幸いです。

## TL; DR

解決方法だけ先に記述しておきます。

`cypress.json`で以下を設定する

```json
{
	"chromeWebSecurity": false,
}
```

## 課題

筆者の所属している会社では婚活サービスのWebリプレイスをしています。

リプレイスをするにも少し規模が大きいので段階的に移行をしているのですがその中で、以下の遷移ができるかの確認が必要な場面が発生しました。

リプレイス後のページ → リプレイス前のページ

:::note 
移行リプレイス前後のページを以下のように表します
- リプレイス後：ページA
- リプレイス前：ページB
:::


素直に実装すると以下のようになるのですが、結果はエラーになりました。

```ts:view_pageA.spec.ts
describe('ページAに関する表示確認', () => {
  beforeEach(() => {
    cy.visit('/pageA/')
  })

  it('ページBへ正常に遷移できる', () => {
    cy.get('.pageBLink').click()
    cy.location('pathname').should(path => {
      expect(path).to.eq('https://pageB.com')
    })
  })
})
```
    
```bash:エラー本文
    Cypress detected a cross origin error happened on page load:
    
      > Blocked a frame with origin "http://localhost" from accessing a cross-origin frame.
    
    Before the page load, you were bound to the origin policy:
    
      > http://localhost
    
    A cross origin error happens when your application navigates to a new URL which does not match the origin policy above.
    
    A new URL does not match the origin policy if the 'protocol', 'port' (if specified), and/or 'host' (unless of the same superdomain) are different.
    
    Cypress does not allow you to navigate to a different origin URL within a single test.
    
    You may need to restructure some of your test code to avoid this problem.
    
    Alternatively you can also disable Chrome Web Security in Chromium-based browsers which will turn off this restriction by setting { chromeWebSecurity: false } in cypress.json.[Learn more](https://on.cypress.io/cross-origin-violation)
```


## 解決方法

> Cypress does not allow you to navigate to a different origin URL within a single test.
> 

異なるorigin URLには遷移できないと書かれています。

> Alternatively you can also disable Chrome Web Security in Chromium-based browsers which will turn off this restriction by setting { chromeWebSecurity: false } in cypress.json.Learn more
> 

エラーにも書かれているように`cypress.json`に`chromeWebSecurity: false`と設定することでこの問題を回避できました。


## 注意点

プロパティ名にもある通り、セキュリティ設定をオフにするので以下の箇所に影響が出るので注意してください。

- セキュアでないコンテンツが表示される（Display insecure content）
- クロスオリジンエラーの表示なしでスーパードメインへの遷移が可能になる（Navigate to any superdomain without cross-origin errors）
- iframeへのアクセスが許容される（Access cross-origin iframes that are embedded in your application）

↓公式ドキュメントはこちら

https://docs.cypress.io/guides/guides/web-security#Set-chromeWebSecurity-to-false

## 余談
今回はこのエラーにたどりつく以前にDockerの知識が浅いので1日以上かかってしまいました。

ちょっとずつDockerの仕組みを理解して問題解決にかかる時間を短くしていきたいです。

ではまた！

## 参考記事

https://docs.cypress.io/guides/guides/web-security#Set-chromeWebSecurity-to-false
構造方程式モデリングの覚書 for Lavaan Package on R

# モデルの記述

```R
model <-'
  # 因子
  Factor1 =~ v1 + v2 + v3 

  # 回帰
  v2 ~ v4 + v5

  # 共変動
　Factor ~~ Factor 

  # 間接効果と総合効果
  v4 ~ a*v1 + c*v5
  v1 ~ b*v5
　IndiredctEff :＝ a*b # v5がV1を経由してv4に与える影響
  TotalEff := a*b + c #v5が直接与える影響も加味した、v5がv4に与える全影響
'
# modelの中でも#を使ったコメントアウトは可能。
  
```

# SEM, CFA の結果の閲覧
```R
fit0 <- cfa(model, data) # sem(model, data)
summary(fit0, fit.measures=FALSE,standardized = TRUE)
# fit.measureをTRUEにすれば、CFIやBICなどの適合度指標も出力される
#　（その分、結果がつらつらと長くなる）
# standardizedはTRUEにしておくと標準化解が得られる。
```

# 様々な適合度指標の取得
```R
fitMeasures(fit0)
# print(fitMeasures(fit0)[c("agfi","cfi","rmsea","bic")],digits = 3)
# という書き方が可能。
# 小数が指数表記になる場合には、Rの基本関数であるoptions(scipen= X) のXの値を大きく取ればいい
```

# 因子得点の取得
```R
lavPredict(fit0)
```
【Project Euler】Problem 90: 2つの立方体の数字

* 本記事は[ProjectEuler](https://projecteuler.net/about)の「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。

### 問題 90.2つの立方体の数字

原文 [Problem 90: Cube digit pairs](https://projecteuler.net/problem=90)

**問題の要約：立方体の面に0-9の異なる数字を書いて2つ並べたときに以下のように平方数になる書き方を数えよ。ただし9と6は逆さに置いてどちらにも使える。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1922159/c00a3ca1-2072-5b10-50b6-4e4b81abf634.png)

以下のステップで全探索で解きました。

1. 0-9から6個の数字を選ぶ組み合わせを2つ作り、その直積を取る
1. それに6か9が含まれていたら、6と9を加えて、その2つの立方体の2つから出来る2桁の数字の集合（2つの順序を入れ替えたものも含めて）を作る。
1. それに2桁の平方数が全部含まれていれば関数**isSquare**はTrueを返すので、それを数えて2で割る。

```python
from itertools import chain
from itertools import combinations as comb
from itertools import product as prod
sqn = set(["01","04","09","16","25","36","49","64","81"])
dgt = [str(i) for i in range(10)]

def a69(lst):   # add 6 and 9 if lst includes 6 or 9
  return set(lst) | {"6","9"} if len(set(lst) & {"6","9"}) > 0 else set(lst)

def isSquare(cub, d1, d2):
  return  cub <= set(chain.from_iterable([((a+b),(b+a)) for a,b in prod(a69(d1), a69(d2))]))

# divide 2 considering the duplication
print(f"Answer : {[isSquare(sqn,d1,d2) for d1,d2 in prod(comb(dgt,6),comb(dgt,6))].count(True)//2}")
```

開発環境：Google Colab
ES6＿スプレッド構文

**1 スプレッド構文**
『...』で配列を展開することができます。
```
const arr = [1,2];
console.log(arr);
// 配列を展開
console.log(...arr);
```
スプレット構文で合計処理をする.
...は順番処理として覚えれば問題ありません。
```
// sumFun関数を作る
const sumFun = (num1, num2) => console.log(num1 + num2);
// sumFun(arr[0],arr[1]);
// スプレット構文で書く
sumFun(...arr);
```
配列をまとめることもできます。
```
const arr1 = [1,2,3,4,5];
const [num1, num2, ...arr2] = arr1;
console.log(num1);
console.log(num2);
console.log(...arr2);
```
スプレッド構文で配列をコピー、結合することもできます。
```
const arr4 = [10,20,30];
// ...でarr4をコピー
const arr5 = [...arr4];
console.log(arr5);
```
コピーだけならconst arr5 = arr4;で書くほうが楽と思う人がいると思いますが、
ここで気をつけないと、JSの参照型の罠にはまる可能性があります。

下記の例を確認ください。
参照渡しとはなどのキーワードで検索しても良いと思います。

```
const arr4 = [10,20,30];
const arr5 = arr4;
arr5[0] = 30;
console.log(arr4);
// arr5の値だけを変更するつもりですが、arr4も変更されました。
```
しかし、スプレッド構文でコピーすると元の値を影響しないです。配列をコピーしたい時は、スプレッド構文を利用してください。

スプレッド構文で配列を結合することもできます。
```
const arr4 = [10,20,30];
const arr6 = [40,50];
// ...でarr4とarr6を結合
const arr7 = [...arr4,...arr6];
console.log(arr7);
// 結果は10,20,30,40,50
```
40 代おっさん GASのUrl Fetchサービスについて学ぶ

## 本記事ついて

本記事は プログラミング初学者の私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## GASでの外部の通信

GASでは、他のサイトとやり取りを行う仕組みが用意されている。
その際、インターネット上で一般的に用いられるHTTPという通信プロトコルを用いて、外部との通信を行う。

私たちがWebページを閲覧する場合、ブラウザにURLを入力します。
この時、ブラウザからWebサーバーに対してWebページを見たいという要求を出します。
この要求をHTTPリクエストと言います。

### HTTPリクエスト

HTTPリクエスト構成されている、３つの部分
・リクエスト行：要求の種類を表すメソッド、対象となるURL、HTTPバージョンを含む情報
・ヘッダー：ブラウザの種類、クッキーなどWebサーバーに送るブラウザの情報
・ボディ：Webサーバーに送るデータ本体（空白の場合もある）

リクエスト行に付与されるメソッドはいくつかの種類がある。

・GET
HTMLドキュメントや画像ファイルを要求するときに使われる。

・POST
ボディに記載された情報をWebサーバーね送信するときに使われる

HTTPリクエストを受けると、それに対して返答がある
それをHTTPレスポンスと言う

### HTTPレスポンス

HTTPレスポンスは以下で構成されている。
・ステータス行：HTTPリクエストの状態を表すステータスコード、説明文、HTTPバージョンを含む情報
・ヘッダー：サーバーの種類やデータのタイプなど、ブラウザに送るWebサーバーの情報
・ボディ：HTMLドキュメント、テキストデータ、バイナリデータなどのブラウザに送るデータ本体

ブラウザはこのHTTPレスポンスの状態を解析して、Webページを表示すると言う動作をしています。

## Url Fetchサービス

Url Fetchサービスとは
GASでHTTPリクエストを送信したり、HTTPレスポンスを解析したりする機能を提供するサービス

Url Fetchサービスで提供されているクラス
|クラス|説明|
|------|---|
|UrlFetchApp|Url Fetchサービスのトップレベルオブジェクト|
|HTTPResponse|HTTPレスポンスを取り扱う機能を提供する|

## 参考資料

<https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Google-Apps-Script%E5%AE%8C%E5%85%A8%E5%85%A5%E9%96%80-%E7%AC%AC3%E7%89%88/dp/4798064742>
[OutSystems]ツリービューを実現するzTree Reactiveの使い方

OutSystemsでツリービューを実現するコンポーネントとして、昔からあったzTreeコンポーネントのReactive版が、ZTree Reactive。

# 確認環境
Personal Environment(Version 11.14.0 (Build 33133))
Service Studio (Version 11.14.5)
[zTree Reactive](https://www.outsystems.com/forge/component-overview/8338/ztree-reactive)  (Version 1.0.1)

# コンポーネントの概要
オリジナルのjQuery pluginが[zTree](https://treejs.cn/v3/main.php#_zTreeInfo)。
ただ、このモジュールでどのバージョンを使っているか不明（モジュールに組み込まれているJavaScriptファイルにはバージョン番号の記載なし）。

なお、Reactive Web AppではjQueryの使用は非推奨なのがちょっと気になるところ。
[外部ライブラリのJavaScriptコードを使用する](https://success.outsystems.com/ja-jp/Documentation/11/Extensibility_and_Integration/JavaScript/Extend_Your_Mobile_and_Reactive_Apps_Using_JavaScript/Use_JavaScript_Code_from_an_External_Library)

> OutSystemsでは、DOMを直接操作するjQueryなどのJavaScriptライブラリを使用しないことを推奨しています。これらのライブラリを使用すると、リアクティブアプリやモバイルアプリが破損する可能性や保守が難しくなる可能性があります。

この記事を書いている時点（2022/3/23)で確認できる最終リリースが2020/5/10とちょっと古い。

プロジェクトページのOverviewに貼ってあるリンクは無効だが、リンクのwww.の部分を消せば接続できる。

# 基本的な使い方
以下のようなツリーを表示する手順。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/cf0a82c6-46b2-b976-37c2-2808ff92c9e4.png)

## 部品を参照する
Manage Dependenciesダイアログで、zTree Reactiveの全要素をチェックし、APPLYボタンをクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/a77ded5c-76d6-234a-7c9b-a5128d25a84c.png)

## データを準備する
必要なデータはzTreeData StructureのList。
zTreeData1つは、ツリービューの各ノードに該当する。

Attributeはたくさんあるが、最低限必要なのは、idとname。階層構造にしたい場合はこれにpIdが加わる。サンプルとして、以下のようなデータをExcelで作成。

- id: 各ノードを一意に特定する値（表示はされない）
- name: 各ノードに表示される文字列
- pId: 他のノードの子ノードにする場合のみ指定。親ノードのidを設定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/7ab44839-9198-a522-66ad-8ba3f7b96c20.png)

これをService Studioにインポートして、Entityを用意。
Databaseを右クリック　→　Import New Entities from Excel　→　作成したExcelファイルを選択
データから、EntityとPublish時にExcelのデータをEntityに取り込むTimerが作成される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/2a901f4a-f24a-f98f-6df1-65108af69f20.png)

## テスト画面を作成する
### DataActionを用意する
今回は、Entityから取得したデータを、zTreeDataのListに詰め替えたいのでData Actionを利用する（他にも、データをAggregateで取得し、On After Fetchイベントでデータを詰め替える方法もある）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/173aeee5-5ce9-4707-0748-6a567613213f.png)

Data Actionの中身は、作成したEntityをAggregateで取得し、出力変数にAssignmentで詰め替えるだけ。データを設定した順番でツリービューに表示されるようなので、AggregateはSort (NodeId属性）を設定しておいたほうが良い。

Assignmentは以下の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/f1160c27-22aa-3719-353a-20c913302697.png)

### ScreenにBlockを配置
zTreeモジュールにあるzTree Blockを画面に配置。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/5df6bba3-6e44-4854-7973-59d33697b596.png)

配置したZTreeUL Placeholderには、デフォルトでHTML Elementが1つおいてある。
黄色の枠をつけたName属性が空になっているが、何らかの値を設定する必要がある。
もしこれをしないと、以下のようなエラーが発生するので注意。

    ZTreeUL placeholder of the tree must have an HTML widget with tag value of 'ul' and it must have a name set

Blockのプロパティを設定する (Data ActionのOutput Parameterを設定する）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/9a3e6c67-a381-5eb9-38f6-84bddd045823.png)