OmniAICreator/Qiita-1.07M huggingface dataset:
初心者　Flutter x MySQL8 に挑戦する 23 with mysql_client

# 自前のwidgetを作ってみたが・・・。

　昨日、検索ページにTextFormFieldをつけて、idだけはUIから入力して検索できるようになったけど、ほかのcolumnはなぜかうまくいかなかった。　

　それで今日は、挿入ページにもTextFormFieldをつけて、新しいデータを入れてみようと思った。　

　昨日のTextFormFieldは１つだけだったが、角丸にしたり、枠線付けたり、色塗りしたり、そういう諸々で25行も必要だ。今日は３つつくろうとしているので、それだけで75行になる。コピペするだけとはいえ、実質それぞれが違っているのは２行だけなので、もういい加減、このだらだら長いfileを卒業したい。　

　ということで、TextFormFieldをclassとして独立fileに入れて、それを呼び出して、たった３行で１つのFieldができる、というのをやってみた。今までこういう工夫をしたことがない。有り余る老後の時間をどう使うか考えてアプリ開発している身としては、「効率よく」はプライオリティが低いのだ。でも、効率も何も、fileが長いと、修正箇所を探すのも大変だし、ということで、ようやく、やってみようという気になったのだ。　

　そして、それは簡単だった。もっと早くやればよかった。色とか変えたくなったら、このfile一つ変えるだけでよいじゃない。＼(^O^)／　

```
import 'package:flutter/material.dart';

class Format extends StatelessWidget {
  final String hintText;
  final Function onChanged;

  const Format({
    required this.hintText,
    required this.onChanged,
    Key? key,
    }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: InputDecoration(
        hintText: hintText,
        hintStyle: const TextStyle(
            fontSize: 12,
            color: Colors.black54),
        fillColor: Colors.grey,
        filled: true,
        focusedBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(20),
          borderSide: const BorderSide(
            color: Colors.grey,
            width: 2.0,
          ),
        ),
        enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(16),
          borderSide: const BorderSide(
            color: Colors.grey,
            width: 1.0,
          ),
        ),
      ),
      onChanged: (text) {
        onChanged();
      },
    );
  }
}
```

　で、意気揚々と挿入ページに導入。引数でそれぞれの役割を変更して、さあ、やってみよう。　

```
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(30.0),
              child: Format(
                hintText: 'year',
                onChanged: (text) {newYear = text;},
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(30.0),
              child: Format(
                hintText: 'name',
                onChanged: (text) {newName = text;},
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(30.0),
              child: Format(
                hintText: 'country',
                onChanged: (text) {newCountry = text;},
              ),
            ),
```

　あら・・・・・　ダメじゃん。　

　基本的には昨日と同じことをやっているのだ。TextFormFieldに書かれた文字列を使ってINSERT文を書く。それだけのこと。検索ページも、挿入ページも、元からあったフローティングアクションボタンを押して始めて関数が走るようになっている。　

　はず。　

　なのだけれども、TextFormFieldにカーソルを当てた途端に、関数が走る。そして、INSERT文が不完全だと文句を言う。そりゃそうだ、まだ何も書いてない。　

　原因はいまだ不明。しかたない。明日にしよう。 

![スクリーンショット 2022-09-01 20.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1646810/04f76172-ff6c-f819-0a93-9f01ea018e81.png)


https://qiita.com/dongri727/items/422a07e83e21c34a1d68

https://qiita.com/dongri727/items/0b51988c474d2b30674e
python並列処理ライブラリRayの使い方

# 使い方の結論
使い方は次のような感じ。説明はこのページの下のほうにあります。

```python:
import time
import random
from tqdm import tqdm
import ray

@ray.remote # 並列化するために関数をデコレート
def func(random_num, big_object):

    ######## 何かの計算 ########
    time.sleep(random_num)              # スリープ
    ############################

    return random_num                   # 何かを計算した体で、返却


def main():
    # funcの引数1
    random_num_list = [random.randint(0, 50) for _ in range(20)]

    # funcの引数2（とても大きいオブジェクトとする）
    big_object = None

    ######## 並列化処理 ########
    result_list = []
    ray.init()                     
    big_object_id = ray.put(big_object)         
    worker_ids = [func.remote(random_num, big_object_id) for random_num in random_num_list]    
    for _ in tqdm(range(len(random_num_list))):
        fin_worker_ids, worker_ids = ray.wait(worker_ids, num_returns=1)         
        result_list += ray.get(fin_worker_ids)                           
    ############################

    print(result_list)


if __name__ == '__main__':
    main()
```




# rayのインストールについて
私は`` pip install -U ray ``でインストールしました。

2022/09/01現在、pipでインストールするのがよいようです。
https://docs.ray.io/en/latest/ray-overview/installation.html





# 使い方の解説
各行に対して、解説をコメントとして書いてみました。

```python:
import time
import random
from tqdm import tqdm
import ray

@ray.remote # 並列化するために関数をデコレート
def func(random_num, big_object):

    ######## 何かの計算 ########
    time.sleep(random_num)              # スリープ
    ############################

    return random_num                   # 何かを計算した体で、返却


def main():
    # funcの引数1
    random_num_list = [random.randint(0, 50) for _ in range(20)]

    # funcの引数2（とても大きいオブジェクトとする）
    big_object = None

    ######## 並列化処理 ########
    result_list = []

    # 初期化
    ray.init()                     

    # 各プロセスでの引数のコピーを防ぐために、先にrayにコピーさせて、そのコピーのidを取得
    big_object_id = ray.put(big_object)     
    
    # 並列処理を実行
    worker_ids = [func.remote(random_num, big_object_id) for random_num in random_num_list]
    
    # 計算が終わったプロセスから、計算結果を取得
    for _ in tqdm(range(len(random_num_list))):

        # num_returnsの数のプロセスが計算終了後、戻り値を取り出すために終わったプロセスを取得
        fin_worker_ids, worker_ids = ray.wait(worker_ids, num_returns=1) 
        
        # 計算が終わったプロセスが関数から受け取った戻り値を取得
        result_list += ray.get(fin_worker_ids)                           

    ############################

    print(result_list)


if __name__ == '__main__':
    main()
```

# 実行結果
上のコードを実行すると次のような出力が出るかと思います。
```
100%|█████████████████████████████████████████████████████████████████████████████████████████████| 20/20 [00:48<00:00,  2.40s/it]
[0, 2, 3, 4, 5, 6, 11, 12, 16, 18, 19, 24, 29, 31, 35, 40, 41, 43, 43, 48]
```

# おわりに
Rayの解説記事少なくないですか…？
LPC812MAXで3個のLEDでRGB(赤、緑、青)


x mbed2リビジョン144

x リビジョン変更がわかる人むけ


目的

基本テスト


![o_con708.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/4e02b1b3-f2f2-39ee-b051-02f5925e9423.jpeg)


![o_con707.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/528f24f4-8b09-66ad-870e-3932bdbf38a1.jpeg)


いろいろ(ぐち)

書き込み器のファームがよくぶっこわれる
再度、書き込めがいいのだけど何とかならないのか?ならん

100円前後だけどいきなり値上げされない事を祈ってます。



mbed_binky_812_RGB


```c++



//mbed_binky_812_RGB

#include "mbed.h"

//GPIOの初期化
DigitalOut  leds[]  = {
    DigitalOut (P0_7),   //R
    DigitalOut (P0_17),  //G
    DigitalOut (P0_16),  //B
};

int main() {


  leds[ 0 ] = 1;
  leds[ 1 ] = 1;
  leds[ 2 ] = 1;

 wait_ms(3000);
  
//while(1){
//  
//  leds[ 0 ] = 0; wait_ms(1000);
//  leds[ 0 ] = 1; wait_ms(1000);
//  leds[ 1 ] = 0; wait_ms(1000);
//  leds[ 1 ] = 1; wait_ms(1000);
//  leds[ 2 ] = 0; wait_ms(1000);
//  leds[ 2 ] = 1; wait_ms(1000);
//    
//}
    
    
    //無限ループ
    while(1) {

        for(int ii=0; ii<3; ii++) {

            for(int jj=0; jj<3; jj++) {

                //GPIOへビットを設定
                if(ii == jj) {
                    leds[ jj ] = 0;
                } else {
                    leds[ jj ] = 1;
                }

            }//for

            wait_ms(1000);// 1秒待つ

        }//for

    }//while



}//main




```



![o_con833.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/87f8a44d-775a-2a8e-868f-af898aab175f.jpeg)


