OmniAICreator/Qiita-1.07M huggingface dataset:
プログラミング初心者がスーパーのレジで困っているクレジットカード・ギフト券の使用可否をLINE-BOTとGoogle spread sheetを連携しようと苦闘中・・・泣

文字を入れたら返答する！！、これはすごい！！と驚き、自分の手間なアナログ作業を少しでも軽減できればと決意！！

#身近な困り事
小売業に勤務しているので、お盆や年末年始の繁忙期には店舗の応援、しかも一番苦手なレジ。特に、このクレジットカードや各メーカーのギフト券って使えるの？使い方は？がすぐに対応できず、まごまごするとお客さまを待たせてしまいます。マニュアルなんて全部は暗記できないし、でもすぐにわかるツールが欲しいなと思ってました。

#解決したい課題（やってみたいこと）
各企業のクレジットカードやギフト券を出されたときに、【会社名】を入力したら弊社の店舗での使用可否が瞬時にわかるようにしたいと考えました。

#使用した機能
　・LINE messaging API
　・Node-Red
　・Googleスプレッドシート

##技術、手法の概要
###1.Node-Redの構成
Qiitaの様々な投稿を見て下記の内容を検討
![キャプチャ(Node-Red).PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495333/21a0e31b-ca35-6175-11ba-6cd4abe7f7e6.png)

###2.Node-Redの中身
①【http in】LINEから送信されたメッセージを取得
![キャプチャ(httpin).PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495333/0ec2357f-981c-5809-5ebb-6769ac0b1965.png)

②【function】①からのメッセージを受け取る

```
// LINEサーバーからの情報を「msg.line」に移動させる
msg.line = msg.payload;
// msg.payloadにメッセージ本体を入れる
msg.payload = msg.payload.events[0].message.text;

return msg;
```

合っているのか徐々に不安・・・

③【GSheet】Google SpreadSheet に登録された表形式の情報を読み込む
いったん、Qiitaの色々な記事を見様見真似でシートを作成。
![キャプチャ(googlespread).PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495333/524839e4-6881-b2ac-5339-15c1360ac702.png)

弊社マニュアルをここに写すのは、自分で入力・・・？


sheetを作ったけど、Node-Redにどのように入力するのか、で手詰まり状態。力尽きる・・・
![キャプチャ(GSheet).PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495333/26c4e40c-365d-b36a-8bd1-a1c31bafedce.png)


④【function】表形式の内容を送信用メッセージに編集
⑤【ReplyMessage】送信用メッセージをLINEに送信
をこれまでの記事を参考にしながら、取り組んでいきます。


#今後
Qiitaの記事を見てるとGoogle SpreadSheet APIが無いといけないみたいなので、アカウントを取得してみますが、やってみると手間で一旦中止。
牛歩だけど一歩ずつ。
ファイルツリーを走査する際に特定のディレクトリの配下は無視したい

python3でファイルツリーをトラバースしたい場合、標準ライブラリとしては`os.walk`が用意されているのですが、このAPIは指定したディレクトリの配下すべてを探索してしまうため、「特定の名称のディレクトリの配下は走査しない」というような場合には、`os.walk`を利用せず、自前で実装することになります。

以下は「カレントディレクトリからファイルツリーをトラバースしていって、ファイルが見つかったら何かする、ただし`.git`ディレクトリと`.svn`ディレクトリの配下は走査しない」ということを実装したものになります。

```py
import pathlib

stack = [pathlib.Path('./')]
skips = {'.git', '.svn'}
while stack:
    entry = stack.pop()
    if entry.name in skips:
        continue
    if entry.is_dir():
        stack.extend(entry.iterdir())
    if entry.is_file():
        do_something(entry)
```

__2022-01-17 22:00 追記__

