OmniAICreator/Qiita-1.07M huggingface dataset:
未経験から直受け独立系SIerに転職するまでPart1

# この記事でお伝えしていること
* 未経験エンジニア転職で行きたいと思った会社の特徴(Part1)
* [スクール](https://tech-camp.in/expert/grant?utm_source=google&utm_medium=ls&utm_campaign=name&gclid=CjwKCAjw5c6LBhBdEiwAP9ejG1iwjAED30lCIk7s0IDvHxKXogUv0Alz8Rxei_7oTr71GznlqAlbhRoCVNUQAvD_BwE)は通うべきかどうか？(Part1)
* 転職活動を有利に進めるために意識したこと(Part2)
* 転職活動時に作成した実際のポートフォリオ(Part2)

# 前提
転職活動をしていく中で主に3人と**コネクションを持ちました**。
1人目は、**未経験エンジニアの採用担当**(エンジニアではない人)
2人目は、**未経験エンジニアの採用担当もしているエンジニア**(未経験でエンジニアになり、5年目くらいの人)
3人目は、**未経験からエンジニアになり3年目の人**
この方々や、**テックキャンプのキャリアアドバイザー**から聞いた話と、私の個人的な意見をベースに書きます。
質問とかあれば、答えます。私も色々な人から教えてもらったので。少しでもお役に立てれば！
##### PGMを学び始めてから内定をいただくまでの流れ
2020年11月　ProgateでRubyを学ぶ
2020年12月　[テックキャンプ](https://tech-camp.in/expert/grant?utm_source=google&utm_medium=ls&utm_campaign=name&gclid=CjwKCAjw5c6LBhBdEiwAP9ejG1iwjAED30lCIk7s0IDvHxKXogUv0Alz8Rxei_7oTr71GznlqAlbhRoCVNUQAvD_BwE)の夜間転職コースに入学
2021年4月　[テックキャンプ](https://tech-camp.in/expert/grant?utm_source=google&utm_medium=ls&utm_campaign=name&gclid=CjwKCAjw5c6LBhBdEiwAP9ejG1iwjAED30lCIk7s0IDvHxKXogUv0Alz8Rxei_7oTr71GznlqAlbhRoCVNUQAvD_BwE)のカリキュラムコンプリート＆独学で+αを学習スタート
2021年8月　転職活動スタート(テックキャンプのサポートあり)
2021年9月　第一希望の企業から内定をいただく
2021年12月　第一希望の企業にて勤務開始予定

##### 内定をいただいた企業情報(社名は控えます)
【本命】
* 独立系SIer企業
* 全件直受け
* アート、ソリューション、自社コンテンツ開発をしている会社
【その他】
SES1社・受託開発会社3社・自社開発会社2社
から内定をいただきました。
全部で20社くらい応募しました。面接に進んだのが、8社くらい。それ以外は書類選考で落ちました。書類選考が厳しいなという印象でした。

# テックキャンプというプログラミングスクールに通って思ったこと
私は、通って本当に良かったと思ってます。実際に受講して感じたメリットとデメリットを、学習内容を簡単に記載します。  
##### [テックキャンプ](https://tech-camp.in/expert/grant?utm_source=google&utm_medium=ls&utm_campaign=name&gclid=CjwKCAjw5c6LBhBdEiwAP9ejG1iwjAED30lCIk7s0IDvHxKXogUv0Alz8Rxei_7oTr71GznlqAlbhRoCVNUQAvD_BwE)での学習内容
Webサービスを作る流れや、ベースを最短で学べたと思っています。友人が本を使って学習していましたが、時間がかかっていたわりに、自力で実装する力があまりついていなかったように思います。
詳しくは、[テックキャンプ](https://tech-camp.in/expert/grant?utm_source=google&utm_medium=ls&utm_campaign=name&gclid=CjwKCAjw5c6LBhBdEiwAP9ejG1iwjAED30lCIk7s0IDvHxKXogUv0Alz8Rxei_7oTr71GznlqAlbhRoCVNUQAvD_BwE)のサイトを見てくださいー。

##### メリット  
* 受講費がまぁまぁかかる！(今は、国の負担もあって安く受けられるみたいなんでHP見てみてくださいー。適用されたのが今年からだったから、まじで悔しい！)  
意外ですが、これだけの金額払ってなかったら、途中で逃げ出した可能性もあったなと…。
* メンターや、相談員、キャリアアドバイザー全員、それぞれの方の意見を尊重した上でアドバイス等をくれること  
当たり前に思うかもしれませんが、意外とそんなことありません。3社くらいのエージェントと面談しましたが、割とこうすれば良いから、こうしなさい！的な物言いで、非常にストレスを感じましたぁ。
* カリキュラムが良い  
教育会社に勤めていたので、職業柄非常にカリキュラムには拘りがありましたが、最高でした。まず、めちゃくちゃ分かりやすい。webアプリを作成する上で必要な概念なども理解できる。そして、自分で学ぶ姿勢が身につく。 
 
##### デメリット
デメリットは、無いかなと思ったので、こうだったら良いのにな！ということを記載したいと思います。

* 環境構築系の知識はほぼほぼ身につかない。
* ペアプログラミングなどもしたかったなぁー  

# 未経験エンジニア転職で行きたいと思った会社
ざっくりいうと、独立系SIerで、全件直受けの会社ですー。もう少し細かく下に書いてきます。
**1. サービスを作る全ての行程に携われる会社**
もともと、テクノロジーを使ったサービスを作ることに興味があったので、コードを書くだけではなくて、企画や、要件定義などの上流工程などにも興味がありました。なので、サービスを作る全工程に携われる会社を希望してました。作業要員はやだー！って思ってましたー。
**2. 独立系SIerに行きたい！**
理由は、2つです。1つ目は、テクノジーの世界を全くしらないので、様々な業種の案件に携わることでテクノロジーでできることを広い視野で知りたい！と思ったから。2つ目は、様々な案件に携わることで、良いサービスを作るためのスキルが多岐にわたって身につくと考えたから。
**3. 絶対直受け！**
テクノロジーを使ったサービスを作ることに面白みを感じてました。なので、クライアントと密にコミュニケーションが取れ、自分やチームの意見が企業に反映される案件に携わりたいと思って直受けに拘りました。仕様書通り作ってくのはヤダー！って思ってましたー！
**4. 社員数が増加している企業**
これは、私個人的な性格の問題かもしれませんが、未経験エンジニアの最初のステップで、リスキーな選択はしたくないなと思ってました。なので、売上、利益とかはみてました。あと、伸びている会社かどうか？という観点で、社員数をみてました。なぜなら、社員を継続的に増やすということは継続的に沢山案件が来ていて、人が足りていないということだと思ったからです。
エンジニア新入社員研修で成長する人の特徴４つ

##はじめに
ある企業に新卒として入社した私が２ヶ月間、同期２５人で受けたエンジニア研修を受けた私の感想とその中で「圧倒的成長をする人」の特徴について紹介していきます！

この研修期間に努力の方法、姿勢によって成長速度に差が大きくでていました！
そんな新人エンジニアで差がつく瞬間をみれたのが面白かったので、思い出がてら書いていきます！

##記事は以下に移動しました！

https://sg-report.com/training/
ハロウィンで遊びながらスクラッチ（scracth）を学ぶ！（１回目／全１０回）

今回からスクラッチ（scratch）を使ったハロウィンゲームを作ります。

全１０回でゲームが作成されます。まず以下のURLにスクラッチの初期状態を置きましたのでクリックして見てください。

https://scratch.mit.edu/projects/244955020/

猫が一匹いますよね。

・みどりのはたをクリックすると、スタートします。（今はなにも作ってないので、動きません(-_-メ)）

・右上の「中を見る」と書いた青いボタンをクリックすると、作成画面になります。



スプライトというのが、登場する人物、ものなどのことをいいます。今は猫だけがいます。


とりあえずここまで大丈夫かな？


https://programingo.com/
Android 12 Advertising ID

## 2021/10 時点
2021/10 に Android 12 が正式リリースされましたが、こちら Adverting ID に関する変更はありません。<br>
Google の説明によると下記のような変更は 2022 の早期にやります。というアナウンスのみです<br>
不気味なのは、これをサーバー側のスイッチもしくはパッチか何かで当てられると、自動的に適用され、ある日突然取れなくなるということです。<br>
ですので、2021のうちに準備しておくのがよいかと思います。

2021/11 
実機の挙動の変更が確認されました。Googleの ADIDの説明によると isLimitAdTrackingEnabled が true なら
00000000-0000-0000-0000-000000000000 が返される
Google の設定より、ADIDの削除というメニューが表示された (Pixel 4aの場合)

### 現在の仕様
* isLimitAdTrackingEnabled が true でも ADID はとれる (なんというざる)
* ユーザーのデバイスOS、アプリケーションのコンパイルSDKのバージョンに関わらず取得可能
* Android 12 のデバイスで isLimitAdTrackingEnabled が true の場合 00000000-0000-0000-0000-000000000000 が
返ってくる 

試しにやってみた結果 (当方が開発しているSDKとアプリの関係も入っています)

|  デバイス  |  SDKコンパイル/ツールバージョン  | アプリのコンパイル/ツールバージョン | パーミッション | トラッキング設定 | ADID |
| ---- | ---- | ---- | ---- | ---- | ---- |
|  Android 12 エミュレータ  |  29/30.0.2  | 31/30.0.0 | なし | デフォルト | 取得可能
|  Android 12 エミュレータ  |  29/30.0.2  | 31/30.0.0 | なし | Limit track | 取得可能
|  Android 8 実機 | 29/30.0.2 | 31/30.0.0 | なし | デフォルト | 取得可能


## 参考
[Advertising ID](https://support.google.com/googleplay/android-developer/answer/6048248?hl=en)
[AdvertisingIdClient.Info](https://developers.google.com/android/reference/com/google/android/gms/ads/identifier/AdvertisingIdClient.Info#getId)

## 変更の概要
* Android 12の API(31) でアプリケーションをビルドした場合に適用される
* パーミッションの設定が必要 (レベルはノーマルなのでダイアログの表示はなく、インストール/アップデート時に表示される、これを許可しないとそもそもインストール/アップデートできない)
* パーミッションがない場合  "00000000-0000-0000-0000-000000000000" になる

アプリケーション開発をされている皆様にはこちらの方法で実装してADID の設定を許可します。
1. AndroidManifest.xml を設定
2. 依存関係に com.google.android.gms:play-services-ads-identifier:17.1.0 以上を入れておく

### AndroidManifest.xml
パーミッションが必要とのことでパーミッション設定を AndroidManifest.xmlに入れます。
```xml
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
```

### Google Play ADS Identifier のバージョンを 17.1.0 以上にする
現在の最新版は 17.1.0 です

![Screen Shot 2021-10-18 at 21.02.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165147/35de5935-6358-7bf1-ed44-abdbd15f56b3.png)

これを入れると、このaarに勝手にパーミッションが含まれている

## アプリケーションを開発をしているが、入れているSDKの設定を入れたくない
アプリケーションの仕様上どうしてもこのパーミッションを取り入れたくない場合、アプリケーションの設定次第で打ち消すことが
可能です。以下のように設定します。

```xml
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove"/>
```
tools:node="remove" によりSDKなどの設定を打ち消します。

## SDKなどを開発する場合の設定について
当方、SDKの開発をしていますが、SDKの場合、以下の2つの方法が取れます
1. SDKのビルドにパーミッションを入れておく(AndroidManifest.xml)
2. 依存関係に com.google.android.gms:play-services-ads-identifier:17.1.0 以上を入れておく

1.は単純に該当のSDKを入れたら自動的にAndroidManifest.xml がアプリケーションのそれとマージされます
2. は依存関係で 17.1.0 のバージョン以上には、AndroidManifest.xml に自動的に入っているので1同様
マージされます (入っているかどうかはデコンパイルツールで確認済)


ちなみにSDKのコンパイルバージョンが 31 でなくても、このパーミッションの設定を入れることは可能です。
Python C# データ連携

こんにちは、本日はPythonとC#間でデータのやり取りをする方法を紹介します。
使用する技術は「共有メモリ」です。

## 動作の仕組み

1. Pythonで名前付き共有メモリの作成
2. C#でPythonで作成した共有メモリを開く
3. 相互にデータの読み書きを実施  

## 環境

**Windows10**
※名前付き共有メモリを利用するにはWindowsOSを利用する必要があります。

pythonでの名前付き共有メモリはWindowsでしか利用できません。。。
「tagname」の引数が共有メモリの名前です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586845/8a76893d-5bc1-5172-7fda-8908e7e27605.png)

Linux、Macなどでは無名の共有メモリを利用するか、
ファイルベースの共有メモリを利用するしかできません。

## ソースコード

今回のプログラムはPythonで共有メモリの1WORD目の値をインクリメントしていき、
C#側でその値を読み取っていきます。

### Python 共有メモリの作成＆書き込み

```python:MMapTest.py
import mmap
import time

WORD_SIZE = 2
MMAP_FILE_NAME = "MAP_TEST"

def mmapWriteShort(adr:int, data):
    '''
    指定のアドレスに値を書き込む(Shortデータ)
    '''
    global mm
    
    try:
        ddata = int(data)
        bytes = ddata.to_bytes(WORD_SIZE, 'little',signed=True)

        for i in range(WORD_SIZE):
            mm[adr*WORD_SIZE + i] = bytes[i]
    except Exception as e:
        pass

def mmapReadShort(adr:int):
        """指定のアドレスの値を読み込む(Shortデータ)

        Args:
            adr : Shared memory address
        """    
        try:
            mm.seek(adr*WORD_SIZE)
            bytes = mm.read(WORD_SIZE)
            val = int.from_bytes(bytes, 'little',signed=True)
            mm.seek(0)
            return val
        except Exception as e:
            return None

if __name__ == "__main__":
    # 名前付き共有メモリの作成
    # 1つ目の引数を−1にすることで、ファイルなしの共有メモリを生成できる
    # （tagname=Noneの場合は無名共有メモリとなる）
    mm = mmap.mmap(-1, 13, tagname=MMAP_FILE_NAME)

    cnt = 0
    
    # アドレス0番地の値をインクリしていく
    while True:
        cnt += 1
        mmapWriteShort(0, cnt)
        #print(mmapReadShort(0))
        time.sleep(1)
```

### C# 共有メモリの読み出し

```c#:Form1.cs
using System;
using System.Text;
using System.Windows.Forms;
using System.IO.MemoryMappedFiles;

namespace SharedMemTest
{
    public partial class Form1 : Form
    {
        MemoryMappedFile _share_mem;
        MemoryMappedViewAccessor _accessor;

        int READ_ADR = 0;
        const string MMAP_FILE_NAME = "MAP_TEST";

        public Form1()
        {
            InitializeComponent();

            // Pythonで作成した名前付き共有メモリを開く
            this._share_mem = MemoryMappedFile.OpenExisting(MMAP_FILE_NAME);
            this._accessor = this._share_mem.CreateViewAccessor();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Enabled = false;

            byte[] data = new byte[2];
            int readsize = _accessor.ReadArray<byte>(READ_ADR, data, 0, data.Length);
            short shortData = BitConverter.ToInt16(data, 0);

            label1.Text = shortData.ToString();

            timer1.Enabled = true;
        }
    }
}
```

## 注意書き

エラー処理は実装していないので、適宜追加してください。

ファイル無しの共有メモリの場合は、
アプリ終了時に共有メモリの値がクリアされるので、
最後の状態を覚えておきたい場合などはファイル有りの共有メモリの利用を検討ください。

## 共有メモリ参考記事

### Python 共有メモリ

> [Python mmap 使い方](https://qiita.com/Yusuke_Pipipi/items/bf6ebeb22404c3d3e814)

### C# 共有メモリ

> [C# 共有メモリを利用する](https://www.ipentec.com/document/csharp-use-memory-mapped-file)

## 更新履歴
* 2021/10/24 初版
Unreal Engineでイベントグラフのノードの実行ピンを複数にして処理を分岐させる。

 C++でカスタムノードを作成した際、内部での処理の結果に応じて実行ルートを分けたい場合が多々あります。
例えば、キャラクターのライフ値を回復させる処理で、既に満タンだった場合と、満タンではなく回復を実行した場合で、イベントグラフの処理を分岐させたい場合を考えてみます。

まず、出力側の実行ピンを複数に分けるため、ピンの名前をenum(列挙型)として定義します。

```C++
UENUM(BlueprintType)
enum class BranchLife : uint8
{
    Regained,
    AlreadyMax
};
```
:::note warn
このenumの定義は関数の宣言の前に入れてください。順番が違うとエラーになります。
:::

次に、ノードとなる関数を宣言します。

```C++
UFUNCTION(BlueprintCallable,meta = (ExpandEnumAsExecs = "Branch"))
    bool RegainLife(int Delta,BranchLife& Branch);
```

重要なのは、meta = (ExpandEnumAsExecs = "Branch")で引数を実行ピンとして指定するのと、実行ピンにする引数を参照(&)にすると出力ピンになるところです。

次に関数を実装します。

```C++
bool APawnHasLife::RegainLife(int Delta,BranchLife& Branch)
{
    if(Life<MaxLife){
        Life += Delta;
        if(Life>MaxLife){
            Life = MaxLife;
        }
        Branch = BranchLife::Regained;
        return true;
    }else{
        Life = MaxLife;
        Branch = BranchLife::AlreadyMax;
        return false;
    }
}
```

実行ピンを出したい場所で、実行ピンとして指定した引数に定義したenumを代入してください。
Branch = BranchLife::Regained;
と
Branch = BranchLife::AlreadyMax;
のところです。

これで、Regainedという名前の実行ピンとAlreadyMaxという名前の実行ピンが作られます。

Regainedに回復時のヴィジュアルエフェクトを実行する処理を接続して、AlreadyMaxに既に満タンだった旨のメッセージを表示する処理を接続する、といったものが考えられます。


参考文献

https://tamfoi.hatenablog.com/entry/2016/06/02/095807

https://answers.unrealengine.com/questions/36107/blueprints-with-multiple-exec-output-pins.html
Poetryで変更したプロジェクト名やモジュール名がModuleNotFoundErrorで反映されないときの対処方法

# 概要
Poetryで作成したプロジェクトの名前やプロジェクトフォルダ内のファイル名を変更したとき、実行すると下記のようなエラーが発生したので対処方法について書きたいと思います。

下記例では、`sample_poetry`というプロジェクト名から`sample_poetry_new`へ変更した際に発生したものです。

```bash
$ poetry run python sample_poetry_new/main.py
Creating virtualenv sample_poetry-Jl97yVcW-py3.9 in /home/hoge/.cache/pypoetry/virtualenvs
Traceback (most recent call last):
  File "/home/hoge/sample_poetry_new/sample_poetry_new/main.py", line 1, in <module>
    from sample_poetry_new import util
ModuleNotFoundError: No module named 'sample_poetry_new'
```

変更したモジュール名がpoetry側で反映されていないのが問題そうです。


# 対処１：キャッシュの削除
エラー出力を見るとホーム配下の`.cash`ディレクトリに仮想環境を作って実行していそうです。

```bash
/home/hoge/.cache/pypoetry/virtualenvs
```

これを削除します。

```bash
$ rm -rf ~/.cache/pypoetry
```

# 対応２：tomlの編集
nameに変更したプロジェクト名を入れます。

```toml:pyproject.toml
[tool.poetry]
name = "sample_poetry_new" # 編集
version = "0.1.0"
description = ""
authors = [""]
```

その他モジュール内で旧プロジェクト名が存在していた場合は、適宜変更を行ってください。


# 対応３：再インストール
削除してしまった依存関係をpoetryで再インストールします。

```bash
$ cd sample_poetry_new
$ poetry install
```

# 結果
上述した対応を行うことで、動作するようになっているはずです。
（poetryコマンドで一括変更とかできそうな気がしなくもないので、もしもっと便利な方法があればコメントでお教えください）
Microsoft Listsの条件付き書式の設定時のフィールド名の命名ルール

# 気づいた限りのルール
1. 内部変数名の文字長は32バイト
2. 変数名は英字のみの場合はそのまま
3. 英字以外の記号、漢字などが入ると、次のルールに従って変数名が定義される
4. 3のルールに基づき連なった文字列を先頭32バイトで命名する

## 3. 英字以外の記号、漢字などが入ると、次のルールに従って変数名が定義される
英字以外の記号、漢字などが入ると、次のルールに従って変数名が定義される

```
_{対象の文字のUNICODE}
```

## 例
`対`ならば、

```
_x5bfe
```
## 3のルールに基づき連なった文字列を先頭32バイトで命名する
`隣の客も良く柿食う客だ`が列名ならば、それぞれのUnicodeを列挙し、結合後、先頭32桁が変数名になる。
ネットワークダイナミクス入門　森北出版　会田雅樹先生

```{r}

#p.25

#スケーリングされたラプラシアン行列S0を求める
A=matrix(c(0,2,2,0,3,0,3,3,1,1,0,1,0,1,1,0),ncol=4)
D=diag(c(apply(A,1,sum)))
L0=(D-A)

eigval_L=eigen(t(L0))$values
eigval_L[abs(eigval_L)<10^(-7)]=0
eigvec_L=eigen(t(L0))$vectors
m=abs(eigvec_L[,eigval_L==0])
t(m)%*%L0

M=diag(c(m))
S0=diag(1/sqrt(m))%*%t(L0)%*%diag(sqrt(m))
Y=eigen(S0)$vectors
lam=eigen(S0)$values
lam[abs(lam)<10^(-7)]=0


#kijを要素とする行列L
L=M%*%L0
eigen(L)$vectors
eigen(L)$values

#逆にLのkijからｘを求めてみる p.31

#Broyden Quasi-Newton Algorithm p.213

f=function(x){

lambda=x[1]
x=x[-1]
  
f1=c()
for(i in 1:length(x)){
vec=L[i,]
value=vec[i]
vec[i]=0
dx=2*sum(vec*value-vec*x)-2*lambda*x[i]
f1=c(f1,dx)  
}
f2=sum(x^2)-1
return(c(f2,f1))
}

X=rep(1,ncol(L)+1)

ite=12000
eta=10^(-2)

H=diag(f(X))

for(l in 1:ite){
X_pre=X  
X=X-eta*H%*%f(X)  
s=X-X_pre
y=f(X)-f(X_pre)
H=H+((s-H%*%y)/as.numeric(t(s)%*%H%*%y))%*%t(s)%*%H
print(sum(abs(f(X))))
}  

lam=X[1]
X=X[-1]

```

```{r}


#p.42 無効グラフ上の拡散方程式

#無効グラフ上の拡散方程式を連立微分方程式としてオイラー法で解く

A=matrix(c(0,2,2,0,3,0,3,3,1,1,0,1,0,1,1,0),ncol=4)
D=diag(c(apply(A,1,sum)))
L0=(D-A)

eigval_L=eigen(t(L0))$values
eigval_L[abs(eigval_L)<10^(-7)]=0
eigvec_L=eigen(t(L0))$vectors
m=abs(eigvec_L[,eigval_L==0])
t(m)%*%L0
#固有値0の左固有ベクトルは定常分布
pm=m/sum(m)
t(pm)%*%L0

M=diag(c(m))
S0=diag(1/sqrt(m))%*%t(L0)%*%diag(sqrt(m))
Y=eigen(S0)$vectors
lam=eigen(S0)$values
lam[abs(lam)<10^(-7)]=0


#kijを要素とする行列L
L=M%*%L0

#オイラー法で計算してみる

#初期値
t=0
x=sample(1:9,ncol(L))/100
x0=x
ite=100
h=0.0001

vecs=t(array(0,dim=c(length(x),ite)))
ts=c()

for(l in 1:ite){
dx=c()
for(i in 1:length(x)){
dx=c(dx,x[i]*sum(L[i,-i])-sum(x[-i]*L[i,-i]))  
}
x=x+h*dx  
t=t+h
ts=c(ts,t)
vecs[l,]=x  
}



#厳密解
lam=eigen(L)$values
vmu=eigen(L)$vectors
xt=array(0,dim=dim(vecs))
for(l in 1:ite){
xs=rep(0,length(x0))
for(i in 1:length(x0)){
xs=xs+sum(x0*vmu[,i])*exp(-lam[i]*ts[l])*vmu[,i]  
}
xt[l,]=xs
}

```
Pythonでできること

①WEBアプリの開発
②ゲーム開発
③ドローン
④Iotアプリ開発（ラズベリパイ）
⑤作業の自動化ができる
⑥スクレイピング
⑦データ解析
⑧機械学習
⑨ディープランニング
Google Apps Scriptをネイティブアプリ風にする

Google Apps ScriptでPWAが作れれば何と嬉しいか...といつも思ってました。個人で開発する際にはFirebaseとかVercelとか今時なんなっと出来ますが、会社の中でやろうとすると途端にハードルが上がります。

会社にG-suiteがあるならばその環境下で手軽にWebアプリをネイティブアプリ風にし利便性を向上させる事が出来ます。

# ポイント

- 社内にG-suiteがあるならばHTMLをコストをかけずにホスティング出来る
- 画面が全画面になる為、ネイティブアプリ風
- Safariで動くが独立したスレッドとして動くので、単独のアプリライクに使える

# 解決していない事

- manifest.jsonやlink.relが設置できない為、アイコンの設定方法だけ実現出来ず
- manifest.jsonの配信が出来ない為、通常のWebホスティング程細かい設定は出来ない

# 今回使うもの

- G-suite
- Google Apps Script

# やり方

Google Apps ScriptでHTMLを呼び出す時は通常以下のようになります。

```
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index")
    .setTitle("sample")
    .setFaviconUrl("https://xxx&.png")
}
```

これをただこのようにし、ホーム画面に設置してそこから起動するだけです。

```
function doGet() {
  return HtmlService.createHtmlOutputFromFile("index")
    .addMetaTag('viewport', 'width=device-width, initial-scale=1')
    .addMetaTag('apple-mobile-web-app-capable','yes')
    .addMetaTag('mobile-web-app-capable','yes')
    .setTitle("sample")
    .setFaviconUrl("https://xxx&.png")
}
```


するとPWAとして作成した時のように全画面、かつ独立スレッドでアプリライクに起動します。
今時そんなに高くもないのでローコードアプリサービスくらい使えば？はあるかもしれませんが、セキュリティの問題等で実現できない方はこんなやり方なら妥協点としていいんじゃないかと思います
【Flutter, Android】内部テストのアプリでFirebaseのAuthenticationのログインが失敗する

## 何が起こったか
FirebaseのAuthenticationを使用し、GoogleログインをFlutterアプリ内で実行しようとしたら下記のエラーが発生しました。

```
PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null, null)
```

しかも、debugモードやreleaseモードで実行したアプリでは発生せず、内部テストにアップロードしたアプリでのみ再現しました。

## 試したこと
debugモードやreleaseモードではログインできるのに、なんで内部テストにアップロードした途端に失敗するんだよと思いつつ、上記のエラー内容を調べるとSHA1のフィンガープリントがfirebaseのコンソールに登録していないのが原因とわかります。
しかし、いくらFirebaseのコンソールを確認してもSHA-1のフィンガープリントは登録されていました。
それもそのはず、debugモードやreleaseモードではログインできるのだから。

## 原因
原因はFirebaseではなく、PlayConsoleの設定でした。
PlayConsoleの「設定」->「アプリの完全性」にある、**SHA-1 証明書のフィンガープリント**という欄にFirebaseに設定しているSHA-1と同じ値を登録する必要がありました。(Dynamic Linksを使用している場合はSHA-256の方も登録する必要がある)
登録すると、内部テストにアップロードしたアプリでGoogleログインが成功すると思います。

![スクリーンショット_2021-10-24_1_43_26-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263659/0be7077f-2908-a9fa-26e3-145c059e4cfb.png)

## 終わりに
調べてみるとなんてことないですが、PlayConsoleの扱いに慣れていないと初見ではわからないですね。
あと多分Flutter関係ないですね。

## 参考記事
* https://joyplot.com/documents/app-bundle-platform-exception/
* https://stackoverflow.com/questions/54557479/flutter-and-google-sign-in-plugin-platformexceptionsign-in-failed-com-google
* https://qiita.com/surasurag/items/54134821da011ffa6ac5
【Ruby】複数の繰り返しメソッドの備忘録

#目的
複数の繰り返しメソッドの備忘録

#結論
6種類あった。
times
for
while
until
each
loop

```ruby:timesメソッド　#回数指定された繰り返し
3.times do
  puts "Hello World"
end
=>Hello World
  Hello World
  Hello World
```

```ruby:for文　#オブジェクトから要素を取り出す場合
sum = 0
for i in 1..5
  sum = sum + i
end
puts sum
=>15
```

```ruby:while文　#条件を自由に指定したい場合
i = 1
while i < 3
  puts i
  i += 1
end
=>1
  2
```

```ruby:until文　#whileではわかりにくい条件を指定したい場合
sum = 0
i = 1
  until sum >= 50
  sum += i
i += 1
end
puts sum
=>55
```

```ruby:eachメソッド　#オブジェクトから要素を取り出す場合
names = ["awk", "Perl", "Python", "Ruby"]
names.each do |name|
  puts name
end
=>awk
  Perl
  Python
  Ruby
```

```ruby:loopメソッド　#回数制限のない繰り返し
loop do
  puts "Ruby"
end
=>Ruby
  Ruby
  Ruby.....
```
#参考
たのしいRuby第５版
【bootstrap】モーダルの作り方

#はじめに#
　モーダルは、確認メッセージやエラーなどの際に表示されるポップアップに利用されます。アラートのように設置する場合は閉じるだけですが、フォームを設置して次の画面へ繋げるための中間画面として使われる場合もあります。
　bootstrapを使うことで簡単にモーダルが設置できますので備忘録として残していきます！

#書き方#
　[公式ホームページ](https://getbootstrap.jp/docs/4.2/components/modal/)から確認することができます。

```modal.html
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>
```

コピペしてきました。基本はこの形になります。
モーダルを表示させるのにクリックするボタンにはdata-toggleを指定します。
そして表示させたいモーダルのidをdata-targetで指定します。
この場合は、Launch demo modalというボタンを押すとexampleModalというidが振られたモーダルが開くというわけですね。

モーダルの方にはheaderとbodyとfooterを指定することができます。
footerにあるdata-dismissを設定すると、モーダルを閉じるボタンが作れます。
さらに、footer部分にformタグを設置することで退会や削除など、取り返しがつかない処理を行う際の確認画面としても使えたりします。

#終わりに#
モーダルを使うことでアプリとしての利便性が高まりますのでぜひ使ってみましょう！
Herokuへデプロイする手順

ローカルで作成したDjangoアプリをHerokuへデプロイする手順を簡単に紹介します。
![heroku_フロー図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1667780/a17c189e-b0ac-c98b-dc6b-f7afd23ae26a.jpeg)

#### 必要なライブラリのインストール

```python
pip install gunicorn
pip install django-heroku==0.3.1
```
***

#### Procfileを作成
下記のように process type : commandの順で記述します。ファイル名の「P」は大文字にしましょう。

```python
web: gunicorn helloworldproject.wsgi
```
***

#### requirements.txtを作成
Herokuに必要なライブラリを教えるために用意します。下記のコードを実行することで自動的に必要なライブラリが記載された「requirements.txt」ファイルが作成されます。

```python
pip freeze > requirements.txt
```

下記のように記述されたファイルが作成されているはずです。

```python
asgiref==3.2.10
dj-database-url==0.5.0
Django==3.0
django-heroku==0.3.1
psycopg2==2.8.6
pytz==2020.1
sqlparse==0.3.1
whitenoise==5.1.0
gunicorn==20.0.4
```
***

#### runtime.txtを作成
手動で「runtime.txt」という名前でファイルを作成し、ご自身のPythonのバージョンを記述してください。

```python
python-3.9.6
```
Pythonのバージョンの確認方法

```
python --version
```
***

#### .gitignore ファイルの作成

```python
*.pyc
*~
__pycache__
djangovenv
db.sqlite3
/static
.DS_Store
```
***

#### Heroku へログイン
Herokuアカウントがない方はアカウント作成してください。

```python
heroku login
```
***

#### アプリケーションを作成

```python
heroku create アプリケーション名
```
***

#### アプリケーションとの紐付け

```python
git init
heroku git:remote -a アプリケーション名
```

***

#### 現在操作しているアプリケーションが heroku 側とつながっているのか確認
実行後、作成したアプリケーションの名前が表示されていればOK

```python
git remote -v
```
***

#### Git への Commit

```python
git add .
git commit -m 'first commit'
```

一行で行う場合

```python
git commit .
```
***

#### Heroku へ Push

```python
git push heroku master
```

pushでエラーが出たら下記を実行後、再度pushする

```python
heroku config:set DISABLE_COLLECTSTATIC=1
```
***

#### アプリケーションの確認

```python
heroku open
```

***
#### エラーが出た時に確認

Herokuのログを確認

```python
heroku logs
```

Free dyno時間の残量を表示

```python
heroku ps -a <app name>
```
Human Interface Guidelinesを理解する1

Human Interface Guidelines
---
Appleが公式で出しているIOSデザインの原則のこと、Iphoneだけでなく、TVos ,macOS,watchOSなどもしっかり書かれている。

基本的な三原則
--

![スクリーンショット 2021-10-24 8.04.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1474285/53c09650-60e7-561b-4998-83302527555a.png)

Clarity
-
わかりやすいこと。
見やすいテキストデザイン,
テキストが明瞭な色であること、
ちょうど良い余白であること
アイコンのわかりやすさ
装飾は最低限に、機能性からレイアウトを考えること。

Deference:（機能性.コンテンツへの）尊重
--
コンテンツや機能性視点でアニメーションや装飾を考える。邪魔しないこと

Depth：奥行き
--
Viewの階層もコンテンツや機能から考える。

アプリのインパクトを最大にする設計原則
--

Aesthetic Integrity デザイン面の整合性
--

アプリの機能と見た目・動作の整合性が取れているかを考える。 
機能性重視のアプリの場合、目立ちすぎないグラフィック・iOS標準のパーツの可能な動作を取り入れることで、機能性をわかりやすくする。
ゲームなどの世界観を大事にするアプリの場合、ワクワクするような見た目を提供する必要がある。

Consistency　デザインにIOSに沿った一貫性を持たせる
--
iOSが提供するUI、慣れ親しんだアイコン、標準のテキストを使って、IOSに沿った一貫性のあるデザインを実現させる。

Direct Manipulation - コンテンツを直接操作できる
--
コンテンツを直接操作した結果を容易に理解できる
ナビゲーションさせるためのスワイプ、コンテンツを並び替える時のドラッグ操作。
画面向きを変更する回転等ができる。

Feedback - 操作に対するフィードバック
--
実行した操作によって得られた結果をユーザーに提供する。ユーザーが期待する挙動を返さなくてはいけない。
タップされた項目をハイライトする、
実行した処理の進行状況をインジケーターで知らせる、処理が完了したら完了アニメーション、サウンドによってユーザーに結果を知らせる等のこと

Metaphors - 慣れ親しんだ体験をアプリ上でも再現する
--
アプリ上のオブジェクトや操作が、現実の慣れ親しんだ体験のメタファーである。 
ユーザーは画面を操作し、このメタファーはiOSで機能する。 
スイッチの切り替え、ピッカーのスクロール操作する等のこと

User Control - 操作はユーザーに主導権がある
--
操作の主導権はユーザーである
アプリ側から、操作の提案をしたり、その操作による危険性を警告することは可能だが、アプリが意思決定をユーザーから奪うのは良くない。
項目を削除する場合、この項目を削除することが可能であると提案し、削除の危険性を警告することができる。かつ削除処理中にその操作をキャンセルできるようにすること。削除操作の開始から終了まで、常にユーザー自身がコントロールしていると感じられるようにする。

「Interface Essentials(インターフェースの基本)」
---

iOSアプリは、共通のインターフェイス要素が定義されたフレームワークであるUIKitのコンポーネントを使用する。
このUIKitを使い、アプリ全体で一貫したデザインを実現すると同時に、カスタマイズもうまくできる
UIKitは柔軟性に優れている。どのiOSデバイスでも見栄えの良いアプリを設計でき、IOSがデザインの変更を導入すると自動的にアップデートされます。
UIKitが提供するインターフェース要素は、下のの3つの主要なカテゴリーが存在する

・Bars
アプリのどこにいるのかを伝え、ナビゲーションを提供します。
アクションの開始や情報を伝えるためのボタン、その他の要素を含めることもできます。

・Views
テキスト、グラフィックス、アニメーション、インタラクティブな要素など、ユーザーがアプリで見るコンテンツも含まれる。
ビューに対しては、スクロール、挿入、削除、配置などの動作が可能です。

・Controls
アクションを開始し、情報を伝えます。ボタン、スイッチ、テキストフィールド、およびプログレスバーなど。
【Dart】非同期取得した値をTextFieldの初期値に入れる方法

#何をするのか？
非同期で取得した値をTextFieldの初期値に入れるのに詰まったので、備忘録としてまとめていきます。

#TextFormFieldではできない？
通常DartでTextFiledに初期値を設定する場合、以下のようにTextFormFieldのinitialValueに値を設定します。
しかしこの方法だと設定する値はビルド時に初期化されるみたいなので、ビルドを通した後に取得した値などを設定した場合には初期値として反映されなくなります。

```dart
TextFormField(
    initialValue: "Test",
)
```

#TextEditingController
以上のような理由があるため、ビルド後に取得した値を初期値として反映するためにはTextEditingControllerを使用する必要があります。
コードは省略していますが、まずStatefulWidgetにして画面を開くたびにinitStateが走るようにしておきます。そしてTextEditingControllerクラスをインスタンス変数として定義し、setState内でそのインスタンス変数のtextを更新することで、もう一度initStateがよばれ画面を再構築します。
あとはTextFieldのcontrollerにプロパティにtoken_controllerを設定することで、対象のTextFieldの初期値が反映されます。

```dart
final TextEditingController token_controller = TextEditingController();

@override
void initState() {
  super.initState();
  getValue();
}

void getValue() {
  // 例としてここではキーチェーンを取得しています。
  FlutterKeychain.get(key: 'token').then((token) => {
    setState(() {
      token_controller.text = token;
    }),
  });
}

// 省略
TextField(
  controller: token_controller,
  // 省略
)
```

#参考
https://quality-start.in/2021/08/flutterで非同期で取得した値をtextfieldに入れて初期化した/
【エラー解決】Trying to access array offset on value of type null

#はじめに#
　Laravelでちょっとハマったので一応備忘録として。

#Trying to access array offset on value of type null#
 Laravelでview composerに値を渡そうとしたときにぶつかりました。

```AppServiceProvider.php
view()->composer('*', function($view){
     $user = \Auth::user();
     $post_model = new Post();
     $post = $post_model->get_post($user['id']);
     $view->with('user',$user)->with('post',$post);
});
```

我らがGoogle先生によると、
「null型の値で配列オフセットにアクセスしようとしています」
だそうです。
どうやらまだユーザー情報がない状態でget_postメソッドの引数に\$user['id']を渡しているのが原因のようです。
それでは、あらかじめ配列を定義してあげましょう。

```AppServiceProvider.php
view()->composer('*', function($view){
     $user = \Auth::user();
     if(empty($user)){
         $user = ['id' => 0]
     }
     $post_model = new Post();
     $post = $post_model->get_post($user['id']);
     $view->with('user',$user)->with('post',$post);
});
```

\$userがない場合に\$user['id']を定義してやればエラーはなくなるはずです。

#終わりに#
　無事に解決できました！
JUnit5で環境変数が指定されていない場合にテストを実行しないようにする

``EnabledIfEnvironmentVariable``アノテーションをクラスまたはメソッドに付与する。複数の環境変数が必要な場合は``EnabledIfEnvironmentVariables``を使用する。

```java
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariables;

@EnabledIfEnvironmentVariables({
    @EnabledIfEnvironmentVariable(named = "HOGE", matches = ".*"),
    @EnabledIfEnvironmentVariable(named = "FUGA", matches = ".*")
})
public class SampleTests {
```

Visual Studio Codeの場合は``settings.json``に環境変数を指定しておくとGUIでもテストが実行できる。

```settings.json
    "java.test.config": {
        "env": {
            "HOGE": "foo",
            "FUGA": "bar"
        }
    }
```
【Python】公開鍵で暗号化した文字列を秘密鍵で復号化する方法

# はじめに

**システム外で暗号化したパスワードをシステム内で復号化して利用したい** というニッチな要望に応える記事です。
英語サイト含めまともな情報が少なく、最終的にあまり見ないであろう方法で動作しました。
人に説明できるほど暗号化技術に詳しくないため、備忘録がてら以下で手順のみ書いていきます。

# 手順

## 1. キーペアを作成する

```shell
# 秘密鍵生成
openssl genrsa 2048 > private-key.pem

# 秘密鍵をPKCS#8に変換
openssl pkcs8 -topk8 -in private-key.pem -inform PEM -out private-key_pkcs8.pem -outform PEM -noiter -nocrypt

# 公開鍵を生成
openssl rsa -in private-key_pkcs8.pem -pubout -out public-key.pem
```

※ ファイル内の改行コードを取り除いておく。
※ この時点で `public-key.pem` は不要になる。

## 2. システム外で平文を暗号化する

システム外、つまり手動で暗号化したかったため、下記のサイトを利用しました。
<https://www.devglan.com/online-tools/rsa-encryption-decryption>

公開鍵はコピペして、Cipher Typeを適切に設定し、Encryptします。
Encrypted Outputがシステムに読ませて復号化すべき文字列です。

![2021-10-24_05h36_16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/936879/cc080633-c564-496a-f612-ae7ed10b8ba8.jpeg)

## 3. プログラム内で復号化する

```python
!pip install pycryptodome

import base64
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA

encoded_encrypted_pw = "上記サイトで暗号化したパスワード"

key = RSA.importKey(open("private-key_pkcs8.pem", "r").read())
decryptor = PKCS1_OAEP.new(key)

decoded_encrypted_pw = base64.b64decode(encoded_encrypted_pw)
decrypted_encoded_pw = decryptor.decrypt(decoded_encrypted_pw)
decrypted_pw = decrypted_encoded_pw.decode("utf-8")

print(f"type(pw): {type(decrypted_pw)}", f"pw: {decrypted_pw}", sep="\n")

# 出力
# type(pw): <class 'str'>
# pw: password
```

---

以上です。
relocation target type not definedが出たとき


Goのバージョン: 1.17.2

2021/10/24追記: 最新のmasterブランチで確認したところ、1.18では修正されて正常にビルドできそうです。

Go1.17を使用して、 `-gcflags=-G=3` を使ってジェネリクスの機能をonにしてビルドする際に
ジェネリクスな関数の中で、ジェネリクエスな引数を取るジェネリクスな関数を 匿名関数の中で呼ぶ場合に起きる（？）っぽい。


## どうするか

```go:問題のコード
package main

func a[T any](t T){}

func b[T any](t T) {
	func () {
		a[T](t)
	}()
}

func main() {
	b[int](0)
}
```


```go:修正したコード
package main

func a[T any](t T){}

func b[T any](t T) {
	a := a[T]
	func () {
		a(t)
	}()
}

func main() {
	b[int](0)
}

```

ジェネリクスな関数を呼ぶ前に、変数に型パラメータまで指定した関数を変数として確保して、匿名関数ではその変数を指定して呼び出せば良い。

## どういうエラーがでるのか

```go:問題のコード
package main

func a[T any](t T){}

func b[T any](t T) {
	func () {
		a[T](t)
	}()
}

func main() {
	b[int](0)
}
```

```bash
$ go build -gcflags=-G=3 .
main.b[int]: relocation target main.a[T₂] not defined
```

どういう理由でエラーが起きてるのかちょっとわかりませんが、多分 `main.a[T₂]` というシンボルを解決できないみたいなエラーだと思います。

ただ、なんでこのエラーが起きてるかはわかりませんでした。
Windowsのbatでフォルダやテキストファイルを生成する

ファイル整理するときに毎回右クリめんどくさいので残しておく
起動
キー入力[a\s]
aでフォルダ、bでテキストを生成する
名前は日付
重複があるとエラーが表示される

```
@echo off

set name=%~dp0%date:/=%_
set type=a
set /p type="a=フォルダ(デフォルト)、s=テキスト: "

if %type% == a (goto :folder)
if %type% == s (goto :text)

:folder

if exist %name% (
	echo msgbox "重複フォルダがあります",vbInformation,"情報">%~dp0msgbox.vbs
	%~dp0msgbox.vbs
	del %~dp0msgbox.vbs
	exit
)
mkdir %name%
exit

:text

if exist %name%.txt (
	echo msgbox "重複テキストがあります",vbInformation,"情報">%~dp0msgbox.vbs
	%~dp0msgbox.vbs
	del %~dp0msgbox.vbs
	exit
)
copy nul %name%.txt
exit
```


もしエクセルとかを追加したいときは同じフォルダに空のファイルを作っておく
そしてdとかにキーを割り振ってからこんな感じで行けると思う(拡張子は忘れずに)
`copy %~dp0空のファイル %~dp0好きな名前`
Qiitaを編集するときのやつ


すぐ忘れるので残しておく
##見出し
\#を1~6個つなげて作る
#一つ
##二つ
###三つ
####四つ
#####五つ
######六つ

##コード
空白行
\`\`\` .拡張子でコードの見出し(省略可)
好きなコード
\`\`\`

```tst.cs
    halo
```

##文字装飾とか
####太文字
\*\*好きな文字\*\*
**太くなる**

####無効化
/を各記号の前に入れる
\\\`\\\`\\\`
\`\`\`

####強調
\`好きな文字\`
`協調`

####色変え
\<font color="Red">赤文字</font>
<font color="Red">赤文字</font>
スクラム開発の現場にJoinして失敗した俺が悪い話

ほぼノー知識でスクラム開発の現場に乗り込んで失敗した話を書き記します。
「なぜスクラムは上手くいかないのか」「スクラム開発のアンチパターン」などチームにフォーカスした記事はあれど、個人にフォーカスした失敗談が見当たらなかったので書こうと思いました。
## はじめに
大前提として、その現場が悪かったとかスクラム開発が悪いとかそういったネガティブキャンペーンをするつもりではありません。
ウォーターフォールと比較して、継続的にプロダクトを作って完成に近づけていくスクラムのメリットは十分理解しているつもりです。
その中で自分が「あ、無理かも」と感じてしまった理由を記して同じ立場に立ってしまった人の救いになれればいいなと思い記します。
## 概要
AWSを基盤とするインフラ開発の現場Joinし、スクラムメンバーとしてプロダクトを開発する役目を受けました。
結論から言うと2週間のスプリントでベロシティを上げることが出来ず周りに迷惑をかけ辞退してしまいました。
ざっくり原因は下記のようなことが考えられます。

・一つのタスクの時間見積をすることが出来ない。
・タスク完了に直接関係ない作業に時間をかけてしまった
・人に時間を管理されることが苦手

一つずつ見ていきます。

## 一つのタスクの時間見積をすることが出来ない
バックログから拾ったタスクを自らどれくらいで終わるかを決めてスプリントに含めるわけですが、これが出来ない。
性格なのか病気なのかわからないが、一つの事柄を集中してn時間で終わらせることが出来ないのであります。
気になったらパラレルであれもこれも手を出してしまい、パラレルで進めてしまうので、トータルで見れば早いかトントンなものの、
一つのタスクで見ると当然ほかのタスクのカットインが入ってしまい遅れて見えてしまうのであります。
新着メールやSlackの新しい通知やスレッドが気になってしまって集中できないのです。

## タスク完了に直接関係ない作業に時間をかけてしまった
上とも似ているのですが、タスクを進めていると「あれ、これやった方がいいな」とか「見てるドキュメントがあやしいな」という事態があるわけです。そこでそれに手を付けてしまって本来のタスクの目的から逸脱した作業を行ってしまう。その結果タスクが遅れてしまいます。
完全に自分が悪いのですが、カイゼンをした自己満足に駆られなかなか是正することが出来ず、POに叱られつらい思いをしました。

## 人に時間を管理されることが苦手
恐らくこれが一番直接的な原因かなと思ってるのですが、私は幼いころから人に時間やスケジュールを管理されるのが大嫌いでした。
親に夏休みの宿題の予定を立てなさい。と言われ立てた試がありません。ただ自分のタイミングでしっかり終わらせることはしてました。
今日はこれを何時から何時までやって、次はこれをやって。というのが出来ないです。
人にスケジュールを管理されるのも大嫌いで、あえてスケジュールと違う行動をしたこともありました。「人に」というのは自分も含まれます。
スクラムの現場において当然ながらこれは致命的であり、結果的にベロシティを上げることが出来ず、撤退することになりました。

## 終わりに
ただ愚痴と言い訳を並べたみたいになってしまいましたが、、、。発達障害や性格上など、どうしてもスクラム開発の現場に馴染めない人がいることもあるというのを多くの人に知ってもらえるとよいかなと思いました。
Swift NavigationController 戻るボタンイベント 検知 

## 環境  
- MacOS Big Sur 11.6
- Xcode 13.0
- Swift 5.5

シンプルにこれで。

```
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    guard let viewControllers = navigationController?.viewControllers, !viewControllers.contains(self) else { return }

    // 実行したい処理など
    NotificationCenter.default.removeObserver(self)
}
```
ワインのデータをスクレイピングする（Vivino編）

僕は今まで自分が飲んだワインの情報をメモしています。
そして、一時期に週2くらいで飲んでいたときに、そのメモを取るのがめんどくさくなったので、一気にワインの情報を集められるようにスクリプトを作成しました。

そして、このスクリプトで取得したデータをGoogle Spreadsheetに貼り付けて、以下のように管理しています。

![20211021_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265324/cc795536-99a4-6cca-c1ac-68610f0aea61.jpeg)

という冒頭で前回のvinica編もお届けしましたが、今回は、Vivinoというサイトで取得するスクリプトを紹介します。なぜ、Vivinoを使ったかというと、vinicaに取得したかったワインがなかったのです・・・。

このスクリプトは開発者ツールで実行します。
今回使ったワインのページは、[ここ](https://www.vivino.com/kanonkop-pinotage/w/56164?year=2018&price_id=26351523)です。

※　今回注意したいのが、Vivinoは一度ページの最下部までスクロールしないと、ページが全てレンダリングされないみたいなので、スクロールしてから開発者ツールで要素を選ぶ必要があります。

以下になります。


## ソース
~~~javascript
// code by utf-8
 
// Use Developer Tool（by ⌥⌘I）
var today = new Date();
 
var length_of_taste = 'width: 20%; left: '.length;
var color_of_wine = `${[...document.querySelectorAll('a')][11].innerText}`.replace('wine', 'Wine');
var list_of_target_parameter_of_wine;
// ワインの種類によって、取得するパラメータを変える。
switch (color_of_wine) {
    case "White Wine":
        list_of_target_parameter_of_wine = [0, null, 1, 2, null];
        break;
    case "Red Wine":
        list_of_target_parameter_of_wine = [0, 1, 2, 3, null];
        break;
}
// 各パラメータにスコアを付けて、その各スコアを結果用のリストに出す。
var list_of_result_parameter_of_wine = list_of_target_parameter_of_wine.map(target => {
    if (target == null) {return null;}
    // Vivino上のスコアを取得する。
    var score = Number(document.getElementsByClassName('indicatorBar__progress--3aXLX')[target].style['cssText'].substr(length_of_taste,2));
    switch (true) {
        case score > 70:
            return 5;
        case score > 50 && score <= 70:
            return 4;
        case score > 30 && score <= 50:
            return 3;
        case score > 10 && score <= 30:
            return 2;
        case score <= 10:
            return 1;
    }
})
 
var array = [`${[...document.querySelectorAll('span')][10].innerText} ${[...document.querySelectorAll('span')][11].innerText}`, // ワイン名
             `${today.getFullYear() + "/" +  String(Number(today.getMonth()) + 1)}/${today.getDate()}`, // 出会った日
             ``, // Instagramに上げた日
             ``, // 製造年
             color_of_wine, // 色
             ``,// 醸造方法
             ``,// 金額
             ``,// 入手場所
             ``,// 最寄り駅
             `${[...document.querySelectorAll('td')][14].innerText}`.replace(/\//g, '>'), // 原産地
             ``, // 原産国
             ``,// 感想
             ``,// ハッシュタグ
             `${[...document.querySelectorAll('td')][13].innerText}`.replace(/, /g, '、'), // 備考（葡萄とか）
             location.href, // 参考1
             ``,// 参考2
             ``,// Instagramに貼り付け
             ``,// vinicaフォルダに入れた
             list_of_result_parameter_of_wine[0], // ボリューム（軽い＜重い）
             list_of_result_parameter_of_wine[1], // タンニン（控えめ＜強い）
             list_of_result_parameter_of_wine[2], // 甘み（ドライ＜甘い）
             list_of_result_parameter_of_wine[3], // 酸味（まろやか＜シャープ）
             list_of_result_parameter_of_wine[4] // 果実味（スパイシー＜フルーティ）
            ];
var sep = "\t";
var data = ``;
array.forEach(function(value) {
    data += value;
    data += `${sep}`;
});
 
// This is it!
console.log(data);
~~~
rails開発環境からgmailでメール送信する方法

# おおまかな流れ :sunny:
1. gmailの２段階認証プロセスの設定とパスワード取得
2. credentials.yml.encの設定をコマンドラインにて行う
3. 環境設定(config/environments/development.rb)
4. メール作成(Action Mailerのマニュアルに従う)

# gmailの２段階認証プロセスの設定とパスワード取得 :hatching_chick:
![Screenshot 2021-10-23 at 18.57.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1502566/9431b3b2-da2c-eeee-91b3-29ab371d545e.png)

指示通り進め、16桁のパスワードを取得。

# credentials.yml.encの設定をコマンドラインにて行う :koala:
```
EDITOR="vi" bin/rails credentials:edit
```
こちらでエディターのvimを起動。
![Screenshot 2021-10-23 at 19.09.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1502566/14d39d89-63d4-2173-add6-831ce0cb1477.png)

こちらの上にgmailのアドレス、先ほど取得したパスワードを入力。
<書き始め>
i => insert
<書きおわり>
esc => escape key
:wq => write and quit

```
gmail:
    user_name: your.account@gmail.com
    password: abcdabcdabcdabcd
```
# 環境設定(config/environments/development.rb) :house:

```
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.perform_caching = true
  config.action_mailer.default_url_options = { host: 'localhost:3000' }
  config.action_mailer.perform_deliveries = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
  address:              'smtp.gmail.com',
  port:                 587,
  domain:               'gmail.com',
  user_name:            Rails.application.credentials.gmail[:user_name],
  password:             Rails.application.credentials.gmail[:password],
  authentication:       :login}
```

これらを追記。

# メール作成(Action Mailerのマニュアルに従う) :airplane:

公式マニュアルのAction Mailer Basicsに従う。

+ mailerをコマンドラインより作成。
+ app/mailersapplication_mailer.rb
+ app/mailers/user_mailer.rb
+ app/views/user_mailer下にてviewページhtml、textファイル作成。

#実際に届いたメール :envelope:
![Screenshot 2021-10-23 at 19.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1502566/1e72d9e9-c50a-7aef-d256-331d5070b730.png)


# 参照リンク :santa_tone1:

+ gmail
https://qiita.com/ozackiee/items/f260f34db4e5c887460d
+ credentials.yml
https://qiita.com/gyu_outputs/items/92c4a2a2f96edb10e298
+ Action Mailer
https://guides.rubyonrails.org/action_mailer_basics.html
Jest エラー対応記録 実行時「The error below may be caused by using the wrong test environment」が出た時の対応

## エラーメッセージ

next.js（ts, jestなどなど）環境構築中、試しにテストが実行されるかをテストしたところ以下のようなエラーが発生。

```
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/configuration#testenvironment-string.
Consider using the "jsdom" test environment.
```

### テストファイル

```tsx:__tests__/Home.test.tsx
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import Home from '../pages/index'

it('Should render title text', () => {
  render(<Home />)
  expect(screen.getByText('Next.js!')).toBeInTheDocument()
})
```

## 解決策

```tsx:__tests__/Home.test.tsx
/**
 * @jest-environment jsdom
 */

import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
import Home from '../pages/index'

it('Should render title text', () => {
  render(<Home />)
  expect(screen.getByText('Next.js!')).toBeInTheDocument()
})
```

## エラーの原因

Jest 27からデフォルトのテスト環境（testEnvironment）が、バージョン26まではjsdomでしたが、27からnodeに変わったそうです。

なぜか？

- nodeを用いたアプリ開発でテストする時も、デフォではjsdom環境で実行されてしまい、パフォーマンス上良くない
- しかもjsdom環境で実行されていることに気づけない

という問題がありデフォルトをnodeに変更したようです。

この変更で、DOM APIを使うフロントエンドのテストではエラーが発生するようになりました。

そうです。👇

```
Running tests in a JSDOM environment incurs a significant performance overhead. Because this was the default behavior of Jest unless otherwise configured up until now, users who are writing Node apps, for example, may not even know they are given an expensive DOM environment that they do not even need.
For this reason, we are changing the default test environment from "jsdom" to "node". If you are affected by this change because you use DOM APIs and do not have the test environment explicitly configured, you should be receiving an error when e.g. document is accessed, and you can configure "testEnvironment": "jsdom" or use per-file environment configuration to resolve this.
```

https://jestjs.io/ja/blog/2021/05/25/jest-27#features-coming-with-breaking-changes

## テストを実行してみる

無事通った :relaxed:

![スクリーンショット 2021-10-24 1.53.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14191/ade8c6cf-bd3e-d288-a145-9065dd16086e.png)

## ちなみに

別の方法もできます。設定ファイル`jest.config`に記載する方法です。

```ts
module.exports = {
  ...
  testEnvironment: 'jsdom',
  ...
};
```


これの便利なところは、最初のやり方は全ファイルに`jsdom`環境でテストすることを宣言（記載）しないといけないけど、これは、設定ファイルに書けば終わりです。

ただし、全Jestファイルに適用されるので、`DOM API`を使わないならJestファイル冒頭に`node`の指定をしてください。

```ts
/**
 * @jest-environment node
 */

...
```

https://developer.mozilla.org/ja/docs/Web/API/Document_Object_Model/Introduction

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103
アスペクト比を固定したまま画像のサイズをフレームに合わせる

以下の図のように画像のアスペクト比を固定したままフレームにフィットさせたいことがよくあります.
その時にフレームサイズとイメージサイズを入力,フィットさせるときのイメージサイズを出力とするアルゴリズムのメモです.

![Screenshot from 2021-10-24 02-08-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589906/04ab1b9a-1af3-e2f8-480b-72aa48b58103.png)

### Input

入力はフレームのサイズとイメージのサイズで以下の様になります.

frame_w, frame_h, image_w, image_h

### Output

出力は新しいイメージのサイズです.

new_image_w, new_image_h

### アルゴリズム

フィットする画像サイズを取得する,JavaScriptの関数は以下のようになります.

```javascript:fit_image.js
function fit_image(frame_w, frame_h, image_w, image_h) {

  var new_image_w, new_image_h;

  var as_f = frame_w / frame_h;
  var as_i = image_w / image_h;

  if (as_f >= as_i)
  {
    new_image_h = frame_h;
    new_image_w = new_image_h * as_i;
  }
  else
  {
    new_image_w = frame_w;
    new_image_h = frame_w * (1 / as_i);
  }

  return [new_image_w, new_image_h];
}
```
あなたの知っているJavaScriptはもう古い(書き方編 -その1- )

## 私の知っているJavaScriptはもう古い
下記の記事をかいたように、JavaScriptの時代の流れについていけなかった私、、、。
そこで今回は今の時代はどんなふうにJavaScriptをかけばいいの？っていうことを学んで行こうとおもいます！

https://qiita.com/k-tetsuhiro/items/fd50feeee323e08e8349


## 知っている書き方
10年前のJavaScriptしか知らなかった私、、、。もし同じような方がいればこういった書き方を過去していたと思います。

```example.js
// 名前を定義する
var name = "タロウ";

// 名前を返す
function get_name(){
    return name;
}
```

し　か　し　！！　
**もうこの書き方では通用しません**（反省）

そこで、最近(ES2015以降)では下記のような記述をしていいきます。
※ES2015ってなに？って方は[こちら](https://qiita.com/k-tetsuhiro/items/fd50feeee323e08e8349#-es2015es6%E3%81%A8%E3%81%84%E3%81%86%E8%A8%98%E6%B3%95%E3%81%8C%E3%81%82%E3%82%8B)を参考にしてみてください

## const, letを使う
昔は変数の宣言で`var name = "タロウ";`みたいに宣言していたと思います。
しかし、**いつでも上書きできてしまう、再宣言できてしまう**ので、意図しないところでバグがおこる、、、といったことがありました。

```example.js
// 名前を定義する
var name = "タロウ";

// 上書きできてしまう
name = "ハナコ";

// 再宣言できてしまう
var name = "ハナコ";
```
ES2015以降ではvarは使わず、**constとlet**を使います。


```let_example.js
// 名前を定義する
let name = "タロウ";

// 上書きはできる
name = "ハナコ";

// 再定義するとエラーになる
let name = "ハナコ"; //　エラー
```
letでは再定義のみエラーとなります


```const_example.js
// 名前を定義する
const name = "タロウ";

// 上書きするとエラーになる
name = "ハナコ"; // エラー

// 再定義するとエラーになる
const name = "ハナコ"; //　エラー
```
const=定数なので、書き換えがおこるとすべてエラーになります
※ただし、オブジェクトや配列は上書きしてもエラーになりません（ここでは詳しく触れない）



## 文字列の連結
昔は文字列の連結に`+` を用いてこんな書き方をしていたと思います。

```example.js
var name = "タロウ";
var age = 20;

//　私の名前はタロウです。年齢は20歳です。
var message = "私の名前は" + name + "です。年齢は" + age + "歳です。"
```

めちゃくちゃ見にくいし、エラーがよく起こった記憶があります。。。
ES2015以降では**こんな書き方**ができます。

```example.js
const name = "タロウ";
const age = 20;

//　私の名前はタロウです。年齢は20歳です。
const message = `私の名前は${name}です。年齢は${age}歳です。`
```
スッキリして読みやすい!!


## アロー演算子
昔は関数の定義で`function`を使って以下のような書き方をしていました。

```example.js
var number = 2;

// 倍返しだ！！
function double(number) {
    return number * 2;
};

double(number); // ４が返ってくる
```

またこんな書き方でもできたとおもいます。

```example.js
var number = 2;

// 倍返しだ！！
var double = function (number) {
    return number * 2;
};

double(number); // ４が返ってくる
```

ES2015以降では**アロー関数**を使います。
アロー関数ではfunctionを使わずに下記のように定義します。

```example.js
// 倍返しだ！！
const double = (number) => {
    return number * 2;
}
```
functionがなくなり、代わりにアロー（矢印）のように`=>`を使います。
`=>`がでてきたら関数だ！って覚えておくと良さそうです。

アロー演算子は他にもこんなふうに書くこともあります。

```example.js
// 基本の形
const double = (number) => {
    return number * 2;
}

// 引数のかっこを省略できる
const double = number => {
    return number * 2;
}

// ただし、引数が二個ある場合は必ずカッコで囲む
const add_numbers = number_1, number_2 => {
    return number_1 + number_2 ; // これはエラーなる
}

const add_numbers = (number_1, number_2) => {
    return number_1 + number_2 ; // これは大丈夫
}

// 一行で書く場合は波括弧を省略できる（ワンライナーなのでreturnは不要)
const double = (number) => number * 2;

// （）を使ってワンライナーのようにまとめる
const person = (name, age) => (
  {
      name: name,
      age: age,
  }
)
person("タロウ",20) // ｛name: "タロウ", age: 20｝ が返ってくる
```


## まとめ
簡単なところをまとめてみましたが、すでに**けっこう昔の書き方**と違う、、、！と思ったのは私だけではないはず。
新しい書き方はすぐにはしっくりこないと思いますが、コード量を書いていけばこっちのほうが便利そうだなーという印象でした！
Reactをガンガン書いて、追いつかなければ、、、！
次回その２では、もう少し新しい書き方があるので、そこに触れたいと思います。

https://qiita.com/k-tetsuhiro/items/4e9e299f4f18f87ff8dc
HerokuにSpring Bootアプリをデプロイするときに出たエラーメッセージと解決した方法

Herokuに初めてSpring Bootアプリをデプロイしたときに出たエラーメッセージと、どうしたら解決したかをまとめておきます。一般論については[公式サイト](https://devcenter.heroku.com/ja/articles/deploying-spring-boot-apps-to-heroku)や多くの優れた記事が既にあるので、この記事では私がハマったときに表示されたエラーメッセージ等と、それをどう解決したかを一般化せずそのまま書いていきます。同じ状況にある人の参考になれば幸いです。
#筆者の環境
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
javac 11.0.2
Apache Maven 3.8.2
#Exception in thread "main" java.lang.UnsupportedClassVersionError: xxx has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
##解決方法：`system.properties`に`java.runtime.version=11`を記入する
コンパイルした環境と実行環境のバージョンが合っていないときに出るエラーのようです。
[公式サイト](https://devcenter.heroku.com/ja/articles/java-support#supported-java-versions)によると
>Heroku は現在、デフォルトで OpenJDK 8 を使用してアプリケーションを実行しています。

とのこと。自分の環境は上記の通りバージョン11なので、これによりバージョンが合わない旨のエラーが出ているようです。公式サイトの記述に従い、自分の環境に合わせて、`system.properties`に

```
java.runtime.version=11
```

を記入することで解決しました。
#Error: Unable to access jarfile target/xxx-0.0.1-SNAPSHOT.jar
##解決方法：`git add -f` オプションで`target/xxx-0.0.1-SNAPSHOT.jar`をコミット対象に含める
下記の記事が大変参考になりました。
https://qiita.com/hiroki1994/items/325fdf7e5943a563867f

`Procfile`内で指定されているjarファイルが、`.gitignore`ファイル上でコミット対象に含まないようになっていたらしく、これにより、デプロイしてもjarファイルにアクセスできないというエラーになるようです。

```
git add -f target/xxx-0.0.1-SNAPSHOT.jar
```

により、実行したいjarファイルを強制的にコミット対象に含めることで解決しました。
#Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 90 seconds of launch
##解決方法：`Procfile`に`--server.port=$PORT`を記入する
以下のページが参考になりました。
https://devcenter.heroku.com/ja/articles/setting-the-http-port-for-java-applications#spring-boot
https://su-kun1899.hatenablog.com/entry/2017/07/23/230000

herokuでは起動ポートが動的に変わるようなので、Spring Boot側のポート番号もherokuの環境変数で書き換える必要があるようです。
`Procfile`に`--server.port=$PORT`を追記して

```
web: java -jar target/xxx-0.0.1-SNAPSHOT.jar --server.port=$PORT
```

とすることで解決しました。
LinuxにもAnacondaのショートカットがほしい

# はじめに
LinuxにAnacondaを入れた。普段はactiveにしていないため、``conda activate``と``anaconda-navigator``の立ち上げの2つを毎回打ち込むことに嫌気が差しためんどくさがりによる雑な備忘録



# 作り方
1. 起動シェルの作成
自分は適当にanaconda.shとしてhome下に作成した

```bash
#!/bin/bash
source ~/.bashrc
# anacondaをインストールした場所によって変更（自分は/opt下にインストールしたため）
source /opt/anaconda3/etc/profile.d/conda.sh
cd ~
cd labo-py #自分の作業ディレクトリを指定
conda activate
anaconda-navigator
```
2. メニューに追加
自分はLinux mint 20.2なので標準でメイン・メニューというものが入っており、自由にアプリとしてコマンドを登録できる。
![スクリーンショット 2021-10-24 01-18-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/995699/4b425326-62f1-956a-2fc3-29cfbca345fe.png)

これで毎回押すだけヽ(=´▽`=)ﾉ
kotlin Module was compiled with an incompatible version of Kotlin. のエラーの場合


以下のエラーに遭遇した場合の解決方法になります。
`android/build.gradle`ファイルの `ext.kotlin_version`のバージョンがあっていないようなので、kotlin_versionを1.1.15に変更します。

```
 Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.5.1, expected version is 1.1.15.
```

```
buildscript {
    ext.kotlin_version = '1.1.15'
```
グラフデータベースneo4jでWEBスクレイピングをしてみた。


https://ten-hope.com/93/

- 読みとるHTMLページについて

ベルギーのBeerGraphをWikipediaのHTMLページから直接読み込む方法がブログにあったので試してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684662/95886430-cb8c-2cd9-6aed-16c654f55b28.png)



neo4jのpluginであるapoc.load.html機能を使って、中間ステップを経ずにHTMLページから直接データを自動的にインポートします。

APOCではHTMLページから直接読み込むことができます。
APOCは、Cypher言語から呼び出されるユーティリティー関数とプロシージャの非常に柔軟で拡張可能な実用的なセットです。
apoc.load.htmlというプロシージャを使うと、インターネット上の特定のHTMLページを呼び出し、その内容を読み取って、
後続のcypher文でその内容を処理することができます。

このプロシージャの構文です。

apoc.load.html(url :: STRING?, query = {} :: MAP?, config = {} :: MAP?) :: (value :: MAP?)

実際にはURL、クエリのセット、設定パラメータ（もしあれば）が必要になります。

- データベースの作成

まずNeo4jにデータベースを作成します。

create database beergraph;

次に、新しいCypher構文を使用して、データベースのインデックスを設定します。

CREATE INDEX beerbrand_name FOR (bb:BeerBrand) ON (bb.name);
CREATE INDEX brewery_name FOR (br:Brewery) ON (br.name);
CREATE INDEX beertype_name FOR (bt:BeerType) ON (bt.name);
CREATE INDEX alcoholpercentage_value FOR (ap:AlcoholPercentage) ON (ap.value);
 
これができたら、次のクエリを使ってWikipediaのページからデータベースに直接インポートすることができます。

WITH "https://nl.wikipedia.org/wiki/Lijst_van_Belgische_bieren" as url
    CALL apoc.load.html(url, {
        brand: "table.wikitable tbody tr td:eq(0)", 
        beertype: "table.wikitable tbody tr td:eq(1)",
        alcoholpercentage: "table.wikitable tbody tr td:eq(2)",
        brewery: "table.wikitable tbody tr td:eq(3)",
        timeframe: "table.wikitable tbody tr td:eq(4)"
        }) yield value
WITH value, size(value.brand) as rangeup
UNWIND range(0,rangeup) as i
    WITH value.brand[i].text as BeerBrand, value.brewery[i].text as Brewery, value.alcoholpercentage[i].text as AlcoholPercentage, value.beertype[i].text as BeerType, value.timeframe[i].text as Timeframe
        MERGE (bt:BeerType {name: coalesce(BeerType,"Unknown")})
        MERGE (bb:BeerBrand {name: coalesce(BeerBrand,"Unknown")})
        SET bb.Timeframe = coalesce(Timeframe,"Unknown")
        MERGE (br:Brewery {name: coalesce(Brewery,"Unknown")})
        MERGE (ap:AlcoholPercentage {value: coalesce(AlcoholPercentage,"Unknown")})
        MERGE (bb)-[:HAS_ALCOHOLPERCENTAGE]->(ap)
        MERGE (bb)-[:IS_A]->(bt)
        MERGE (bb)<-[:BREWS]-(br); 

3,000個のノードと6,000個の関係をインポートができました。

ベルギーのビアグラフです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684662/2673bf28-8811-e408-9557-36dd5bf27c1b.png)


ベルギーはビールの数が多いのですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684662/05a0ae2a-a624-d58c-a523-73f4534099a4.png)