-2-
Windowsでpoetryが上手くインストールできない場合の対処

いつも通りPowerShellからpoetryを入れようとしたら入らなくなってた

```PS
PS C:\Users\hoge> (Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python -                                                                                       
Retrieving Poetry metadata

This installer is deprecated, and cannot install Poetry 1.2.0a1 or newer.
Additionally, Poetry installations created by this installer cannot `self update` to 1.2.0a1 or later.
You should migrate to https://install.python-poetry.org instead. Instructions are available at https://python-poetry.org/docs/#installation.

This installer will now exit. If you understand the above warning and wish to proceed anyway, set GET_POETRY_IGNORE_DEPRECATION=1 in the environment and run this installer again.
```

どうやら今(2022/09/01 20:45頃)現在進行形で公式さんが色々変更を入れてるっぽい

https://github.com/python-poetry/poetry/issues/6314


取りあえず、以下を参考に


https://python-poetry.org/docs/

```PS
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | python -
```

or

```PS
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
```

で入れられる。
特に環境をいじってなければ上のコマンドでいいはず。
【Tableau Data Saber挑戦記】tableau Data Saberプログラムの詳細と自身の経験について

# はじめに
先月までtableau Data Saberプログラムを受講していました。
今回は受講を検討している人に向けて自身がこのプログラムを経験してきたなかで感じたことやプログラムの特長などをお伝え出来たらと思っています。（まだ検討していなくても興味を持っていただけたなら幸いです。）

構成については以下になります。

1．「tableau Data Saberプログラムの詳細と自身の経験について」（←今回はこれです）

2.「これからはじめる人に向けて伝えたいこと〜初学者、経験者それぞれのメリット〜」

3.「しくじり先生！俺みたいになるな　～コミュニティ活動での失敗談～」

今回は「tableau Data Saberプログラムの詳細と自身の経験について」になります。
それ以降は別のドキュメントでお伝えいたします。

# Data Saberプログラム概要
Data SaberプログラムのWEBサイトです↓
https://datasaber.world/

『データを通して世界を理解し、それを人に正しく伝える努力を怠らず、
人の心を動かし、行動を促す。これがDATA Saberである。』


と公式サイトに記載されているようにデータドリブンなカルチャーを広めていくことを主眼にした取り組みです。
ですので、ただ純粋にテクニックや知識の向上を目指すというものではなく、tableauやこのプログラムのことを広めていくコミュニティ活動というものも必要になります。

ざっくりとした流れとしては

はじめに師匠となる人に養成のお願いすることでエントリーとなります。

技術試練とコミュニティ活動をクリアした人のみ最終試験を受けることが許され、最終試験も突破できたら、Data Saberを名乗ることができる、というものです。



# 技術試練
技術試練はtableauの分析テクニックや知識を問うものになります。
実際に手を動かして分析するHandsOn系のものと、知識を問われる座学系の２パターンがあります。

たとえばHandsOnではこのような問題があります。


”メーカーごとの売上の分布を見たとき、利益が赤字になっているメーカーの売上に特徴はありますか？
なお、メーカーごとに作っている商品のカテゴリが異なるので、カテゴリも分けられるよう考慮して表現してください。

メーカーの情報はExcelに入っていないので、
「サンプル - スーパーストア - メーカー情報.tdsx」
を使用してください。”
3. HandsOn - Intermediate I　より
https://public.tableau.com/app/profile/data.saber/viz/3_HandsOn-IntermediateI/3_HandsOn-IntermediateI

問題文ですが実業務とかでも有りそうな感じなので結構実践的だと思います。
ちなみに上記の問題だと「データ結合」や「ブレンディング」などが必要になるのですが、そういうテクニック的にも実践的なことを学べるため学びが多いと思います。

また、googleフォームで回答を提出します。
https://docs.google.com/forms/d/e/1FAIpQLScmtx4Ab8emi5GXTJqOoilfj8JWPl2kEoOdnUDq4udyr8bg7A/viewform


解説はKT氏（Data Saberの主催者）のYoutubeチャンネルにあります。
DATA Saber Boot Camp Week1 "HandsOn - Fundamental"
https://www.youtube.com/watch?v=vaDpfaL3-2M&list=PLR1ulf4cyh2lTnNKO1m3_VGgKRLczHOgO&index=5

# コミュニティ活動
コミュニティ活動は、「広める活動」であり、技術試練のような知識を問う問題とかではありません。
活動を行うとポイントが加算され、50ポイント獲得するとクリアとなります。
「パブリック」と「組織内」で各々20pt以上獲得することが求められます。

どんなことをするかというと、この「情報共有」や「ブログ記事の投稿」「イベント開催」などがあります。詳しくはコミュニティ活動を見ていただけたらと思います。
https://datasaber.world/#:~:text=10.%20ZEN%E5%95%8F%E7%AD%94-,II,-.%20%E5%B0%8E%E3%81%8F%E5%8A%9B%E3%82%92

コミュニティ活動は仕組みや内容も複雑ですし、何をするかを考えるのも大変だったりするので、序盤で目星をつけておいたほうがいいと思います。早めに師匠に相談してしまったほうがよいかもしれません。

コミュニティ活動もGoogleフォームで実績報告します。
https://docs.google.com/forms/d/e/1FAIpQLSd3n9836P9o_ikpz-TNHNfRJGDlC6BuArcwGLB3nc5XdsG0Mw/viewform

# 最終試験
技術認定試験ならびに口頭試問という内容です。
技術試験はHandsOn系の内容、口頭試問は座学系の講義の内容がメインになってくるかと思います。とにかく、習ったことを項目ごとに整理しておくと良いと思います。

# 期間は９０日間
挑戦期間は90日間です。長いようで短い。すごく短い。
他の挑戦者も言っていますがやっぱり短いと思う人が多いようです。

多くの人が悩むのがコミュニティ活動が期間終了ギリギリになりがちということだと思うので最初にこの部分に目星をつけておけばゆとりをもってのぞめると思います。

# まずは実際に見てみる
勢いでエントリーしてしまうよりは、まずは実際の問題やKTチャンネルの動画を少し見てみることをオススメします。
そこで、イメージがつけば後々やりやすいでしょうし、あとになって「やっぱり、ちょっと…」て感じになるのも防げるかなと。
すでに挑戦している人に訊いてみるのもいいと思います。
自分に訊いてくださっても全然OKです。


次は、2.「これからはじめる人に向けて伝えたいこと〜初学者、経験者それぞれのメリット〜」になります。

以上、お粗末さまでした。

# 参考
自分がエントリーする前に参考にした記事を載せておきます。
​​Data Saberを目指す
https://qiita.com/nagatakoki/items/31d7d42d04f05f24151d

Tableau DATA Saber 認定制度の振り返り
https://meow3571.hateblo.jp/entry/2020/07/06/213010

# 【Tableau Data Saber挑戦記】の最初の記事はこちら
こちらもぜひ