コメントで指摘があった通り、`os.walk`でも同じことが実現できることがわかりました。不勉強でした(´・ω・`)

```python
import os

skips = {'.git', '.svn'}

for dirpath, dirnames, filenames in os.walk('./'):
    for skip in skips:
        if skip in dirnames:
            dirnames.remove(skip)

    for filename in filenames:
        do_something(os.path.join(dirpath, filename))
```
スナップショット取得後に仮想ディスクをデタッチできるか試してみた

VMware ESXi 6.5の仮想マシンに以下3種類のディスクモードがアタッチされています。
`依存型`、`独立型：通常`、`独立型：読み取り専用`

https://kb.vmware.com/s/article/1003002 には以下内容が公開されており、仮想ディスクファイルの削除については言及されておりますが、デタッチについては言及されていないと読み取ったため実機確認してみました。
> If you take a snapshot of a virtual machine, and then remove a virtual disk from the virtual machine using the VI Client, the virtual disk flat file is not removed.

結論としては**全てのディスクモードにおいて仮想ディスクをデタッチすることはできませんでした**。



## 依存型の場合

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/17fd5a56-dce9-7398-2084-8657df64f976.png)

・デタッチできなかった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/da9df08d-fa6c-9abf-c921-19c3d6e07749.png)

・スナップショットを削除
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/90e0ec30-3004-3650-53b2-a9fdad16cb4b.png)

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/596b3d17-c36a-b027-8630-8a8a446c6340.png)

・デタッチできた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/40261ba5-6547-97c7-922b-63aaeb59f362.png)


## 独立型：通常の場合

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/f61fe770-6ad9-b4f5-697a-c917e9642de4.png)

・デタッチできなかった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/d4211bd9-719c-b994-3036-517d3c9ee67c.png)

・スナップショットを削除
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/5802c02e-c13d-62f7-37d2-f78ed2402a36.png)

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/4a0cd7b1-0dca-247f-2bed-e44c6f97a541.png)

・デタッチできた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/87ca664b-0b96-3de2-9ebc-584fc40e7482.png)


## 独立型：読み取り専用の場合

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/b9256f91-fb5f-b1ae-2a6e-c661e426fb83.png)

・デタッチできなかった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/08851647-42eb-07c8-d747-b1e80a8b0f2c.png)

・スナップショットを削除
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/7f5011ca-f668-19cf-8dd0-d0936b1bd5ae.png)

・仮想ディスクのデタッチを実施
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/74ae8834-3d6c-8de0-fc8f-46554dde9699.png)

・デタッチできた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377697/b2c77b05-fb7b-b429-7e54-1ac9e18877cf.png)
GitHubのリポジトリと個人アクセストークン作成、ローカルのソースコードをアップロードまで

#リポジトリの作成
自分のGithubページの右上にある+のところのNew repositoryをクリック
↓
新しいリポジトリのページのリポジトリ名とdescriptionを入力
↓
オプションはPrivateを選択
↓
下の方にあるCreate repositoryをクリック
↓
既存のリポジトリをGitHubに追加するコマンドが表示される
また、HTTPSかSSHオプションを選択できるのでHTTPSを選択

#リポジトリをプッシュする際に必要な個人アクセストークンを作成
[Githubのドキュメント](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
を参照して
右上のプロフィール画像のところにあるSettingsをクリック
↓
左サイドバーのDeveloper settingsをクリック
↓
左サイドバーのPersonal access tokens
↓
Generate new tokenをクリック
↓
Noteのところでメモなり簡単な利用理由などを入力
↓
Expirationで個人アクセストークンの有効期限を設定
↓
Select scopesで個人アクセストークンに含める権限を設定できるのでrepoにチェック
↓
Generate tokenをクリック
↓
個人アクセストークンの発行完了
ページを閉じたり移動したりすると個人アクセストークンが見れなくなってしまうので注意

#GitHubをリモートoriginに追加してそのリポジトリにpush

```
#GitHubをリポジトリのoriginとしてGitの設定ファイルに追加
$ git remote add origin https://github.com/GitHubアカウント名/リポジトリ名.git
#ローカルのリポジトリをリモートのoriginにプッシュ
$ git push -u origin master
```

####-u とは 
--set-upstream と同じ。
上流ブランチを設定するオプション。
今回初めてローカルのブランチのmasterをレポジトリのoriginにpushするので、-uオプションをつけるとorigin上のmasterブランチがローカルmasterの上流ブランチとして設定される。

####ブランチについて
今回ブランチをmasterにしていますがmainが推奨のようなのでmasterの部分をmainに変えてpushしてもいいかもしれません。
ラズパイ+OpenCVの画像処理の速度調査

# はじめに

[以前、ラズパイを使って子供の寝相の監視をした](https://qiita.com/mickie895/items/dbb7571388264e575ec8)が、そのときにMicroSDの容量不足に悩まされた。

OpenCVで保存すれば容量は減らせるのだが、撮影間隔を守ることができなかったので、
実際にどれぐらい時間に対して不安定なのかを確認していた。

もったいないのでとりあえず公開しておく。

# 時間測定用コード

適当な時間待ってから写真を撮って保存するのにかかった時間を計測するプログラムを書いた。
これを起動して、一晩放置してみた。

```python
from picamera import PiCamera
from picamera import array
from time import sleep
import datetime
import cv2

count = 0

with open("time.csv", "w") as w:
    with PiCamera() as cam:
        while count < 10000:
            with array.PiRGBArray(cam) as stream:
                start = datetime.datetime.now()

                cam.capture(stream, 'rgb')
            
                cv2.imwrite("test_bgr.jpg", stream.array)
                
                end = datetime.datetime.now()
                
                w.write(str((end - start).total_seconds()))
                w.write("\n")
                
                sleep(0.5)
                count += 1

```

# 結果

1万回の撮影結果を時間ごとにまとめた結果が下記の表。
例えば、0.5～0.6秒かかったのは10000回中5750回となる。

![時間計測.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395414/dda3ab52-1b91-2e3d-caa5-2cb0588e0cbf.png)

もっとsleepの時間を短くしたら結果は変わるかもしれないが、
毎秒撮影するときに10000回中に数回は1秒以上の時間がかかるとなると、
1時間に一度、撮影に1秒以上かかるタイミングがあると考えて良さそうだ。

毎秒動作させることが必須な使い方には、ラズパイ+OpenCVは向かなさそうだということがこの実験からわかった。
Elasticsearchのplain highlighterは100万文字以上のドキュメントを捌けない

こんちには、Qiita株式会社([2021年12月に従来のincrements株式会社から社名変更を行いました](https://blog.qiita.com/changed-company-name/))で普段Qiita Teamの開発を担当しているWakameSunといいます。

去年の6月にElasticsearchの検索結果がおかしくなっているとフィードバックを頂き、その調査の過程や原因を下記の記事にまとめました。（↓）

https://qiita.com/WakameSun/items/b1a28a9027577583d4af

これで事態は一安心・・・かと思いきや再びこのクエリで調べたら検索結果が返ってこないよとフィードバックを頂きました。
自身のPCでもそのクエリで調べたところ、ソートによって検索結果が出たり出なかったりと変わっていたのでなったので上司に確認を取り本格的に調査を開始することにしました。
そして、調査の結果以下のエラーが検索結果がおかしい時は吐かれていることがわかりました。(一部を抜粋・匿名化しています)

```
{"type":"illegal_argument_exception","reason":"The length of [zzz] field of [yyy] doc of [xxxxx] index has exceeded [1000000] - maximum allowed to be analyzed for highlighting. This maximum can be set by changing the [index.highlight.max_analyzed_offset] index level setting. For large texts, indexing with offsets or term vectors, and highlighting with unified or fvh highlighter is recommended!"}}},"status":400}
```

上のエラーメッセージを見ていただければわかりますが、plain highlighterでは100万文字以上のドキュメントが検索結果になるとエラーになるんですね。
[公式にも書いてあります](https://www.elastic.co/guide/en/elasticsearch/reference/current/highlighting.html)が見落としてました・・・(下記)

> Plain highlighting for large texts may require substantial amount of time and memory. To protect against this, the maximum number of text characters that will be analyzed has been limited to 1000000. This default limit can be changed for a particular index with the index setting index.highlight.max_analyzed_offset.


unified highlighterとplain highlighterとfast vector highlighter(fvh)の3種類がある、というのは[先日の記事](https://qiita.com/WakameSun/items/b1a28a9027577583d4af)で記載をしましたが、Qiitaではplain highlighterを2021年6月当時利用していました。エラーを回避するためには、ハイライターを変えるか、100万文字以上のドキュメントを入れないことが必要になります。
その時は先日の記事の件もあり、plain highliterを使うことはためらわれたため、長すぎるドキュメントに関しては100万より少し少ない値で足切りをしています。
(100万文字としていないのは、RubyとElasticsearchで文字数カウントの方法が違うらしく、Rubyで100万文字以下判定でも、Elasticsearchでは100万文字より多い文字数判定になる場合があったためです)

## まとめ
- Elasticsearchのplain highlighterを利用した検索は100万文字以上のドキュメントが検索結果にある場合エラーになる
- 公式ドキュメントは細部まで読もう(英語だからと読み飛ばさない)
ベジェ曲線の放電線をShaderGraphで描画する (SGL005)

VFX力を上げていきたいので模写ログ.

## 完成品

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

## 全体像

VFX Graph
![vfx graph](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/cd78fea3-9b56-0005-322b-c18460c81f0e.png)

VFX ShaderGraph

![shader graph](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/7a68eb67-9db9-51f7-f065-1861ae5925a4.png)

ShaderGraphの方は、電気線の両端をきれいにアンチエイリアスかけてフェードアウトするために利用.

## 作り方

ただの模写なので、本家をみてください..!

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

### 雑にブロック解説

Spawn
- SingleBurst: 256個生成

InitializeParticleStrip
- particleはつなげて先表示する
- SetProgressで`i/255`のfloat値をセットして、後のベジェ曲線の補完位置Tで利用

UpdateParticleStrip
- SetPositionで位置の更新 (particleの順番ごとにベジェ曲線の補完位置をそれぞれ代入)
- AddPositionでパーリンノイズをベジェ曲線位置に対してランダムに追加. このノイズ部分がアニメーションする

Output ParticleStrip Quad
- ScreenSpaceSizeでスクリーンスペースに対して、いい感じに細いピクセルラインを引く
- ShaderGraphは両端をいい感じにエイリアスかけるようにしたものを参照

![shader graph](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/fda30ff8-475a-79f2-8d3a-ade2a236eb70.png)


SetProgress
- GetAttribute: attributeIndexInStripでStripLineのindexを取得して0-1のprogressに直してセットしておく. (あとで使う)

![SetProgress](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/70069f2a-fc67-76d5-d06e-b011f306e524.png)

Bezier Curve Line Positions
- ベジェ曲線の位置を算出
- Initializeで設定したSetProgressの値をここで参照して中間位置に設定

![Bezier Curve](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/cd64558a-d020-605f-7332-77202b0df9f0.png)

SourceTransform/TargetTransform
- transformをblackboardのパラメータとして外だしする.
- ベジェ曲線の高さもパラメータとして外出しする.
- transform(position)と高さから2つのsource position, target positionを生成してベジェ曲線に使う

![SourceTransform/TargetTransform](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/697f3607-c84b-8b02-5703-e330a264a81e.png)

LineNoise
- ベジェ曲線に乗せるノイズを生成する
- Perlin Noise 3Dでノイズ生成.
- ノイズのレンジは中間が多め、先端末端を少なめにするように、SampleCurve+GetProgressで調整.

![LineNoise](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/e328bae9-21c7-73e8-cc34-32c96e93f82e.png)

TransformBinding
- VFXPropertyBinderを使って、exportしたpropertyのtransformを割り当てる.
- これによってオブジェクト位置に連動して先端と終端の放電線の位置が変わる.

![TranformBinding](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15000/f903d28a-c8ed-b44e-1cb5-3a517964328e.png)

## 感想

VFXPropertyBinderが便利...!
ノイズの使い方やVFX ShaderGraphの使い所なんかもすごく勉強になった.
色んな人のやり方勉強できる模写おすすめ..! Youtubeに色々上がってる
RustでWordleクローンを作る

最近流行りの[Wordle](https://www.powerlanguage.co.uk/wordle/)は、英単語推測ゲームです。これをCLI上で遊べるよう、Rustで作ってみました。プログラミング言語の習作の題材として面白いと思います。以下に仕様とRustプログラムをそのまま載せますので、参考にしてください。

![wordle.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15528/6ff5cfa9-67c2-75c4-1412-8ca316d2af65.gif)

## Wordle基本ルール

* 6回の挑戦以内に正解の英単語を当てる
* 正解の単語は必ず5文字
* 単語を入力しエンターボタンを押すと、その単語が正解単語とどれだけ近いか、各文字の色が変わることで教えてくれる

## 仕様

- [ ] ループ中にユーザからの入力を取る
- [ ] 入力文字列を正解の文字列と比較し正誤判定
- [ ] 結果表示
  - [ ] 文字位置が一致している場合、その文字色を緑で表示
  - [ ] 文字が別の位置に含まれる場合、その文字色を黄色で表示
  - [ ] 文字が正解単語に含まれない場合、普通に表示
- [ ] 6回の挑戦以内に正答したら終了
- [ ] 6回目の挑戦で不正解だったら正答を表示して終了
- [ ] [発展] 正解の単語をランダムにする
- [ ] [発展] 5文字しか入力できないようにする

下の自分の実装例では、発展とした仕様は未実装ですが、完全なWordleクローンを目指すならぜひ。

## Rustでの実装例

まだRustを書き始めたばかりなのでよろしくお願いします。

https://github.com/takmatsukawa/wordle-rs

```rust:main.rs
use colored::*;
use proconio::input;
use proconio::marker::Chars;
use ansi_escapes::{EraseLines};

#[derive(Debug)]
struct Word(Vec<char>);

impl Word {
    fn print_diff(&self, other: &Word) {
        for (i, char) in self.0.iter().enumerate() {
            let maybe_correct_char = other.0.get(i);
            match maybe_correct_char {
                Some(correct_char) => {
                    if char == correct_char {
                        print!("{}", char.to_string().green());
                    } else if other.0.contains(char) {
                        print!("{}", char.to_string().yellow());
                    } else {
                        print!("{}", char);
                    }
                }
                None => {}
            }
        }
        println!();
    }
    
    fn eq(&self, other: &Word) -> bool {
        self.0 == other.0
    }
}

fn main() {
    let correct_answer = Word(['w', 'o', 'r', 'l', 'd'].to_vec());

    let mut correct = false;

    for _i in 0..6 {
        input! {
            chars: Chars
        }

        let input = Word(chars);

        print!("{}", EraseLines(2));
        input.print_diff(&correct_answer);
        if input.eq(&correct_answer) {
            println!("Genius");
            correct = true;
            break;
        }
    }

    if !correct {
        println!("{}", correct_answer.0.into_iter().collect::<String>());
    }
}
```

```toml:Cargo.toml
[package]
name = "wordle-rs"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ansi-escapes = "0.1.1"
colored = "2.0.0"
proconio = "0.4.3"
```
Laravel + Docker の環境で、PostgreSQLに接続するためにやったこと

# はじめに
docker-composeを使用してLaravelの環境を構築し、PostgreSQLに接続するためにやったことを備忘録としてまとめておきます。

なお、docker-composeを使用したLaravelの環境は、下記の記事を参考に構築しました。
[Dockerを使ってLaravelのローカル開発環境を作る(Apache版)](https://qiita.com/ucan-lab/items/38cd04cee1f3f9e024b9)

ファイルパスなどは少し変更している箇所がありますので、ご自分の環境に合わせて読み換えてください。

# 環境
* OS: MacOS Big Sur
* Docker: 20.10.11
* docker-compose: v2.2.1

# やったこと
下記の二つを行いました。

* 環境変数 `DB_CONNECTION` の値を変更
* コンテナ内に、PHPの拡張機能 `pdo_pgsql` をインストール

以下で詳しく説明していきます。

# 環境変数 `DB_CONNECTION` の値を変更
Laravelで使用するDBを設定しているのは、`config/database.php` ファイル内の下記の箇所になります。

```php:config/database.php(一部)
'default' => env('DB_CONNECTION', 'mysql'),
```

Laravelの `env()` 関数は、第一引数で取得する環境変数の値を指定し、第二引数にデフォルト値（環境変数に第一引数で指定した値がない場合の値）を指定することができます。（[参考](https://readouble.com/laravel/8.x/ja/configuration.html)）
そのため、MySQL以外のDBを使用したい場合は、**環境変数 `DB_CONNECTION` に何かしらの値を設定する必要があります。**（[参考](https://tektektech.com/laravel-postgresql-connect-and-use/)）
今回はPostgreSQLを使用したいので、`pgsql` を設定します。

コンテナ内の環境変数の設定は、`docker-compose.yml` ファイル内で行っているので、そこに追記します。

```yaml:docker-compose.yml(一部)
version: "3.8"

services:
  web:
    build:
      context: .
      dockerfile: ./docker/app/Dockerfile
    ports:
      - ${WEB_PORT:-80}:80
    volumes:
      - ./src:/work/src
    environment:
      - DB_CONNECTION=pgsql # ここを追記
```

# コンテナ内に、PHPの拡張機能 `pdo_pgsql` をインストール
Laravelでは、PHPの拡張機能である[PDO](https://www.php.net/manual/ja/book.pdo.php)を使用してDBを操作していますが、PDOは使用するデータベースのドライバをインストールしなければ動きません。
そのため、コンテナ起動時に `docker-php-ext-install` コマンドを使用して `pdo_pgsql` をインストールします。（[pdo_pgsqlについてはこちら](https://www.php.net/manual/ja/ref.pdo-pgsql.php)）
加えて、`pdo_pgsql` は、`libpq-dev` パッケージが必要なので、こちらもインストールします。（[参考](https://oki2a24.com/2019/11/10/install-pdo-pgsql-in-php-7-3-apache-docker-container/)）
上記処理を `Dockerfile` に記述します。

```text:docker/app/Dockerfile
FROM php:7.4-apache

SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

ENV COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git unzip libzip-dev libicu-dev libonig-dev postgresql libpq-dev && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  a2enmod rewrite && \
  docker-php-ext-install intl pdo_pgsql zip bcmath

COPY ./docker/app/php.ini /usr/local/etc/php/php.ini
COPY ./docker/app/httpd.conf /etc/apache2/sites-available/000-default.conf

WORKDIR /work/src
```
Windows カスタムインストールUSB作成


#Windows11 Proエディションだけのインストールメディアを作成する方法

参考：[パソコンりかばり堂本舗-パソコンサポートブログ](https://ikt-s.com/windows11pro-installmedia/)

作成理由：
Chuwi LarkBoxPro にはWindows10 Homeがプリインストールされていますが、個人的にProにアップグレードして使っていました。
windows11をインストールしてみようと、インストールUSBを作成しクリーンインストールしたところHomeに戻された、そのためWin11Pro Onlyのインストールディスクが欲しくなった。

## 前提

- USB 1本
- 作業PC(Windows)

Windows11インストールUSBを先に作成しておく

## 初めに：
ドライブ名やフォルダー名はあくまでも例なので、それぞれの環境に合わせてください。

- USBインストールメディア… `D:ドライブ`
- 作業用ディレクトリ… `C:\work`

スタートメニューを右クリックしてWindows PowerShell （管理者）、もしくはコマンドプロンプト(cmd.exe）を右クリック→管理者として実行するで開き、DISMコマンドを使います。

表示されたエディションのうち、Proエディションのインデックス番号を調べます。インデックス番号は『3』になっているはずです。

```powershell:
# 次の1行のコマンドを実行します。
DISM /Get-ImageInfo /ImageFile:D:\sources\install.esd
```

D:\sources\install.esd ファイルから、インデックス3のProエディションだC:\work\install.esd にエクスポートします。以下の一行のコマンドを実行します。

```powershell:
# 次の1行のコマンドを実行します。
DISM /Export-Image /SourceImageFile:D:\sources\install.esd /SourceIndex:3 /DestinationImageFile:C:\work\install.esd
```

## install.esdを入れ替えて完成
　USBメディアのD:\sources\install.esd ファイルを削除して、エクスポートされたC:\work\install.esdファイルと入れ替えます。これで、ProエディションだけのUSBインストールメディアが完成です。
多分一番簡単なDockerでのPHP環境構築


ちょろっといじりたいときとか下記コマンドでPHPの環境が構築できる
下記コマンドを実行

```docker
mkdir workspace
cd workspace
docker run -p 8080:80 -v path-to-workspace/www:/var/www/html --name php80 -d php:8.0-apache
docker exec -ti php80 bash
touch index.php
echo '<?php phpinfo();' > index.php
```

下記にアクセス
http://localhost:8080
【PHP】session_start(): Failed to read session data: user (path: /var/lib/php/session) in ...が発生したときの対応

#　はじめに
セッションハンドラでセッション管理をしていたときに**session_start()**を呼び出すと下記エラーが発生。

```
session_start(): Failed to read session data: user (path: /var/lib/php/session) in ...
```
本記事ではエラーの原因、解決方法を備考録としてまとめております。

# 原因
####セッションハンドラ実装の流れ
* session_set_save_handler関数を定義
関数の説明は[公式](https://www.php.net/manual/ja/function.session-set-save-handler.php)を参照

* session_set_save_handler関数にSessionHandlerInterfaceインタフェースを実装したクラスのオブジェクトを渡す
* SessionHandlerInterfaceを実装するには、以下の6つのメソッドを実装する

1. ``close(): bool``
1. ``destroy(string $sessionId): bool``
1. ``gc(int $lifetime): bool``
1. ``open(string $savePath, string $sessionName): bool``
1. ``read(string $sessionId): string``
1. ``write(string $sessionId, string $data): bool``

今回のエラーの原因は`Failed to read session data〜`の通り、readメソッドの部分が該当していると読み取れる。公式の[session_start関数](https://www.php.net/manual/ja/function.session-start.php)にてエラー文言について触れている部分がないか検索すると、下記内容が見つかる。
 
```
If you are using a custom session handler via session_set_save_handler() then calling session_start() in PHP 7.1 you might see an error like this:
session_start(): Failed to read session data: user (path: /var/lib/php/session) in ...