[【Tableau Data Saber挑戦記】1. はじめます](https://qiita.com/hetare/items/d55a7dda34f3b0e9073b)

https://qiita.com/hetare/items/d55a7dda34f3b0e9073b
Ubuntuに複数バージョンのPythonをインストールする

自分用の構築手順メモです。

基本は環境を切り替えるのですが、切り替えずに手元でPython3.7、Python3.8、Python3.9あたりを使いたい用件があったので作りました。

## 引用元文献
こちらの記事を使わせて頂きました。
[Ubuntu22.04LTSにPythonPipをインストールする方法](https://ja.linuxcapable.com/how-to-install-python-pip-on-ubuntu-22-04-lts/)
[Install packages for Python 3.9 on Ubuntu 22.04](https://stackoverflow.com/questions/72478985/install-packages-for-python-3-9-on-ubuntu-22-04)
[Ubuntuでpythonのバージョンを切り換える](https://qiita.com/piyo_parfait/items/5abbe4bee2495a62acdc)

## PythonPPAのインポート
```sh
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt-get update
```

## Python3のインストール
必要なバージョンのPythonをインストールします。
```sh
#sudo apt install python3.7 -y
sudo apt install python3.9 -y
sudo apt install python3.10 -y
sudo apt install python3.11 -y
```
## ~~python3-distutilsのインストール~~  pip3インストール  
~~各バージョンのPythonでpipを使うにはバージョンごとのpython3-distutilsが必要になります。これがないとModuleNotFoundErrorが出ます。~~
> ModuleNotFoundError: No module named 'distutils.util'  

### 2023.12.25追記  
以前の手順では各バージョンのpipをインストールできなくなってました。こちら([Python3.8 から 3.10にあげたらpipコマンド使えなくなったんだが](https://qiita.com/Zombie_PG/items/62f4b792ac541c04ee40))を参考にさせて頂きました。  

以下でpip3をインストールします。  

```sh
#sudo apt install python3.7-distutils
#sudo apt install python3.9-distutils
#sudo apt install python3.10-distutils
#sudo apt install python3.11-distutils
##以下に変更
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.9
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.10
curl -sS https://bootstrap.pypa.io/get-pip.py | python3.11
```

## update-alternatives
PythonとしてDefault versionを登録したい場合は[こちらの記事](https://qiita.com/piyo_parfait/items/5abbe4bee2495a62acdc)を参考にupdate-alternativesを使いました。
点と直線の距離「長野県立大学2022年前期第１問」をsympyでやってみた。

点A(2,2),点B(5,3),点C(4,6)...線分ABの垂直二等分線...

オリジナル

https://mathmathmass.exblog.jp/32210925/

上と同じです。大学入試数学問題集成＞

https://mathexamtest.web.fc2.com/2022/202211437/2022114370100mj.html#top-0101


# sympyで
もっと短くなるはずです。アドバイスを頂けると幸いです。

T氏様の手順
接点を計算してみました。CADで確かめたので、大丈夫だと思います。
```python
from sympy import *
x,y,a,k=symbols("x,y,a,k",real=True)
def myTentyoku(myPoly):
    sa=Poly(myPoly,x).coeffs()[0]
    sb=Poly(myPoly,y).coeffs()[0]
    sc=Poly(Poly(myPoly, x).coeffs()[1], y).coeffs()[1]
    return sa,sb,sc
A,B,C=Point(2,2),Point(5,3),Point(4,6)
sL=solve(Line(A,B).perpendicular_line(Rational(1,2)*(A+B)).equation(),y)[0]
print("#(1)",sL)
D=Point(a,sL.subs({x:a}))
AB=-Line(A,B).equation()
sa,sb,sc=myTentyoku(AB)
ans=solve(abs(sa*D.x+sb*D.y+sc)/sqrt(sa**2+sb**2)-sqrt(10),a)
print("#(2)",ans[0],solve(AB,y)[0].subs({x:ans[0]}))
print("#(2)",ans[1],solve(AB,y)[0].subs({x:ans[1]}))
ci=Circle(A,B,C)
print("#(3)",ci)
myLine=Eq(y,2*x+k)
sa,sb,sc=myTentyoku(myLine.rhs-myLine.lhs)
ans=solveset(abs(sa*ci.args[0].x+sb*ci.args[0].y+sc)/sqrt(sa**2+sb**2)<=ci.args[1],k, domain=S.Reals)
print("#(4)",ans)
print("#(4)",Line(myLine.subs({k:ans.args[1]})).projection(ci.args[0]))
print("#(4)",Line(myLine.subs({k:ans.args[0]})).projection(ci.args[0]))
#(1) 13 - 3*x
#(2) 5/2 13/6
#(2) 9/2 17/6
#(3) Circle(Point2D(3, 4), sqrt(5))
#(4) Interval(-7, 3)
#(4) Point2D(1, 5)
#(4) Point2D(5, 3)
```

以下,
Polyのcoeffsを使う必要がありませんでした。
ax+by+cをLineの中に入れました。
√10が√5になっただけでした。

```python
from sympy import *
x,y,a,k=symbols("x,y,a,k",real=True)
A,B,C=Point(2,2),Point(5,3),Point(4,6)
myLine_y=solve(Line(A,B).perpendicular_line(Rational(1,2)*(A+B)).equation(),y)[0]
print("#(1)",myLine_y)
D=Point(a,myLine_y.subs({x:a}))
ans=solve(Line(A,B).projection(D).distance(D).simplify().simplify()-sqrt(10),a)
print("#(2)",ans[0],solve(-Line(A,B).equation(),y)[0].subs({x:ans[0]}))
print("#(2)",ans[1],solve(-Line(A,B).equation(),y)[0].subs({x:ans[1]}))
#
ci=Circle(A,B,C)
print("#(3)",ci)
myLine=2*x+k-y
print("#(3)",myLine)
ans=solveset(Line(myLine).projection(ci.args[0]).distance(ci.args[0]).simplify().simplify()<=ci.args[1],k, domain=S.Reals)
print("#(4)",ans)
print("#(4)",Line(myLine.subs({k:ans.args[1]})).projection(ci.args[0]))
print("#(4)",Line(myLine.subs({k:ans.args[0]})).projection(ci.args[0]))
#(1) 13 - 3*x
#(2) 5/2 13/6
#(2) 9/2 17/6
#(3) Circle(Point2D(3, 4), sqrt(5))
#(3) k + 2*x - y
#(4) Interval(-7, 3)
#(4) Point2D(1, 5)
#(4) Point2D(5, 3)
```

# 別解省略
(2)の別解1.円と直線の交点計算できるはずです。
(2)の別解2.ベクトルの計算でできるはずです。
(2)の別解3.行列の座標変換。小座標→大座標変換でできるはずです。
(2)の別解4.エクセルvbaのソルバーでできるはずです。ただし、解は分数表示でできなくて小数点表示になります。
(3)の別解は、参考「3点を通る円を求める」でできるはずです。


# 問題文△ABCは、直角二等辺三角形でした。

```python
from sympy import *
A,B,C=Point(2,2),Point(5,3),Point(4,6)
print("#AB",A.distance(B)/sqrt(10))
print("#BC",B.distance(C)/sqrt(10))
print("#CA",C.distance(A)/sqrt(10))
#AB 1
#BC 1
#CA sqrt(2)
```

# 参考

https://qiita.com/mrrclb48z/items/7fa9e060b46334060ac7

https://qiita.com/mrrclb48z/items/36eb751ac44698659612#sympy%E3%81%A7

https://qiita.com/mrrclb48z/items/dacf38b39cca8ed50b6e
Viteで作成したWebページが無限リロードする

# 背景

- ローカル環境

# 環境

- Vite: 2.9.9
- Vue: 3.2.25

# 原因

- WebSocketの接続先に接続することができず、リロードされるようになっていた。

# 解決方法

- WebSocketの接続先のIPおよびポートが接続可能になるように、環境側の設定を変更した

# 詳細

## 構成イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384027/12100efb-b066-1009-81c7-fc8bf6cc27be.png)

## 調査

- 以下の現象が起こっていた
- `https://vite-sample.com`にアクセスすると、数秒の間隔で画面が自動的にリロードされる
- 開発者モードで確認すると、リロード直前に以下のような出力がされていた

``` : 開発者モード console
[vite] connecting...
WebSocket connection to 'wss://vite-sample.com:8084/' failed: 
[vite] server connection lost. polling for restart...
```

- つまりWebSocketへの接続に失敗している
- これが無限リロードと関係しそうなので、コードの方の調査
- vite側で提供しているclient.tsの以下の個所が原因そうだと判明
    - 以下にWebSocketの関連個所のみを抜粋
    - client.tsを全体は[こちら](https://github.com/vitejs/vite/blob/main/packages/vite/src/client/client.ts)から参照

``` ts: client.ts
// ...省略...

// use server configuration, then fallback to inference
const socketProtocol =
  __HMR_PROTOCOL__ || (location.protocol === 'https:' ? 'wss' : 'ws')
// ここで接続先設定
const socketHost = `${__HMR_HOSTNAME__ || location.hostname}:${__HMR_PORT__}`
// viteの設定でlocalhostに置き換えた
const socket = new WebSocket(`${socketProtocol}://${socketHost}`, 'vite-hmr')

// ...省略...

// ping server
// ここでソケットへの接続が閉じたもしくは失敗した際に、リロードが発生する
socket.addEventListener('close', async ({ wasClean }) => {
  if (wasClean) return
  console.log(`[vite] server connection lost. polling for restart...`)
  await waitForSuccessfulPing()
  location.reload()
})

// ...省略...
```

- イメージとしてはこんな感じ
    - 画面表示するようにポート:443は開けてる
    - だけどWebSocketで使われるポート:8084は開けてない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384027/e2508a3b-c5b9-d289-036f-45d449cab25f.png)

## 対応

- 調査からWebSocketへの接続さえできれば無限リロードも解消されそうなことが分かった
- WebSocketが正常に接続できるようにするためには以下の方法が考えられる
    - いまの接続先が正常に接続できるように、環境の設定変更で対応する
    - 正常に接続できる接続先に置き換えるように、Viteの設定変更で対応する

### 環境の設定変更で対応案

- 今回のケースではこちらで解決した
- ALBでポート:8084も開けた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2384027/75a27fe1-26ab-4c84-2070-c53986d0fb50.png)

### Viteの設定変更で対応案

- 今回は、あとから気づいたので、試していない（ので知見ある方いればコメントください・・・）
- vite.config.jsでWebSocket接続時に使用するポートを明示的に指定する

``` js.vite.config.js
// ...省略...
  server: {
    host: true,
    port: 8084,
    strictPort: true,
    hmr: {
      // ここに接続可能なポートを指定する
      port: 443,
    },
  },
// ...省略...
```

# まとめ

- Viteは内部でWebSocketの通信もしており、これも疎通していないといけない
- WebSocket向けの設定はhmrで行う

# 参考情報

- [『WebSocket connection to '<ローカルIP>' failed』 無限リロード](https://teratail.com/questions/djbisp8h8s63km)
- [Viteでブラウザが再読込が繰り返す問題を解決](https://diary.varwww.com/posts/2021/vite-unexpected-reload-loop.html)
- [Docker環境でvite+Nginxを動作させた時に詰まったポイント](https://qiita.com/tomopict/items/2c321374e917cc679efb)
- [Configuring Vite](https://vitejs.dev/config/)
【Laravel Livewire】俺的住所検索ベストプラティクス

## はじめに

以前こんな記事を書いた。

https://qiita.com/sgrs38/items/40dd4d4abbfee0f7a452

「うーん気持ち悪いなぁ」と思いながら対応した記憶がある。

理想は
1. 郵便番号を入力
1. 検索ボタンクリック
1. 都道府県とかをセット。

というフローが良い。
PHP側に住所検索API処理を書けば綺麗にいけるんじゃねえかと思い,
取り組んだ結果を書いてく。


## zipcloud API

http://zipcloud.ibsnet.co.jp/doc/api

yubinbangoは自動入力だったがこちらは任意のタイミングで検索をかけられる。

## コードを書く
一旦動きそうな住所取得処理を書きます(処理は適当です、コピペしない方がいいです)

```php
<?php

namespace App\Services;

class AddressApiService
{
    private $base_url = 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=';

    public function __invoke(string $zipCd): array
    {
        $json = file_get_contents($this->base_url . $zipCd);
        $json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
        $arr = json_decode($json, true);

        if ($arr['results'] === null) {
            $resultArray['message'] = '郵便番号から住所が見つかりませんでした。';
            return $resultArray;
        }
        $resultArray = [];

        if (!empty($arr['message'])) {
            $resultArray['message'] = $arr['message'];
            return $resultArray;
        }

        $resultArray['prefecture'] = $arr['results'][0]['address1'];
        $resultArray['address1'] = $arr['results'][0]['address2'];
        $resultArray['address2'] = $arr['results'][0]['address3'];

        return $resultArray;
    }
}

```

livewireコンポーネントにはこんな感じの処理を書きます。
```php

    public function setAddress(): void
    {
        $addressApiService = new AddressApiService();
        $addressArray = $addressApiService($this->hoge->zipcd);
        if (!empty($addressArray['message'])) {
            session()->flash('addressErrorMessage', $addressArray['message']);
            return;
        }
        $this->hoge->prefecture = $addressArray['prefecture'];
        $this->hoge->address1 = $addressArray['address1'];
        $this->hoge->address2 = $addressArray['address2'];
    }
```

Blade側にはボタンか何かに``wire:click="setAddress()"``って書いておいて
エラーメッセージ用の記述しとけばOKです。多分。
```html
@if(session()->has('addressErrorMessage'))
    <p class="pt-0 col-form-label col-form-label-sm text-danger">
        {{ session()->get('addressErrorMessage') }}
    </p>
@endif
```


## おわり

住所自動入力はlivewireと相性悪いんで任意のタイミングで検索できるzipcloudのAPIを使ってこうと思います。
Redash v10でs-jisでcsvをダウンロードする方法

## かつての方法

python2時代のredashは.envに↓を書けば、shift-jisでcsvがダウンロードできました。
```bash
export REDASH_CSV_WRITER_ENCODING="cp932" 
```
しかし、v9からpython3になりUTF-8が標準になったことから、CSVのエクスポートに上記の設定を使っている`utils/__init__.py`の`class UnicodeWriter`を使用しなくなってしまいました（=上記設定は使われず、cp932でCSVはエクスポートできない。）


## v9から
コードを見た感じでは`redash/handlers/query_results.py`でcsvのあれこれをしています。この為、仕方なくソースコードを変更することにしました。
https://github.com/getredash/redash/blob/2589bef1f21b7c84e4ceacdf5cd2c620bdedb111/redash/handlers/query_results.py#L436

```python
    @staticmethod
    def make_csv_response(query_result):
        headers = {"Content-Type": "text/csv; charset=UTF-8"}
        return make_response(
            serialize_query_result_to_dsv(query_result, ","), 200, headers
        )
```
これを、
```python
    @staticmethod
    def make_csv_response(query_result):
        headers = {"Content-Type": "text/csv; charset=Shift_JIS"}
        return make_response(
            serialize_query_result_to_dsv(query_result, ",").encode("cp932"), 200, headers
        )
```
に変更すればshift-jisでcsvがダウンロードされます。utf-8 bom付きにしたい場合は、↓のようにします。
```python
serialize_query_result_to_dsv(query_result, ",").encode("utf_8_sig"), 200, headers
```


## dockerに対応させる
上記を変更すればshift-jisに対応できるのですが、ちょっと前からセルフホストはdockerが標準になっているため、コンテナを停止等すると変更内容が元に戻ってしまいます。その為、下記のようにホスト側に変更したファイルを置き、volume接続することにしました。

ただ、この方法は**redashのバージョンを上げるたびに再設定する必要があります**。該当箇所をsed等で書き換えるDockerfile等を書いて、docker-compose buildし直すのも良いかもしれません。

変更したファイルをpatch/query_results.pyにおいた場合。

docker-compose.yml
```yaml
services:
  server:
    <<: *redash-service
    command: server
    ports:
      - "5000:5000"
    volumes:
      - ./patch/query_results.py:/app/redash/handlers/query_results.py:ro
    ....
```

これで`docker-compose rm server`、`docker-compose up -d server`すると、変更したファイルが使用されます。
Chromeブラウザ＞検証ツール(デベロッパー ツール)(developer tools)について便利な使い方をまとめてみる

# 今回やりたい事_説明
Chromeデベロッパー ツール(developer tools)で以下の利用方法を案内
* キャプチャ取得
* パスワード可視化
* ブラウザUA変更

# 今回の記事が参考になる方
* 非エンジニア
* 制作ディレクタ
* 新社会人
### ▼環境案内▼
:::note info
PC：Windows　OS 10（非エンジニアなので細かいことは書かない。）
Chromeブラウザ：最新版
NETWORK：普通に自宅のWi-fi か　会社有線LAN環境
:::

***

## １．Chromeブラウザを開きデベロッパー ツールを起動した後に表示しているページをキャプチャ取得する

デベロッパー ツールを起動するにはキーボードの「F12」を押下するか、マウスで右クリックして、「検証」を選択する
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/d104dfda-ba85-4a62-d292-5496b007a4b3.png)
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/a735933d-26e0-8890-b8f9-45d533e8f1d9.png)


:::note warn
もしもデベロッパー ツールを起動した後にメニューが日本語でない時には言語設定を変更しましょう。
英語が得意であれば別ですが、日本語の方が断然進めやすいです。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/4d27118f-1baa-0e08-55ec-7feef7955c2f.png)
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/ea3e8faa-012c-ed72-51c7-b672b6dc06ad.png)
:::

**画面を選択してキーボードの「Shift＋Ctrl＋p」を押下します。**
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/d28f6ed2-3954-fa91-44e5-dab8f218aa84.png)

表示された実行＞部分に「キャプチャ」と入力すれば、以下の選択肢が表示する。
**フルサイズを選択すると表示していないページ下部までキャプチャできる**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/0ff4134d-8c3f-68e0-64c1-09069b8b20df.png)

保存してキャプチャ取得完了！簡単でしょ？ぜひ活用下さい。

## ２．ChromeブラウザでUserAgentを指定してページ表示したい
こんな時ありますよね。
#### iPhone13で見たい！とか、Galaxy＊で見たい！とかなら結構手段あるんですが、例えばMac safariで見たい！とか、時にはアプリで生成したWebViewの特有のUserAgentがあってそれでみたい。

なんかもあると思ってます。その時はこちらの方法で出来ますよー。

* デベロッパー ツールを起動する
* 画面右上の「三点」を選択する
* 「その他のツール」を選択する
* 「ネットワーク状態」を選択する

**ユーザエージェント欄の「プルダウン」で好きなUserAgentを選択する**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/5d83f80e-0bcb-cfba-f1a3-83c3a8b67095.png)

ページを再度読み込めば、指定のUAでページ表示できます。

#### もしアプリで生成したWebViewの特有のUserAgentで見たい時にはプルダウンの選択をカスタムにしてもらい入力欄に値を入れればそのUserAgentで閲覧できますよー。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/482281b3-a544-7c1e-52a6-859c612fbb73.png)

## ３．Chromeブラウザでパスワード可視化する方法
chromeブラウザとかで、自動保存してくれているパスワードが「・・・・」とか「＊＊＊＊」とかで非表示になっているケースありますよね？

パスワードを表示する。みたいなサポートがあればいいんですが、全部あるわけじゃない。

そんな時にこれ使えますよ。

* デベロッパー ツールを起動する
* 矢印アイコンを選択
* パスワード欄を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/2703ec9d-bca2-a201-4284-5745ce6452b0.png)

* 要素内の青く背景が表示しているところに注目
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/ef90cb5f-7f41-d743-9f27-aeb7805ea112.png)
* 「type="password"」となっている"password"部分をダブルクリックし「text」に変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/789571/45f8e01b-ea62-b984-7d3b-67c748e9ae1f.png)
#### はい、表示されましたね。。魔法かよ！

今日はここまで。Chromeブラウザは第二弾やろうと思いますので次回もよかったら見て下さい。では。
パスワードバリデーション用の正規表現のまとめ・ちょっとした説明

# はじめに
Next.js + rails アプリでフロント側にパスワードのバリデーションを実装しようと思い、
どうやってやるのかなーと調べていました。
使える正規表現自体は見つかったのですが、表現の内容を理解するためにMDNのページを行ったり来たりして大変だったので
今回使った特殊文字をまとめておきます。
プラスちょっとだけ構造を説明します。

# 想定している読者
- 初心者
- Javascriptでパスワードのバリデーションを実装したい方
- 見つけた正規表現を理解せずコピペで使うのは嫌な方
# 今回使う正規表現
```javascript
/^(?=.*[a-zA-Z])(?=.*\d).{6,20}$/
```
- 数字とアルファベットを最低1文字ずつ含む
- 6文字以上20文字以下

の文字列にマッチします。

こちらの記事を参考にしました。
[言語別：パスワード向けの正規表現](https://qiita.com/mpyw/items/886218e7b418dfed254b)

3つに分けたほうが理解しやすいと思ったので、分けて書きます。
## Part1
```javascript
/^(?=.*[a-zA-Z])(?=.*\d)/
```
| 特殊表現 | 意味 |
|:-:|:-:|
|^|文頭にマッチ|
|(?=hoge)|直前の表現にhogeが続く場合のみマッチするようになる|
|.|任意の1文字にマッチ|
|*|直前の表現の0回以上の繰り返し|
|[a-zA-Z]|大/小文字アルファベット1文字にマッチ|
|\d|アラビア数字1文字にマッチ|

Part1は文頭^に対して2つの(?=hoge)がかかっています。
ここでは

「任意の文字の0回以上の繰り返し+何かしらのアルファベット1文字」が後ろに続き(?=.\*[a-zA-Z])、
かつ
「任意の文字の0回以上の繰り返し+何かしらの数字1文字」が後ろに続く(?=.\*\d)
「文頭」にマッチします。
つまり数字とアルファベットを含む文の文頭にマッチします。
## Part2
```javascript
/.{6,20}/
```
|特殊表現|意味|
|:-:|:-:|
|.|任意の1文字にマッチ|
|{x,y}|直前の表現のx回以上y回以下の繰り返し|

Part2は
任意の文字の6回以上20回以下の繰り返し
にマッチします。
つまり6文字以上20文字以下の文字列にマッチします。

## Part3
```javascript
/$/
```
|特殊表現|意味|
|:-:|:-:|
|$|入力の末尾にマッチ|

Part3は入力の末尾にマッチします。それだけです。
# まとめて
```javascript
/^(?=.*[a-zA-Z])(?=.*\d).{6,20}$/
//最初と一緒です
```
「数字とアルファベットを含む文の文頭」+「6文字以上20文字以下の文字列」+「入力末尾」
つまり
数字とアルファベットを含む6文字以上20文字以下の文字列にマッチする正規表現ができました!
vistaでandroid studio その9

# 概要
vistaでandroid studio 1.0.1やってみた。
ListView使ってみた。

# 参考にしたページ

https://qiita.com/joji/items/dca48fb0e915077a01b2


# 写真

![device-2022-09-01-110532.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/27c846c9-6876-7112-680c-a0f6a0869b90.png)


# レイアウト

```
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	android:paddingBottom="@dimen/activity_vertical_margin"
	tools:context=".MainActivity">
	<ListView
		android:id="@+id/list"
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:choiceMode="singleChoice"
		/>
</RelativeLayout>

```

# サンプルコード

```
package com.ohisamallc.ohiapp154.ohiapp154;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class MainActivity extends ActionBarActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		setContentView(R.layout.activity_main);
		final ArrayList<String> data = new ArrayList<>();
		data.add("胡椒1");
		data.add("ターメリック1");
		data.add("コリアンダー1");
		data.add("生姜");
		data.add("にんにく");
		data.add("サフラン");
		final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, data);
		ListView list = (ListView) findViewById(R.id.list);
		list.setAdapter(adapter);
		list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
				CharSequence msg = ((TextView) view).getText();
				Toast.makeText(MainActivity.this, String.format("選択したのは%s", msg.toString()), Toast.LENGTH_SHORT).show();
			}
		});
	}
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.menu_main, menu);
		return true;
	}
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		if (id == R.id.action_settings)
		{
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}


```
以上。
React: オブジェクト内の配列の中にあるオブジェクトのステートを更新する

Reactで、以下のようなオブジェクト内の配列の中にある`id: 2`のオブジェクトのmemoを`'ccc'`に更新する方法。
```
{
    id: 1,
    name: 'test',
    memos: [
      {
        id: 1,
        memo: 'aaa',
      },
      {
        id: 2,
        memo: 'bbb',
      },
    ],
  }
```

- コード

```
import * as React from 'react';
import './style.css';

export default function App() {
  const [list, setList] = React.useState({
    id: 1,
    name: 'test',
    memos: [
      {
        id: 1,
        memo: 'aaa',
      },
      {
        id: 2,
        memo: 'bbb',
      },
    ],
  });

  const updateMemo = () => {
    setList((prev) => ({
      ...prev,
      memos: prev.memos.map((el, index) =>
        index === 1 ? { ...el, memo: 'ccc' } : el
      ),
    }));
  };

  return (
    <div>
      <h1>Hello StackBlitz!</h1>
      <p>Start editing to see some magic happen :)</p>
      {list.memos.map((el) => (
        <li>{el.memo}</li>
      ))}
      <button onClick={() => updateMemo()}>Update</button>
    </div>
  );
}
```
- StackBlitzで実際に動かしてみる

https://stackblitz.com/edit/react-ts-hftiaw?embed=1&file=App.tsx
生鮮野菜の価格予測 1位 解法 & 振り返り 

Nishika株式会社が主催「[生鮮野菜の価格予測](https://www.nishika.com/competitions/32/summary#description)」コンペに参加しました。本記事で私の取り組みの共有と振り返りをしたいと思います。

# コンペ概要
- 本コンペでは、[東京都中央卸売市場日報](https://www.shijou-nippo.metro.tokyo.lg.jp/)および[気象庁](https://www.data.jma.go.jp/obd/stats/etrn/)よりNishikaにて収集・加工し作成したデータを用いて、過去実績の市場データや天気情報から将来の各野菜の日次卸売価格を予測することをテーマとします。
- 予測対象となる各野菜の日次卸売価格は、RMSPEで評価されました。RMSPEは、予測データ数$n$、真の値$y_i$、予測値$𝑦̂_𝑖$に対して以下の計算式にて算出されます。
```math
RMSPE = 100\left(\dfrac {1}{N}\sum ^{N}_{i=1}\left( \frac{\widehat {y_{i}}- y_{i} }{y_{i}}\right)^2\right)^{\frac{1}{2}} 
```

# コンペ結果
- 最終ランキングでは参加者974人中1位で入賞させていただきました。
![スクリーンショット 2022-09-01 17.28.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456896/f710eddc-a5f7-9a7d-d916-99fee97c8c94.png)

# データ
- データは、野菜に関する市場データと天気データの２つに大別することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456896/46fec43c-1fea-5aff-9f87-2f3a0a84a5a0.png)
- 野菜の種類``kind``は、きゅうりやミニトマト、レタスなど計16品目になります。

# 解法
#### モデリングにあたって
- 価格は多少の急騰はあるかもしれませんが、基本直近の価格±αくらいで推移しそう。
- 野菜によって価格のレンジが大きく異なるため、種類ごとにモデルを作成した方が良さそう。
- 季節で価格が異なるため、学習期間は検証期間と同一の季節/期間で採用した方が良さそう。

#### モデルの構成
- モデルは、市場データと天気データを結合し、野菜の種類ごとにLightgbmで学習・推論をするようなシンプルな構成になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456896/2ddbd635-7145-89fb-b118-257e19c0d59e.png)

#### 前処理
- 予測対象の``mode_price``の過去実績値、天気情報の指数平滑移動平均を特徴量として採用しました。
- 学習させる``mode_price``は、最新のデータの平均値±2SDでクリッピングしています。

#### パラメータの決定方法
- 野菜の種類ごとにベイズ最適化を用いて決定しました。
- 最適化するパラメータは、Lightgbmのハイパーパラメータと学習させる期間の２種類です。
学習させる期間は、何年分遡るか、前後何ヶ月分を参照するかの組み合わせで考えています。図は、２年分遡り、前後２ヶ月を参照する場合の各データの関係を表しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456896/f6bb28fc-0e36-4d05-e318-edeae09178d0.png)

# おわりに
- 運営の皆さま、参加者の皆さま、ありがとうございました。
- 学習期間も最適化するパラメータとして扱ったことが、特に精度向上に繋がったと思います。
テスト設計で使う仕様書はJIRAのエピックやストーリーの集合体で肩代わりできるかもという話

### 事の始まり
ひょんなことからスタートアップのQAエンジニアになり、スピード感のある開発の中で仕様を担保していく必要がある局面に立たされました
いいテストケースを書くにはソフトウェアの仕様書が必要でしたが
スクラップ＆ビルドしながら進んでいく開発の中で仕様書を書くというのは至難の業です

そこでプロジェクト管理をしているJIRAのエピックをもとにして記法を整えれば仕様書の肩代わりになるのではないか？
**コーディングに必要な情報 = テストを作るときに必要な情報ではないか！**
と考え以下の説を提唱しました
異論は認めます


#### 仕様書はエピックやストーリーの集合体で肩代わりにできるかも!

### ストーリーとは？
ユーザーストーリーの略称でエピックに対してユーザーが何を求めて、どのような価値が提供できるかを書きます
タスクはストーリーをスライスしてやること毎にまとめます
> ユーザ：誰がプロダクトを欲しがるのか
アクション：ユーザができること
データ：データを集積したり取り出したり
制御：ポリシー・ルールなど制約を課す（地域、年齢、最大値、最小値、QRコード必須など）
環境：どこで、（オンライン、店舗、電話）
インターフェース：システム、デバイス（印刷なのか、メールなのか）
品質特性：満たすべきパフォーマンス

例：
* ストーリー名
    * ユーザはセール商品を知れる
* 紐づくタスク
    * セール一覧のデザインを検討する
    * セール一覧の取得APIを作成する
    * セール一覧のAPIから情報を取得する
    * デザインから画面を制作する
### エピックとは？
ストーリーやタスクをグルーピングしたもの案件単位やバージョン単位で切ったりいいと思います

関係性はこんな感じ?
<img width="525.75" alt="image.png (40.5 kB)" src="https://img.esa.io/uploads/production/attachments/19067/2022/06/09/123678/eb4ea22e-1949-404d-aa2e-0a8dfb2b3978.png">

### 実際にテスト設計をするにあたって仕様書の内容(例)
* 画面ごとの動作仕様(UI構成図とUIの仕様)
* この機能によりユーザーができるようになること
* 想定されるユーザーのユースケース
* 制約
* 競合する機能
* 非機能的な仕様(応答速度やセキュリティ周り)

### ストーリーやエピックの内容と照らし合わせる
* 画面ごとの動作仕様(UI構成図とUIの仕様) 
    * タスクの成果物を追うとわかる(画面設計をするところで成果物が出るはずなので)
* この機能によりユーザーができるようになること
    * ストーリーのアクションを見るべし
* 想定されるユーザーのユースケース
    * 正常系はストーリー名からわかる
    * 異常系は別に考える or 観点表を作って正常系の派生で異常系が作れるようにする
* 制約
    * ストーリーの制御欄に書かれる
* 競合する機能
    * ストーリーの制御欄に書かれる
* 非機能的な仕様(応答速度やセキュリティ周り)
    * ストーリーの品質特性に書かれる

### つまり
 **ちゃんと記法を揃えれば、外部仕様書がなくてもストーリーやエピックで事足りるかも！！！**
ということです

**皆さん鉞投げ合いましょ！**
.NETの単体テストのコードカバレッジをVisual Studioに表示する

## 前提

以下の内容は本エントリでは言及しない。

1. 単体テストの書きかた
2. xUnit以外のテストフレームワーク
3. レポートジェネレータ

## はじめに

コードのカバレッジをVisual Studioで表示したい場合はEnterprise Editionを使う必要がある。
このエントリではCommunity Editionでカバレッジを表示する方法を紹介する。

## Fine Code Coverageで確認する

カバレッジ情報をExtensionの`FineCodeCoverage`を利用してコードと一緒に確認する。

- [FortuneN/FineCodeCoverage: - github.com)](https://github.com/FortuneN/FineCodeCoverage)

  ![FineCodeCoverageでカバレッジ情報とソースコードが一緒に表示されている](https://raw.githubusercontent.com/FortuneN/FineCodeCoverage/master/Art/preview-coverage.png)

  エディタ内やスクロールバー(マップ)にカラーバーが表示されるようになる。

1. VS 2022 Marketplaceからインストールする (2019もある)

    - [Fine Code Coverage \- Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=FortuneNgwenya.FineCodeCoverage2022)

1. テストメニューの「テストの実行」でテストが走ったあとに自動でカバレッジデータを取得してくれる
1. 取得したデータをエディタ内に綺麗に表示してくれる
1. テストエクスプローラーと一緒に表示すると該当箇所を探しやすくなる
1. 統計情報もタブ付きのビューで確認できる

### オプション

![finecodecovorage-settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1912766/37eb3acf-6725-7262-c646-b3f661ec9f6a.png)

#### カバレッジ出力をとめる

カバレッジの取得がテストのたびに毎回走るのは動作が重くなるので必要な時に機能を有効にするのがオススメ。

- 「オプション」- `Fine Code Coverage`
- Run(Common) - `Enabled`を`false`にする

#### カバレッジのカラーバーを消す

カバレッジ情報のカラーバーがエディタ内に表示されているとビルド時のコンパイルエラーのマークが見えにくいといったことがある。このような場合は表示に関するフラグを`false`にすると消せる。

- 「オプション」- `Fine Code Coverage`
- UI(Common) - いくつかある`ShowXXXMargin`のいずれかを`false`にする

## .NET公式ドキュメント

- [dotnet テストと xUnit を使用した \.NET Core での単体テスト C\# コード \- \.NET \| Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-with-dotnet-test)
- [単体テストにコードカバレッジを使用する \- \.NET \| Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/core/testing/unit-testing-code-coverage?tabs=windows)

## 参考サイト

- [C\# の単体テストのコードカバレッジの結果をWeb形式で出力する \- Qiita](https://qiita.com/kojimadev/items/84d0a31a94fe6b22b450)
- [dotnet core × Unit Test × コードカバレッジ \- きなこもち\.net](https://www.kinakomotitti.net/entry/2021/02/22/142816)
js ホイスティング(巻き上げ)について

jsを触っていると、ホイスティング(巻き上げ)というものが出てきたので
まとめる。

## ホイスティング

```js
 console.log(v)
 console.log(l)
 console.log(c)

 var v = 1
 let l = 2
 const c = 3