As of this writing, it seems to be happening in PHP 7.1, and things look OK in PHP7.0.

It is also hard to track down because if a session already exists for this id (maybe created by an earlier version of PHP), it will not trigger this issue because the $session_data will not be null.

The fix is simple... you just need to check for 'null' during your read function:

<?php

function read($id)
{
  //... pull the data out of the DB, off the disk, memcache, etc
  $session_data = getSessionDataFromSomewhere($id);
 
  //check to see if $session_data is null before returning (CRITICAL)
  if(is_null($session_data))
  {
    $session_data = '';  //use empty string instead of null!
  }

  return $session_data;
}

?>
```


###### どうやら、戻り値が**null**今回のエラーが発生する模様。。

#　解決方法
先の検索結果を参考に、戻り値がnullかどうかをチェックしてnullの場合は空文字を返す。

```
//check to see if $session_data is null before returning (CRITICAL)
  if(is_null($session_data))
  {
    $session_data = '';  //use empty string instead of null!
  }

  return $session_data;
```

# 終わりに
readメソッドでDBの取得が0件の時に戻り値をnullにしていたため今回のエラーが発生しておりました。エラー事象を解決した後に、[公式](https://www.php.net/manual/ja/function.session-set-save-handler.php)のreadメソッドの説明を眺めていたら、しっかり取得データがないときは空文字を返却するよう記載があった。。
> read コールバックは、常にセッションエンコード (シリアライズ) された文字列を返さなければなりません。何もデータを読み込まなかった場合は空文字列を返します。
【Flutter】 showModalBottomSheetの高さを比率で指定する

#はじめに
[showModalBottomSheet](https://api.flutter.dev/flutter/material/showModalBottomSheet.html)はデフォルトで50%くらいの高さで表示されます。

![スクリーンショット 2022-01-16 20.51.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/3aff0b1f-b807-c3e8-68b1-88a7a87922ac.png)


**それを👇のように80%の高さで表示させます！**
![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/395cfb80-1b73-ff9a-9f02-bd3102f5ac5f.gif)

#前提

https://qiita.com/kokogento/items/87aaf0a0fbc192e51504

画面サイズに対する比率で高さを指定するには👆の記事を参考に`size_config.dart`を作成しておいてください:bow:

#showModalBottomSheetを用意

```dart
import '../../widgets/comment/view_comments.dart';

// 省略

         Padding(
              padding: EdgeInsets.symmetric(vertical: 20),
              child: Center(
                child: IconButton(
                  icon: Icon(
                    Icons.comment,
                    size: 40,
                  ),
                  onPressed: () => showModalBottomSheet(
                    isScrollControlled: true,
                    shape: RoundedRectangleBorder(
                        borderRadius:
                            BorderRadius.vertical(top: Radius.circular(20))),
                    context: context,
                     // showModalBottomSheetで表示される中身
                    builder: (context) => ViewComments(),
                  ),
                ),
              ),
            ),
```

#showModalBottomSheetの中身

```dart:view_comments.dart
import '../../config/size_config.dart';

class ViewComments extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Container(
       // 80%の高さで表示させる
      height: SizeConfig.blockSizeVertical * 80,
       // 省略
   );
 }
}
```

#最後に
`showModalBottomSheet`の高さを変更する方法は👇の`DraggableScrollableSheet`を使う場合が一般的かもしれません。

https://api.flutter.dev/flutter/widgets/DraggableScrollableSheet-class.html

しかしながら、`DraggableScrollableSheet`を使うと角を丸くしたりmodal外をタップでdismissさせたりと言った処理が効かなくなり、ちょっと一工夫が必要になるっぽいのでめんどくさくてやめました・・・w

`DraggableScrollableSheet`を使う場合は👇が参考になるかと！

https://www.youtube.com/watch?v=AjAQglJKcb4
API Gateway + Lambdaを設定して、ブラウザから関数を呼び出せるようにする。

# 目的
AWS外のサービスからURL形式でLambdaを呼び出せるようにAPI Gatewayの発行とLambdaの統合をしました。

# 前提
AWSアカウントを有しており、コンソール上で操作ができること。

# やったこと
①適当なLambdaの作成
②API Gatewayの発行とLambda統合

# ①適当なLambdaの作成
AWSコンソール > Lambda にて関数の作成を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/865f24af-71ba-8f97-d5c9-34f3bee156a1.png)

今回はNode.jsで適当なLambdaを作るだけなので「一から作成」とNode.jsのバージョンを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/5752a4a4-23a1-cd11-f7d3-115940b3a258.png)

関数の作成を押下します。

関数が作成され、コードソース画面に遷移するので「Test」ボタンを押下し、適当なお名前を付けたテストイベントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/5af54e50-0e6e-3ba4-adc5-91fc36d37977.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/974d02fb-bc9e-f01b-c1f3-5b8ce669a420.png)

改めて「Test」したときにレスポンスが以下であればOKです。

```
{
  "statusCode": 200,
  "body": "\"Hello from Lambda!\""
}
```

# ②API Gatewayの発行とLambda統合
①で作ったLambdaはいったん寝かせておいて、API Gatewayを仕込みます。
AWSコンソール > API Gateway　よりAPIの作成を行います。
APIタイプはHTTPを選択しました。「構築」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/43d57f88-51eb-b2b3-bbc8-fd0c44ccef10.png)

さっき作ったLambdaを選択します。
API名はよしなに付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/de452f24-480e-f961-4e53-a7f741fd2939.png)

「次へ」を押して設定できるルートとステージは特に何もしていません。
そのまま作成完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/b703234e-3509-b88a-43ff-83ab6f0c510e.png)

作成後の画面で表示される「URLを呼び出す」のリンクは罠です。そのまま飛んでもLambdaは呼び出されません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/284a32a9-7625-7068-5d76-0a51cc18b176.png)

押下すると新しいブラウザのタブが開いて以下表示されます。悲しみ。

```
{"message":"Not Found"}
```

API Gateway のルートを開いて、URLの後ろにパスを付け足して再検索してみましょう。
今回の例であれば
https:// hogehuga.execute-api.ap-northeast-1.amazonaws.com/testfunc
になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455207/2591e21f-543e-8ddd-2741-2aef71a2cdf3.png)

うまくいけば②で作ったLambdaが覚醒して画面上に以下表示されます。うれしみ。

```
"Hello from Lambda!"
```

# 終わり
【JavaScript】ES6　モジュールのエクスポート&インポート、およびbabelのインストール方法

##モジュールとは
部品と近い意味ですね。

大きなファイルを機能ごととか、役割ごととかで細分化する。
細分化したものを組み合わせて一つのものにする。

###モジュールのメリット
・重複命名防止
・コード再利用可能
・保守しやすい

###モジュールの文法
<b>export</b>
 モジュールをエクスポートする

<b>import</b>
 モジュールをインポートする

##エクスポートの方法
主に三つに分けられる。
1.個別エクスポート
2.一括エクスポート
3.デフォルトエクスポート

###個別エクスポート
エクスポートしようと思う　データの前に、「export」を加えること。

```javascript:individualExport.js

export let language = "JavaScript";

export function output(){
    console.log("I'am from individualExport.js file!! ");
}

```

###一括エクスポート
export{ // エクスポートしようと思うデータ}

```javascript:batchExport.js

let language = "React";

function output(){
    console.log("I'am from batchExport.js file!! ");
}

export {language,output}

```

###デフォルトエクスポート
export defaultを使用して、エクスポートする。
出力したdefaultのは、<b>object型</b>

```javascript:defaultExport.js

export default{

    language : "Jquery",
    change : function(){
        console.log("I'am from defaultExport.js file!!");
    }
}

```

##インポートの方法
1.全部インポート
2.分割代入
3.リネーム代入
4.別名でデフォルトインポート

```javascript:import.js
//1.全部インポート

import * as i from "individualExport.js";

import * as b from "batchExport.js";

import * as d from "defaultExport.js";

/*********************************************************************
要注意：defaultの場合、戻り値はオブジェクト型のため、
オブジェクト中のものを呼び出すときには、普通のオブジェクト型と同じ扱いしないといけない。
*********************************************************************/

d.default.change()　　//I'am from defaultExport.js file!!


//2.分割代入

import {language,output} from "individualExport.js";

//3.リネーム代入
import {language as language2 ,output as output2} from "batchExport.js";

//4.別名でデフォルトインポート

import {default as one} from "defaulExport.js";

```


##他のモジュール導入方法
モジュールをapp.jsに集約して、HTMLファイルで <b>app.js</b>をインポートする。

```javascript:app.js

import * as i from "individualExport.js";

import * as b from "batchExport.js";

import * as d from "defaultExport.js";

```


```forImport.html
<html>
    <head>
        <meta charset="utf-8">
        <title>モジュールインポート</title>
    </head>

    <body>
        <script src="./js/app.js" type="module"></script>
    </body>
</html>
```

##おまけ


一部のブラウザ(*)はまだES6以上の仕様に対応していないので、最新の書き方で書いたJavaScriptを反映できない。
そこで、Babelを使って、ES5に変換してもらえる。

*https://kangax.github.io/compat-table/es6/

###インストール
>npm i babel-cli babel-preset-env browserify

###babelで変換
>npx babel js（変換前のjsフォルダパース） -d dist/js（変換前のjsフォルダパース） --presets=babel-preset-env

###バンドル
>npx browserify ./js/app.js -o dist/bundle.js 

###インポート
```
<script src="./js/bundle.js" type="module"> </script>
```
[Rails][devise]追加したカラムにストロングパラメーターを使えるようにしよう

備忘録として

ユーザー管理機能を実装できる**devise**
デフォルトとして`「Eメールアドレス」「パスワード」`のカラムがすでに存在しており、ストロングパラメーターも使用できるが、自分で追加するカラム（ユーザー名など）にはストロングパラメーターは使えない状態なので、使えるようように記述する必要がある。

### 前提
- deviseを導入済み→[deviseの導入手順](https://qiita.com/Shi-raCanth/items/ce98a39800401cc77ee0)
- マイグレーションファイルでカラムを追加している

## application_controller.rbファイル
すべてのコントローラーが継承しているファイル。
ここに処理を記述しておくことで、すべてのコントローラーで共通となる処理を作ることができる。
`deviseの処理を行うコントローラーはGem内に記述されているため編集することができない`ので、このapplication_controller.rbを編集していく。

## devise_parameter_sanitizerメソッド
deviseにおけるparamsのようなメソッド。deviseのUserモデルに関わる「ログイン」「新規登録」などのリクエストからパラメーターを取得できる。
このメソッドとpermitメソッドを組み合わせることにより、deviseに定義されているストロングパラメーターに対し、`自分で新しく追加したカラムも指定して含めることができる`。
普通のストロングパラメーターと同じく、新たに定義する`Privateメソッド`の中で使用します。

```ruby:application_controller.rb
private
def configure_permitted_parameters  # メソッド名は慣習(自由につけても構わない)
  # deviseのUserモデルにパラメーターを許可
  devise_parameter_sanitizer.permit(:deviseの処理名, keys: [:許可するキー])#追加したカラムを記述
end
```

第一引数の処理名には、deviseですでに設定されているsign_in, sign_up, account_updateが使用できる。

|処理名|役割|
|-----|---|
|:sign_in|サインイン（ログイン）の処理を行うとき|
|:sign_up|サインアップ（新規登録）の処理を行うとき|
|:account_update|アカウント情報更新の処理を行うとき|

 

### 実際に編集してみる

```ruby:application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname])
  end
end
```
### 解説
２行目
`before_action`の`if`というオプションを使用している点に注目。
これは、値にメソッド名を指定することで、その戻り値がtrueであったときにのみ処理を実行するよう設定している。
今回は`:devise_controller?` というdeviseのヘルパーメソッドを指定して、もしdeviseに関するコントローラーの処理であれば、そのときだけconfigure_permitted_parametersメソッドを実行するように設定してる。

## まとめ
- 自身で追加したカラムでストロングパラメーターを使えるようにするには、特別な記述が必要
- devise_parameter_sanitizerメソッドを使用する
- application_controller.rb内に記述する

　
※補足等があればコメントいただけると幸いです。
【Plotly】PythonのPlotlyで時系列データを可視化する

##1.初めに
Pythonでデータを可視化する際、matplotlibとseabornが定番だが、表示範囲やスケールを変える際の手間がかかる。
インタラクティブに表示範囲・スケールを変更できる可視化ツールとして、今回Plotlyの使い方を簡単にまとめた。

##2.プログラム

```python:visualize.py
import pandas as pd
import numpy as np

df_passanger = pd.read_csv("AirportPassengers.csv", sep=";",  index_col=0)
print(df_passanger.head())

# Using plotly.express
import plotly.express as px