//→結果
// Uncaught ReferenceError: Cannot access 'l' before initialization

// ここでvがエラーにならないのは、varはホイスティングしているからです。vはundefinedとして処理されます。
// もしも、変数vのundefinedがどこかで使わるなら大問題になりかねないですね。定義していないのに、使われるわけですから
// その予防策としてletなどを使用するようにしましょう。多分これがvar非推奨の要因なんでしょうな。







// 関数はホイスティングする
Hos()
//動く
HosClass()
//動かずエラーになる

function Hos(){
  var test = 1
  console.log(test)
}
// クラスはホイスティングしない
class HosClass {
  constructor(test){
    this.test = 'testt'
  }
  // var test = 1
  //変数を定義してもエラーになります。
  //なので、クラスを使った際に、変数定義で変な値が入ることはないとこと思われます。
}

```

|`var`    |*巻き込みあり*                  |
|----------|--------------------------|
|`let`  |巻き込みなし         |
|`const`|巻き込みなし|
|`function(関数)`|**巻き込みあり**|
|`class(クラス)`|巻き込みなし|


基本は、巻き込みなしのletを使用する。
constも巻き込みなしなので、使用OK.


関数を使用する際には注意かな。
関数定義を書く前に、関数の使用をするとスルーされてしまうのでご注意を！！！

#### 巻きこみ
自分の中での巻き込みは、
「ダウンタウンのガキの使いやあらへんで !! 体育館で24時間鬼ごっこ !」の
浜田だけカップ麺をあんまり食べさせてくれない時に、巻き込んだシーン！
いやそれは、巻き込み！
今は巻き上げ！

覚えた！
802.11a/b/g/n/ac/ax の規格の比較

# 802.11と修正規格
IEEEに関する現在の標準は802.11の1つだけです。

修正される場合は、たとえば、802.11aや802.11bのような最後に小文字が付く修正された規格が作成されて、最終的に標準である802.11に組み込まれます。

# 802.11の規格の比較表

| IEEE 規格 | 伝達速度 | 周波数帯域 |
| --- | --- | --- |
| 802.11 (1997年) | 1, 2Mbps | 2.4GHz ISM |
| 802.11b (1999年) | 1, 2, 5.5, 11 Mbps | 2.4GHz ISM |
| 802.11a (1999年) | 6, 9, …., 54 Mbps(書くのがめんどくさかった) | 5GHz U-NII |
| 802.11g (2003年) | 1, 2, …., 54 Mbps(書くのがめんどくさかった) | 2.4GHz ISM |
| 802.11n (2009年) | 6.5~600Mbps | 2.4GHz ISMと5GHz U-NII |
| 802.11ac(2013年) | 600Mbps~6.93Gbps | 5GHz U-NII |
| 802.11ax(2018年) | 最高4.8Gbps | 5GHzと2.4GHz |

発展してきたんだな〜

## 802.11

原初の規格です。ほぼ使われていません。FHSSとDSSSを使いますが、ほとんどがFHSSを使っていたらしい

## 802.11b

伝達速度が向上して、いろんな人に使われ始めました。（どちらかというと主に装置の値段が安くなったかららしい）

802.11 DSSSの劣化版らしい
NestJSでAPIをバージョニング管理する

APIサーバーを構築していると、 APIの後方互換を持たせるために、`/v1/`や`/v2/`といったREST APIにバージョンを持たせると思います。

3年前にNestJSでAPIバージョニングをするという記事が書かれていますが、NestJS v8から、新しい機能として実装されましたので、その機能を紹介します。

https://qiita.com/potato4d/items/2d5dc727d33e06260a44

## 手順
有効にする方法は至って簡単
次のコードを追加するだけ！

```diff_typescript:main.ts
const app = await NestFactory.create(AppModule);
+ app.enableVersioning({
+   type: VersioningType.URI,
+ });
await app.listen(3000);
```

### Controllerに宣言するパターン
例えば次のようなコントローラー

```diff_typescript:user.controller.ts
+ @Controller({ version: ['1', '2'], path: 'users' })
+ // `https://api.example.com/v1/user` -> OK
+ // `https://api.example.com/v2/user` -> OK
+ // `https://api.example.com/v3/user` -> 404
export class UserController {
  constructor(private userService: UserService) {}

  @Get('/:id')
  findOneById(@Param('id') id: string) {
    return this.userService.findOneById(id)
  }

  @Post()
  async create(@Body() body: CreateUserDTO) {
    const result = await this.userService.create(body)
    return result
  }

  @Put('/:id')
  async update(@Body() body: UpdateUserDTO, @Param('id') id: string) {
    const result = await this.userService.update(id, body)
    return result
  }
}
```

### Routeに宣言するパターン
```diff_typescript:user.controller.ts
@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}

+ @Version(['1', '2'])
+ // `https://api.example.com/v1/user` -> OK
+ // `https://api.example.com/v2/user` -> OK
+ // `https://api.example.com/v3/user` -> 404
  @Get('/:id')
  findOneById(@Param('id') id: string) {
    return this.userService.findOneById(id)
  }

  @Post()
  async create(@Body() body: CreateUserDTO) {
    const result = await this.userService.create(body)
    return result
  }

  @Put('/:id')
  async update(@Body() body: UpdateUserDTO, @Param('id') id: string) {
    const result = await this.userService.update(id, body)
    return result
  }
}

```

## デフォルトのバージョンを宣言する
`defaultVersion`を指定しておくと、全てのController、Routeにデフォルトのバージョンが宣言されます。
```diff_typescript:main.ts
const app = await NestFactory.create(AppModule);
 app.enableVersioning({
   type: VersioningType.URI,
+  defaultVersion: ['1', '2'],
 });