#fig = px.line(df_passanger, x="Month", y="Passengers", title='Life expectancy in Canada')
fig = px.line(df_passanger, y="Passengers", title='Air Passengers')
fig.show()
```
read_csvでファイルを読み込む。その後、plotly.expressを使い、折れ線グラフでデータを表示。
グラフ部分の表示例を以下に示す。
![スクリーンショット 2022-01-16 23.11.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/509872/235c95b4-c8ee-c77e-1918-7956215c8ae6.png)


```python:output.py
fig.write_html("test.html") 
```
グラフ画像は、グラフエリア右上のボタンから画像ファイルとして手動で保存できるが、上記のようにhtml方式で保存もできる。html方式の場合、スケールなども変更できる。

##3.参考資料
公式ドキュメント
[Line Plots with plotly.express](https://plotly.com/python/line-charts/#line-plots-with-plotlyexpress)
[Plotly.express.line](https://plotly.com/python-api-reference/generated/plotly.express.line)

Qiita記事
[[Python] Plotlyでぐりぐり動かせるグラフを作る](https://qiita.com/inoory/items/12028af62018bf367722)
【AtCoder】PythonでABC235 C問題(The Kth Time Query)を解く

せっかくQiitaのアカウントを取得しましたので、AtCoderの問題の解説を書いてみたいと思います。

# 問題

[HHKB プログラミングコンテスト 2022（AtCoder Beginner Contest 235）](https://atcoder.jp/contests/abc235/tasks/abc235_c)

# 考察

## やりたいこと

まずは落ち着いて問題文を読む。

長さNの数列 A がある。たとえば、A = \[ 1 1 2 3 1 2 \] (入力例1の場合) 。 
この数列に対して、以下のようなクエリが飛んでくるので、順に答える。

・「1が1番目に出てくる場所はどこか?」　→　「1番目です。」
・「2が2番目に出てくる場所はどこか?」　→　「6番目です。」
・「3が2番目に出てくる場所はどこか?」　→　「ありません。(3は1個しかない)」
・「5が1番目に出てくる場所はどこか?」　→　「ありません。(5は一つもない)」

## 愚直な探索の計算量

一つのクエリが来たら、Aの先頭から順に要素を見ていって、たとえば「3が2番目に出てくる場所」がどこかを調べれば、Aの要素へのアクセスを $2\times10^5$ 回程度することで、クエリに答えることが出来る。 
しかし、クエリは $2\times10^5$ 個あるので、最悪で $(2\times10^5)\times(2\times10^5)=4\times10^{10}$ 回のAの要素へのアクセスが必要となる。

## 計算の効率化

上記の方法だと、毎回先頭から順に要素を見ている部分が効率が悪く感じられる。 
極端な話、仮に最初に「1が3番目に出てくる場所はどこか」というクエリがあったなら、先頭から順に要素を調べて、3つめの「1」が見つかった時点で、「1が1番目に出てくる場所」「1が2番目に出てくる場所」は把握できている。 
そもそも、先頭から順に調べる際に、「1」だけに注目するのではなく、他の数字もどこにあったかを覚えておくことも出来る。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2416446/06448c28-4c03-f896-c737-04b8de5cb7ac.png)


それならいっそのこと、最初に先頭から最後までAを走査(スキャン)して、どの数字が、何番目と何番目にあったかのメモを作ればよい。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2416446/d690b33d-a64f-8b3b-d7cc-7b3a45cc8450.png)



そうすると、クエリに対して(一つ一つAの要素を順に見ずに)即座に答えられる。

・「2が2番目に出てくる場所はどこか?」
　→　2は「3,6」番目にあるので、答えは「6番目」。

・「3が2番目に出てくる場所はどこか?」
　→　3は「4」番目にある。このリストは要素数が1なので、2番目の「3」は「ありません」。

・「5が1番目に出てくる場所はどこか?」
　→　メモの中には5は登場していないので、「ありません」。

このようなデータ構造を作ることが出来れば、効率的に解ける。

必要な計算量は、
・メモの作成の際に、Aの要素に合計 $2\times10^5$ 回アクセスして内容を読み取る。
・$2\times10^5$ 個のクエリに対して、即座に答える。
なので、時間制限に十分間に合いそうだ。

# 実装

上記のようなメモは、「リストの辞書」を使うと実装できる。利用時に
・memo[1] → [1, 2, 5]
・memo[2] → [3,6]
・memo[3] → [4]
・memo[5] → (要素なし)
のように使えるようなイメージだ。

memoを作る手順は以下のようにする。先頭から順番にAの要素を見ていって、
・「1」番目は 1 だった　→　memo[1] のリストに「1」を追加。
・「2」番目は 1 だった　→　memo[1] のリストに「2」を追加。
・「3」番目は 2 だった　→　memo[2] のリストに「3」を追加。
・ (以下同様...)
のようにすればよい。

このような場合は、collections の defaultdict が役に立つ。 
詳細は、下記のコードを見ていただければわかるかと思います。

# 解答

[清書した解答 【提出 #28589877】](https://atcoder.jp/contests/abc235/submissions/28589877)

```python:ABC235C.py
from collections import defaultdict

# 入力を受け取る
n,q = tuple(map(int, input().split()))
a = list(map(int, input().split()))
xk = [ tuple(map(int, input().split())) for _ in range(q)]

# メモの辞書
memo = defaultdict(list)

# メモを作る
for i,ai in enumerate(a):
    memo[ai].append((i+1))  # python は 0-indexed なので、i に 1 を足しておく

# 答えを格納する配列
ans = []
for x,k in xk:
    # A の中に x が存在する個数 = memo[x] の要素数 となる。
    # A の中に x が存在しない場合、 memo[x] = [] (空リスト) になるので、問題ない。
    if len(memo[x])<k:
        ans.append(-1)
    else:
        ans.append(memo[x][k-1])  # python は 0-indexed なので、k から 1 を引く

# 答えを出力
print(*ans, sep='\n')

```
【React】分割代入でPropsの値を受け取る

# TL;DR

```typescript

interface Props {
  firstName: string
  lastName: string
}

const sampleComponent: FC<Props> = ({ firstName, lastName}) => {
  // firstNameの記述だけで値を受け取れる
}
```

このように記述すれば、Propsの中のプロパティをそのまま使用することができる

# 分割代入ってなに？

分割代入(Destructuring assignment) 配列やオブジェクトからプロパティを取り出して、別個の変数に代入することを可能にする構文

# 従来のPropsの受け取り方

これまでPropの値を受け取る時は下記のようにしていました。

```typescript

interface Props {
  firstName: string
  lastName: string
}

const sampleComponent: FC<Props> = props => {
  // props.firstNameでPropの値を受け取れる
}
```

これのショートハンドがTL;DRで記載した内容です

# 参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
ReactとNode.js、AWSで社内システムを構築するまで 1/2

こんばんは。

社内システムの構築プロジェクトが終わったので、日記風にどんなことをやったかを書いていきます。

まず、プロジェクトの概要ですが、社内でExcelで管理している出社状況データ(今日は○○さんはテレワークだよーとか、〇日はお休みだよーとか、部の出社率とかを管理するもの)のweb化でした。

上長からこんな技術使ってとか、こんな風に作ってという指定はありませんでした。

なのでまずは社内の集会などを利用して、どんなシステムを作ったらいいかという皆さんの要望を聞き出すヒアリングから開始しました。

大体20人くらいの方から意見を頂き、すべての意見を反映させることは出来ないので、要望の数が多いものをピックアップして、あとはExcelで管理していたデータをそのままweb上で再現することとしました。

ここで僕にとって幸福だったのは、使用技術の指定がなかったことです。ちょうどプライベートでReactやTypeScript、Node.js、AWSあたりを勉強していたので、SPAでWebアプリを作ってみようと思い、この4つの技術を使うこととしました。

SPAについて簡単に説明すると、従来のよくあるフレームワークでのWebアプリの作り方とは異なり、クライアントとサーバーサイドを独立させて、データのみをやり取りする手法です。
![IMG_20220116_230746.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677365/a6ebc98a-4919-d959-6cf7-4bfd794dacf3.jpeg)

ちなみに現在はGolangで別途APIサーバを作ってこんな感じで機能追加する予定です。
(プライベートで)
![IMG_20220116_231946.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677365/d24f341f-a184-a7d6-8ca6-aa1de36c0edc.jpeg)

まず、React(クライアント側)ですが、stateとpropsを使っていきます（当たり前)。
社内には部署が4つあります。そのため部署1～4のstateが必要です。
そしてカレンダーは月ごとに管理しているので、年(2022とか)と月(1～12)のstateも必要です。

次にログイン機能です。ログイン機能の作成についてはこちらの記事で詳しく説明しています。
https://qiita.com/natarisan/items/b43219f03566da8d0a66
ログインしているクライアント情報を保持するstateも必要です。

これらのstateをprops(場合によってはuseContextとか)を用いてうまいことコンポーネント間で共有します。

そして、各stateが変わったとき、そのstate応じたデータをfetchメソッドやaxiosメソッドでNodeサーバと通信して取ってきます。こんな感じで、fetchメソッドの中に現在のstateを当てはめる形でサーバにリクエストを送りました。
![スクリーンショット 2022-01-16 231249.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677365/3b35be12-8a24-408d-1194-b1d5320f5fee.png)

データベースもstateに対応するよう作成し、stateに対応するデータを返すようにしました。

同じくサーバ側で対応するデータの出社率計算機能も作成し、ひとまずアプリは完成です。
React-routerなども用いてやっとSPAっぽくなったなあという印象でした。

続いての記事は、作成したアプリをAWSにデプロイするまでを書いていきます。

今回は以上です。
apt と apt-get について

## 勉強前イメージ

前によくubuntu使ってたときはapt-getだったけど、新しく生まれた？

## 調査

### apt と apt-get について

apt と apt-get、どちらも両方ともDebian系のパッケージ管理コマンドになります。
`apt-get` が昔から使われてきており、設計上のミスを修正したのが `apt` になります。
なので最新のDebianを使うのであれば `apt` を使うのが良いかと思われます。

### apt と apt-getの違いは？

基本的に使うのはほぼ一緒のようですが、
削除のコマンドやアップグレードの際の動きが少し違うようです。

- 削除のコマンド(完全削除)

多分これだけじゃないと思いますが、
`apt` : apt remove -purge
`apt-get` : apt-get purge

- アップグレードの動作

`apt-get` はアップグレードの際の条件が厳しく、バージョンアップを行った際に他のパッケージのインストールが必要な場合はアップグレードを停止するそうです。
`apt` にかんしては少し条件が緩和されたそうです。
詳細はDebianの公式を確認お願いいたします。

## 勉強後イメージ

インストールとかするぐらいだとapt使ったほうがいいって感じかな。
ubuntu14.04からapt推奨らしい。

## 参考

- [apt と apt-get の違い](https://rs-techdev.com/archives/151)
- [【Linux】Debianのapt、apt-get、aptitudeの違い](https://ac-as.net/apt-apt-get-aptitude-difference/)
SwiftUIでのプレビューをより便利にする

# 概要

よく使うSwiftUIのプレビュー機能は実装を共通化しておくと便利です。
本記事では一例として以下の実装例を紹介します。

 - BoolPreview｜Bool値のプレビュー
 - ColorSchemePreview｜外観モードのプレビュー
 - LocalizedPreview｜ローカライズ言語のプレビュー

## BoolPreview

**利用例**

ViewのもつBool値の状態を切り替えてプレビューします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/ac65eb77-3c39-fe2c-52a0-26b7af0820be.png)

こんなふうに入れ子にして利用するのも便利です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/41138ab0-f715-d23e-23a0-0ef3f5f4fd46.png)


**実装**

```swift 
struct BoolPreview<Content>: View where Content: View {
    let content: (Bool) -> Content

    var body: some View {
        ForEach([true, false], id: \.self) { boolValue in
            content(boolValue)
        }
    }
}
```

プロジェクト全体でBoolPreviewを利用すると、必ずプレビューがtrue→falseなど順序を統一できるという効果もあります。

## ColorSchemePreview

**利用例**

外観モード（ライト/ダーク）を切り替えてプレビューします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/ffbe370b-4448-bb93-98a9-5f5c13bfd949.png)

**実装**

```swift
struct ColorSchemePreview<Content>: View where Content: View {
    let content: () -> Content

    private var colorSchemes: [ColorScheme] {
        [ColorScheme.light, ColorScheme.dark]
    }

    var body: some View {
        ForEach(colorSchemes, id: \.self) { colorScheme in
            content()
                .preferredColorScheme(colorScheme)
        }
    }
}
```

## LocalizedPreview

**利用例**

プロジェクトに設定されたローカライズ言語を切り替えてプレビューします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/c5301db2-6875-a9cd-3273-c701daef1ee6.png)


**実装**

```swift
struct LocalizePreview<Content>: View where Content: View {
    let content: () -> Content

    private var localizations: [Locale] {
        Bundle.main.localizations
            .map(Locale.init)
            .filter { $0.identifier != "base" }
    }

    var body: some View {
        ForEach(localizations, id: \.identifier) { locale in
            content()
                .environment(\.locale, locale)
        }
    }
}
```

# 最後に

本記事ではSwiftUIのプレビューを便利にする実装例の一部をご紹介しました。
他にも以下のようにいくらでもPreview型をつくることができます。
それぞれのプロジェクトにあった必要なものを実装すると良いです。

 - OrientationPreview｜デバイスの回転のプレビュー
   - https://developer.apple.com/documentation/swiftui/view/previewinterfaceorientation(_:)
 - DynamicTypePreview｜DynamicType設定に応じたプレビュー
   - https://developer.apple.com/documentation/swiftui/environmentvalues/dynamictypesize
 - IsEnabledPreview｜ユーザー操作を許可しているかに応じたプレビュー
   - https://developer.apple.com/documentation/swiftui/environmentvalues/isenabled
 - DevicePreview｜iPhoneとiPadなど、デバイス種別に応じたプレビュー
   - https://developer.apple.com/documentation/swiftui/view/previewdevice(_:)


## 参考

 - https://developer.apple.com/documentation/swiftui/previews-in-xcode
 - https://developer.apple.com/documentation/swiftui/environmentvalues
 - https://developer.apple.com/documentation/swiftui/view-appearance
wasabiのfstabの書き方

大量のアーカイブ写真の保管場所に困っているので、転送量も無料ということで、格安ストレージとしてwasabiを導入してみました。
[オブジェクトストレージ「Wasabi」の活用方法](https://www.nttpc.co.jp/technology/wasabi.html) 	

こちらを見てハマった場所だけ共有します。

>試験以外（サービス等）で利用される場合は、必ず/etc/fstabにs3fsマウント設定を記載し、サーバー起動時に自動マウントされるようにしておきましょう。

最後にあっさりと記載がありますが、この部分の記載に四苦八苦したので共有します。

fstabに追記する必要があるのは以下の通りです。

```/etc/fstab
s3fs#[バケット名] [マウント先] fuse nonempty,allow_other,url=https://s3.ap-northeast-1-ntt.wasabisys.com/,passwd_file=[パスワードファイルの置き場所],use_cache=/tmp/cache 0 0
```

これ探すの微妙に難しくて１時間以上かかりました。なんせ公式ドキュメントなどにも記載が無いのだから...

キャンペーンの[Wasabi 1TB無料お試しキャンペーン　クーポン投入方法](https://help.arena.ne.jp/hc/ja/articles/360050627474-Wasabi-1TB%E7%84%A1%E6%96%99%E3%81%8A%E8%A9%A6%E3%81%97%E3%82%AD%E3%83%A3%E3%83%B3%E3%83%9A%E3%83%BC%E3%83%B3-%E3%82%AF%E3%83%BC%E3%83%9D%E3%83%B3%E6%8A%95%E5%85%A5%E6%96%B9%E6%B3%95)はリンクが生きていて、コードはもらえるものの、期限が切れていた。
ちょっとそこは残念ですが、割と使い勝手良さそうなのでみなさんも使ってみてください。
ubuntu20.04環境でvenvを使ってpythonの環境構築した際のメモ

venvはpython用の仮想環境
少なくともpython3.7以降であれば`venv`が標準でよく利用されているようなので折角なので使っていきたい

> [venv --- 仮想環境の作成](https://docs.python.org/ja/3.7/library/venv.html)
> 仮想環境の作成
> 仮想環境 を作成するには venv コマンドを実行します:
> python3 -m venv /path/to/new/virtual/environment

`/path/to/new/virtual/environment`には実際のディレクトリパスを指定すると良い様子

```
# `venvtest`という名前で設定する場合
python3 -m venv ~/venvtest
```

有効化

```
source ~/venvtest/bin/activate
```

有効化したら、ここで環境構築する。`requirements.txt`を使う場合は以下の通り。

```
pip3 install -r requirements.txt
```

終了

```
deactivate
```

削除

```
python -m venv --clear ~/venvtest
```

非常に便利！

# 参考
[venv --- 仮想環境の作成](https://docs.python.org/ja/3.7/library/venv.html)
[venv: Python 仮想環境管理](https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e)
天気予測AIを作る

#概要
[前回](https://qiita.com/TanakaSU/items/a382a2032c479f37ced2)、気温を予測するAIを作りましたが、天気を予測してみようと思い作りました

#環境
OS:Windows
言語:Python
エディター:Geany


#データを集める
AIに学習させるための気象データを集めましょう。
今回は気象庁のHPから過去の気象データをダウンロードします。

https://www.data.jma.go.jp/gmd/risk/obsdl/

年月日,平均気温(℃),降水量の合計(mm),日照時間(時間),最深積雪(cm),平均風速(m/s),最多風向(16方位),平均蒸気圧(hPa),平均現地気圧,等のデータをダウンロードしましょう。

#説明変数がテキストだった場合どうすればよいか？
今回は説明変数の中に風向き(16方向)がありますが、そのままでは使えません。なのでテキストを種類ごとにナンバリングしてくれる機能を使います。

```python
from sklearn import preprocessing
label = preprocessing.LabelEncoder()
label.fit(data)
labels_id = le.transform(data)
```
上2行はLabelEncoderの宣言。
label.fit(data)はラベルとラベルIDの対応づけを行います。この文字列は0にしよう，みたいなことを決めています。これはle.fit(ラベルの一次元リスト)で行います。
ラベルとラベルIDの対応づけの変換することができます。これはtransform(ラベルの一次元リスト)で行います。返り値に配列が来ます。



#AIモデルを決める
Scikit-learnのアルゴリズムチートシートを見てモデルを決めます。![scikit-learn-cheat-sheet-0-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2398061/cf88c16b-06b9-c05e-9113-966e55acc164.png)
今回は「サンプル数は50以上」→「カテゴリー予測」→「ラベル付きのデータを持っている」→「サンプル数は10万以下」として**LinerSVC**(線形)や**kneighbors classifier**(非線形),**ensemble classifier**(非線形)等が選べます。
３つ試したところensemble classifierのスコアが一番良かったのでensemble classifierのコードのみ載せておきます。

#**ensemble classifier**(非線形)
作成したAIモデルにスコアをつけるプログラム。
値が大きいほど良い (0<score<1)
今回はあらかじめ説明変数のファイルと目的変数のファイルを分けています。(data.csv、targetGOZEN.csv)

```python:tenkiAI
 # -*- coding: Shift-JIS -*-