await app.listen(3000);
```

## `VERSION_NEUTRAL`について
`VERSION_NEUTRAL`を宣言した場合、そのAPIにはバージョンが宣言されません。
```diff_typescript:user.controller.ts
@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}

+ @Version([VERSION_NEUTRAL])
+ // `https://api.example.com/user` -> OK
+ // `https://api.example.com/v1/user` -> 404
  @Get('/:id')
  findOneById(@Param('id') id: string) {
    return this.userService.findOneById(id)
  }

  @Post()
  async create(@Body() body: CreateUserDTO) {
    const result = await this.userService.create(body)
    return result
  }

  @Put('/:id')
  async update(@Body() body: UpdateUserDTO, @Param('id') id: string) {
    const result = await this.userService.update(id, body)
    return result
  }
}
```

# 公式ドキュメント
詳しくは公式ドキュメントがわかりやすいです。

https://docs.nestjs.com/techniques/versioning
SwiftPM を用いたプロジェクトで出たエラー対処法

# エラー内容
SwiftPMを利用しているプロジェクトをcloneしてビルドした時に以下のエラーが出た。

```
the package at '/' cannot be accessed (Couldn’t read 'v10.28.6':
```

# 対処法

`[AppName].xcworkspace/xcshareddata/swiftpm/Package.resolved` に存在するPackage.resolvedを削除する。

削除後、再度ビルドしたらうまくいった。

なお、Finderで上記ファイルにアクセスする場合、[AppName].xcworkspaceを右クリックし、「パッケージの内容を表示」をクリックすればアクセスできます。

# まとめ
その他、良い解決策があれば教えていただけるとありがたいです！

# 参考
* https://stackoverflow.com/questions/67185817/package-resolved-file-is-corrupted-or-malformed
reactでURLパラメータから値を取得する方法

はじめに
--
現在ポートフォリオのフロント側をreactで開発しています。
開発の中でURLパラメータの値を取得したい場面があったので、その中で調べたことを備忘録として残します。

環境
--
react 18.0.0
react router dom 5系
rails 6.1.4
M1mac

パラメータの値を取得
--
値を取得するにはreact-router-domからuseLocationをimportし、searchプロパティをするように記述をします。（const searchの箇所)

そして、URLSearchParamsクラスを使えばパラメータの値を取得する準備が完了します。
(const queryの箇所）

https://developer.mozilla.org/ja/docs/Web/API/URLSearchParams

最後にパラメータの名前(今回だとname)をキーとしてquery.get内で指定してあげれば、値を取得できるようになります。

~~~react
//urlがhttps://hoge.app?name=fugaだった場合("name=fuga"取得したい値)

import React from 'react';
import { useLocation } from "react-router-dom";

const NameQuery: React.FC = () => {
  const search = useLocation().search;
  const query = new URLSearchParams(search);
  const name = query.get('name')

  return (
    <p>{name}</p>
    //fugaが表示される
  );
}
~~~

昨日調べて実装しただけなので、パラメータ取得については数時間しか経験がありません。
まだ深い部分まで理解できていないかもしれませんが、比較的簡単に実装することができました。

他にもuseLocationでできることはあるみたいなので、全体的に調べてみようと思います。

参考
--
https://v5.reactrouter.com/web/example/query-parameters

https://developer.mozilla.org/ja/docs/Web/API/URLSearchParams

https://zenn.dev/urawa72/articles/1476fc2a94aeed373c89
クラウドWebアプリ/IoT通信


(自分用メモ)クラウドWebアプリ/IoT通信について

```plantuml

actor "ユーザ\n/ブラウザ\nまたは機器" as op
participant "IDプロバイダ" as idp
participant "サービスプロバイダ*" as sp
participant "Webホスティング" as web #lightblue
participant "API*" as api #lightblue

note over sp,api:クラウドサーバ
==認証~Token取得==
op->sp:Webサイト表示(URL)
sp->sp:tokenチェック()
op<--sp:[token不正の場合] リダイレクト応答(IdP_URL)
op->idp:開く(IdP_URL)
op->idp:ログイン(ログインID,パスワード)

idp->idp:token発行() (*2)\n- IdP署名\n- 権限範囲\n- 有効期限
op<--idp:応答(token)
op->sp:Webサイト表示(URL,token)
sp->sp:tokenチェック()
sp->web:Webサイト表示()
op<--sp:応答(HTML)

op->op:HTML実行()
==アプリ操作==
```
*: サービスプロバイダ、APIはマイクロサービス、要求毎にサーバが生成される。
*2: ユーザ認証によって発行されるメインtoken(有効期間長)。実際には、個々の通信にはアクセスtoken(有効期間短)が使用される。アクセスtokenはメインtokenを引数としてサービスプロバイダが発行する。本図では省略。

```plantuml
actor "ユーザ\n/ブラウザ\nまたは機器" as op
participant "IDプロバイダ" as idp
participant "サービスプロバイダ*" as sp
participant "Webホスティング" as web #lightblue
participant "API*" as api #lightblue
note over sp,api:クラウドサーバ
==アプリ操作==

op->sp:情報収集(URL,token)
sp->sp:tokenチェック()
sp->api:DBアクセス()
op<--sp:応答(JSONなど)
op->op:Web画面更新()

==token更新(タイムアウト)==

op->sp:情報収集(URL,token)
sp->sp:tokenチェック()
op<--sp:[token有効期限超過]リダイレクト応答(IdP_URL)

ref over op,idp:newToken=認証()
op->sp:情報収集(URL,newToken)
op->op:Web画面更新()
```
作業手順書にmermaidを使ったら便利だったメモ

# 概要
- サービスのメンテナンスの作業手順書にmermaidのガントチャートを使ったという話
- 作業手順書作成時の時間見積と、実際の作業実施時の時間計測の差が簡単に把握できて便利
- 下のような図を出せて便利ということ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514463/ebebb197-e52d-c8fd-9f79-f2212df498af.png)

※2023/08/22 mermaidの記法が変更になったらしく、ガントの分指定が `min` ではなく　 `m` となった

# 実際のメンテナンス作業の時間を把握したい
これらをなんとなく把握したい。

- 決めた手順が実際にどれくらい余裕があったのか把握したい
- 事前にステージング環境で同じメンテナンス作業をして問題ないか確認、本番メンテナンスの予定にフィードバックしたい
- etc.

# どうやって把握するか
## 作業前の時間見積
- 作業手順書に必要時間を記録する
- etc.

## 作業実施時の時間計測方法
- 作業ログを別途作成し、タイムスタンプを記録する
    - 自分は作業チームでMeetに入って作業開始と終了をコメントして、MTG終了前にコメント欄の内容をコピーするのをよくやる
- 作業手順書に見積とは別に計測値として記録する
- etc.


# 比較が面倒だから図にしたい
記録してもテキストで比較すると分かりにくい。
時間を表現する図で便利なのはガントチャート。

テキストベースの作図ツールは[PlantUML](https://plantuml.com/ja)か[mermaid](https://mermaid-js.github.io/mermaid/#/)のどちらかを使うことが多い（個人的感想）。

## 使ってみる
### PlantUML

[PlantUMLのガントチャート](https://plantuml.com/ja/gantt-diagram)では最小単位が日付になっている。
メンテナンスの作業単位は分（minute）レベルなので、作業手順書の時間表現には使えなさそう。

### mermaid

[mermaidのガントチャート](https://mermaid-js.github.io/mermaid/#/gantt)では最小単位が秒まで使える。
日付も指定しなくていいので、作業手順書の時間表現に使えそう。

# 書いてみた
作業手順書はesaで書くことが多く、esaはmermaidにも対応している。以下はesaの出力画像。
ここでは表示されていないが、現在時刻の縦線も表示されるのでリアルタイムで見るときにも便利。

## 実施前の時間見積
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514463/ac48549b-5664-cd8d-3ef1-be84602c7d64.png)

## 実施中の時間計測
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514463/ebebb197-e52d-c8fd-9f79-f2212df498af.png)

## mermaidのコード
余計な日付指定がないので、かなりシンプルに書ける。

```
gantt 

dateFormat HH:mm
axisFormat %H:%M

メンテ開始 : milestone, m1, 12:00,2m

サービスをメンテナンス状態にする : 15m
環境にアクセスできないことを確認 : 5m 
Auroraのスナップショットを取得する : 40m
スナップショット復元する : 40m
Aurora v3からAurora Serverless v2にする : 30m
デプロイ : 10m
確認 : 10m
サービスのメンテナンス状態を解除する : 10m

メンテ終了 : milestone, m2, 15:00, 2m
```

# まとめ
- 細かい手順の表現だけになってしまいがちな作業手順書の概略を図示できるので、共通認識を築くのに便利
- 普段はPlantUML使っているけど適材適所
[小ネタ] Docker で設定ファイルをボリュームマウントする際はディレクトリをマウントするようにしよう

## Dockerでファイルを直接マウントすると書き換え時に面倒くさい

ついついやってしまいがちですが、以下のように **ディレクトリではなくファイルをDockerコンテナにマウントするとちょっと面倒くさいことになります**。

```yaml:docker-compose.yml
version: "3"

services:
  caddy_server:
    image: caddy
    volumes:
      # Caddyfile というファイルをそのままマウントしている
      - ./Caddyfile:/etc/caddy/Caddyfile
```

これがどういうことになるかというと、**Dockerコンテナの外からマウントしたファイルを書き換えても何故か反映されない**といったことが起きます。

私が遭遇したケースだと下記2ケースでそれが発生しました。

- Vimでマウントしているファイルを書き換える
- Ansibleでマウントしているファイルを置き換える

両者に共通しているのは、**ファイルを書き換える際にinodeを変更してしまう**というところです。

inodeを変更するというのはつまり、一部のツールはファイルを上書きする際、上書きというよりはパスが同じだけの別ファイルに置き換える動作をしてしまいます。

**Dockerコンテナに直接マウントしているファイルを書き換えるケースでは、コンテナ外からマウントしているファイルを書き換えると、一見ファイルを変更したように見えても、Dockerがマウントしているファイルは元のまま書き換わらないことがあります。** 別ファイルに置き換えられたファイルをDockerコンテナが参照することはありません。

## じゃあどうするか？

置き換えツールにinodeを変更させないまま上書きさせることもできないわけではないのでしょうが（例：[vimのbackupcopyオプション](https://vim-jp.org/vimdoc-ja/options.html#'backupcopy')）、一般的な解決策とは言えないので、こういった設定ファイルをDockerコンテナにマウントする際は下記のいずれかの方法を取ることになります。

- 設定ファイルを置き換えるたびに**Dockerコンテナを作成しなおす**
- Dockerコンテナに**ファイルではなくディレクトリをマウントする**

コンテナのダウンタイムが問題にならない場合、マウントしている設定ファイルを変更した場合は、きっぱりコンテナをゼロから作り直したほうが色々気にしなくて済みます。

コンテナのダウンタイムが問題になる場合、設定ファイルを含んだディレクトリをマウントする方が無難です。

```yaml:docker-compose.yml
version: "3"

services:
  caddy_server:
    image: caddy
    volumes:
      # Caddyfile というファイルを含んだディレクトリ(./config_dir)をマウントする
      - ./config_dir:/etc/caddy
```

本件、割とAnsibleでコンテナの設定ファイル書き換えているとありがちだと思います。ファイルをマウントする時は気をつけましょう。