import re
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import svm,linear_model,preprocessing,ensemble
from sklearn.neighbors import KNeighborsClassifier

np.set_printoptions(threshold=np.inf)

#年月日,平均気温(℃),降水量の合計(mm),日照時間(時間),最深積雪(cm),平均風速(m/s),最多風向(16方位),平均蒸気圧(hPa),平均現地気圧(hPa)
#天気概況(昼：06時～18時)	天気概況(夜：18時～翌日06時)	

#気象データを説明変数として読み込む
f=open('data.csv','r')
data=re.split('[,\n/]',f.read())
f.close()
del data[-1]
print(data)

del data[::11]#西暦を消す

#方角をナンバリングする
dire=[]
for i in data[7::10]:
	dire.append(i)
le = preprocessing.LabelEncoder()
le.fit(dire)
labels_id = le.transform(dire)

#方角データをナンバリング方角に置き換える
j=0
for i in range(len(data)):
	if(i%10==0):
		data[i+7]=labels_id[j]
		j+=1

#二次元numpy配列にする
data = np.array(data, dtype='float64')
data=data.reshape(int(len(data)/10),10)

#天気データを目的変数とする
f=open('targetGOZEN.csv','r')
target=re.split('[,\n/]',f.read())
f.close()
del target[-1]
target = np.array(target, dtype='str')

#明日の天気を予測するために説明変数と目的変数を１日分ずらす
data=np.delete(data,len(target)-1,0)
target=np.delete(target,0)

#学習用データとテストデータに分ける
traind, testd, traint, testt =train_test_split(data,target, test_size=0.2)

#学習モデルの生成と学習
clf = ensemble.RandomForestClassifier(criterion='entropy', n_estimators=1000, random_state=1, n_jobs=2,class_weight="balanced")
clf.fit (traind,traint)

#スコアの表示
print(clf.score(testd,testt)) 

```

結果はこうなります。

```:実行結果
スコア:0.1761904761904762
```

#まとめ
スコアが低いですが今回はクラス分類での正答率をスコアにしているので、例えば本来の天気が「晴れ」に対して「晴れのち曇り」という予測が出てきた場合、当たらずとも遠からずといった感じですがスコアでは不正解にカウントされています。
自分で予測を確認したところそこまで大きな間違いは感じられませんでしたし、逆にぴったり当たっている予測が17%ほどあるということはそこそこなスコアだと思います。
nuxt.jsで作ったサイトでposition:stickyが効かないのはbodyにデフォルトで当たるoverflowが悪かった

## 先に結論：```nuxt.config.js```に下記のように書くことで```position:sticky;```が効くようになります
```js:nuxt.config.js
bodyAttrs: {
    style: 'overflow: unset!important;'
},
```
## 注意点
逆に```body```に```overflow: hidden;```などを当てたいときに困ります。特にモーダルとかの実装。```html```に```overflow: hidden;```を設定するなどの副次的な対応が必要になると思います。
ということは、この対応法は少なくともBestな方法ではないので、他の方法があればご教授いただければ幸いです。

<hr>

さて、上記でstickyが動くようになった場合、なぜそれで直るのかも一応知っておいたほうがよいかもしれないので、書いておきます。

## 前提１：stickyしたい要素の親にoverflowの値が設定されているとstickyが効かなくなる
>なお粘着要素は、直近の祖先がスクロールしない場合でも、「スクロールの仕組み」を持つ直近の祖先 (overflow が hidden, scroll, auto, overlay として作成されたもの) に「粘着」します。これによって「粘着」のふるまいを効果的に抑止します (GitHub issue on W3C CSSWG を参照)。
[position - CSS: カスケーディングスタイルシート](https://developer.mozilla.org/ja/docs/Web/CSS/position)

## 前提２：bodyにoverflowの値が```style```で指定されている

前提１を踏まえて```position: sticky;```を適用した要素の親をブラウザで順番に見ていくと、なんと```body```が下記のようになっていました。

```css:style
body {
    overflow: scroll!important;
}
```
このとき厄介なのが```style```によって指定されていることです。このことが意味するのは、CSSの上書きでは十中八九打ち勝てないことを意味しています。ご丁寧に```!important```までされていますので、それはより濃厚です。実際、```nuxt.config.js```を編集し、```body```にclassを指定し、それによってこのoverflowを上書きすることも試しましたが無駄でした。

```js:nuxt.config.js
bodyAttrs: {
    class: 'overflow-unset'
},
// この場合、classは当たるが、styleには打ち勝てない
```
<hr>
ということで、```nuxt.config.js```で```body```に```style```属性値をセットすることで無理やり```sticky```を動作させるというtipsでした。
FFmpegの使い方メモ

# FFmpegの簡単な使い方
音声と動画ファイルをマージする必要があったため、調査結果のメモを残しておきます。
 
データは以下の形式を想定します。

 * video[X].mp4
  ビデオファイル(映像のみ)

 * audio[X].mp4
  音声ファイル


# 形式変換
## ファイル形式の変換
```bash
ffmpeg -i video.mp4 video.avi
```

# 映像と音声の結合
## 長さが同じ、または長いほうに合わせる場合
```bash
ffmpeg -i video.mp4 -i audio.mp3 output.mp4
```

## 時間の短いほうに合わせる場合(-shortest)
```bash
ffmpeg -i video.mp4 -i audio.mp3 -shortest output.mp4
```

## 高速に結合したい(-copy)
-copyを付けると、ほぼ一瞬でおわります。

```bash
ffmpeg -i video.mp4 -i audio.mp3 -shortest -c copy output.mp4
```

# 音声の結合
## 時間をずらして結合
audio1.mp3 の開始5000ミリ秒後から audio2.mp3を結合する。

```bash
ffmpeg -y -i audio1.mp3 -i audio2.mp3 -filter_complex "[1]adelay=5000|5000[a1]; [a1]amix=inputs=2:duration=longest[a]" -map "[a]" output.mp3
```

filter_complexはセミコロン単位に分解して考えればよい。

* [1]adelay=5000|5000[d1]
 [1]は2つめの入力ファイル(audio2.mp3)を指す。adeleayで開始時間を左右チャンネルともに5000ミリ秒に指定。フィルタ後のストリーム名を[d1]とする。

* [d1]amix=inputs=2:duration=longest[a]
 [d1]を利用して結合する。durationで長いほうにあわせる。短いほうに合わせる場合はshortest。

# 映像の結合
## 2つのファイルを並べる
ビデオファイル2つを横に並べる。
| video1.mp4 | video2.mp4 |

```bash
ffmpeg -i video1.mp4 -i video2.mp4 -filter_complex "hstack" output.mp4
```

## 3つ以上のファイルを並べる
ビデオファイル3つを横に並べる。
| video1.mp4 | video2.mp4 | video3.mp4 |

```bash
ffmpeg -i video1.mp4 -i video2.mp4 -i video3.mp4 -filter_complex "[0:v][1:v][2:v]hstack=inputs=3[v]" -map "[v]" output.mp4
```

## タイル以外の並べ方
video1のファイルの横幅は1920pxなので、video2,3の横幅は半分の960を設定。

| <----- video1.mp4 ------> |
| video2.mp4 | vodeo3.mp4 |

```bash
ffmpeg -i video1.mp4 -i video2.mp4 -i video3.mp4 -filter_complex "[1:v]scale=960:-1[left]; [2:v]scale=960:-1[right]; [left][right]hstack[bottom]; [0:v][bottom]vstack" output.mp4
```

# 参考
[FFmpegの基本的な使い方](https://blog.123abcsoft.com/2021/01/14/ffmpeg%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9/)