OmniAICreator/Qiita-1.07M huggingface dataset:
LastSubmitとLast(もしくはLastN)の使いどころ

何か月かぶりにキャンバスアプリをいじっていたらすっかり忘れておりましたので覚書です。
今回はLastSubmitとLast(/LastN)それぞれの使いどころについてです。

![スライド1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/8abb8562-52f4-c962-2fd3-780ed178e5fb.png)

今回はとあるアプリの顧客情報の登録を行う画面と登録確認ダイアログを例として挙げます。
まず自由にUIをデザインした登録フォームとDataverseで作成した登録フォーム画面を比較しましょう。

# LastSubmitの使い方について
## 自前で作成したフォームに使えないLastSubmit
自前で用意したフォームでレコードを作成した場合、 ラベルに`テーブル名.LastSubmit.列名.Text`を記述しても最後に登録したデータは表示されません。

**ちゃんと公式ドキュメントは読もう**
ちなみに公式ドキュメントにLastSubmitプロパティ使用時の注意点がしっかりと書いてありました…

https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/controls/control-form-detail
>LastSubmit – 任意のサーバーが生成したフィールドを含む、最後に正常に送信されたレコード。
>- このプロパティは **Edit Form コントロールのみに適用**されます。
>- (中略)
>- このプロパティの値は **OnSuccess 式で**利用できます。

なのでLastSubmit関数を使用すると以下のような感じになります。

![スライド2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/4bb43158-3afd-d710-9944-35aa63a151ec.png)

上記例では登録データの確認にディスプレイフォームコントロールを使用していますが、 ```Edit Form コントロール.LastSubmit.表示させたい列名```をTextコントロールのTextプロパティにセットすれば確認ダイアログを自由なデザインで表現することもできます。
![LastSubmit関数.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/b5124f96-6c5e-371f-5901-6327a5f98606.png)

:::note info
**NewForm関数の実行タイミングに注意しよう**

<font color="red">NewForm関数を実行するとLastSubmitに保存されたデータは空に</font>なります。
フォームコントロールを使用してキャンバスアプリを作成するときSubmitForm関数を実行した後はNewForm関数を実行する必要がありますが、LastSubmitを使用したい場合は**どこでNewForm関数を実行するべきか考慮する必要があります。**

上記の「顧客登録_2」画面では確認ダイアログを閉じて登録確認画面に戻るOKボタンのOnSelectイベントでNewForm関数を実行しています。
:::

# Last関数を使用する
登録フォームを自由なUIデザインにした上でレコード登録確認ダイアログが欲しい場合は[Last関数](https://docs.microsoft.com/ja-jp/power-apps/maker/canvas-apps/functions/function-first-last)とSortByColumnsを組み合わせると似たような挙動を再現することができます。

```Last(SortByColumns(テーブル名, "createdon(作成日)")).表示させたい列名```

ただ、これだとアプリを**同一のタイミングで複数人が使用していた場合**別の人が登録したデータが表示される可能性はあります。
テーブルのレコードの作成者列を使用して、テーブルをログイン中ユーザー名で絞る([Filter関数](https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-filter-lookup))ように設定すると良いでしょう。
```
Last(SortByColumns(Filter(テーブル名, 作成者.ユーザー名 = User().Email), "createdon")).表示させたい列名
```

# 動作の比較
![スライド5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/afb62992-f463-d71e-1f51-80f64eb1c415.png)
![スライド6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2218676/5a9bd5f9-dd64-337f-936d-059528b78f1a.png)

# 2022/07/07 追記

https://ippu-biz.com/development/powerplatform/powerapps/index_access/

Last絡みで配列のインデックスの仕方にアップデートがあったみたいですね。


# 参考
https://qiita.com/yamad365/items/e572e471a3dd2ca4d69c

https://www.spguides.com/powerapps-lastsubmit/
Music Transformerで自動作曲してみる

## はじめに
自動作曲関連のAI技術を調べていたところ、「Music Transformer」が面白そうだったので実験してみました。

基本的には公開されているColabのコードを動かすだけで試せますが、一部注意点もあったのでメモしておきます。

## Music Transformerとは
自然言語処理分野を中心にもはや定番となっているTransformerを音楽生成に適用したネットワーク。2018年にGoogleが発表したもの。

![music_transformer_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2542055/024dd7bd-64ca-c33d-b8dc-e9570bb37500.png)
([Music Transformer: Generating Music with Long-Term Structure](https://magenta.tensorflow.org/music-transformer))

これ以前にもLSTMなどを使った自動作曲モデルはありましたが、音楽において重要である時間的な繰り返しを上手く扱えないという課題がありました。

それに対し、Music Transformerでは長い時間スケールにわたって繰り返し構造が生み出され、より高度な音楽生成が可能となっています。

Music Transformerについて詳細は[こちらの解説記事](https://qiita.com/NayuSato/items/e193ea8b8fc5db560431)がとてもわかりやすいです。


## 実験

以下のColabページでMusic Transformerを試すことができます。
[Generating Piano Music with Transformer](
https://colab.research.google.com/notebooks/magenta/piano_transformer/piano_transformer.ipynb)

機能として、以下のような事が可能です。
- ランダムに音楽生成 
- 指定した曲に続く音楽を予測して生成
- 指定したメロディに対して伴奏を生成

### やってみたこと
今回はシンプルな入力として、童謡『かえるの合唱』の冒頭15秒ほどを与えて続きの生成を試してみました。

Colabの「Choose Priming Sequence」にて「Upload your own」を選択し、作成しておいた『かえるの合唱』のmidiファイルを指定しました。
この時、max_primer_secondsにより冒頭の何秒を入力として使用するかを指定できます。

そして、「Generate Continuation」を実行し続きの生成を行いました。

※注意点として、モデルのセットアップ時点でnumpy関連のエラーが出る場合があります。
自分の場合、numpyのバージョンを変更(1.21.5 --> 1.19.5)することで解決しました。

### 結果
結果は以下のような感じになりました。可視化ツールとしてシンセシアを使用しています。
最初の約15秒(緑色)が入力として与えた部分で、それ以降の青色がMusic Transformerによる生成結果です。

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

入力として与えた曲の内容（スケールや3度の進行）を再利用しつつ、微妙にアレンジを加えてくれているのがわかります。

[他にも何曲か（リスト）](https://www.youtube.com/playlist?list=PL95LRlfaHQbPJkPXMRNDvnPVwbBgDfzKF) で試してみましたが、曲によってはアレンジがかなり独創的な物もあり面白かったです。


## おわり
自動作曲モデルのMusic Transformerで遊んでみました。

他にもTransformerを使った音楽生成ネットワークとして「[MuseNet](https://openai.com/blog/musenet/)」というものもあるようなので、
そちらも今度試してみたいと思います。
深さ優先探索

再帰により、深さを表現する

木以外でも使える([例](https://atcoder.jp/contests/joi2009yo/tasks/joi2009yo_d))

```c++
int N, Q;
Graph E(201010); //つながり
int val[201010]; //総合得点

void dfs(int cu, int pa = -1){ //cuを訪問済み(-1)に設定 

    fore(to, E[cu]) //cuの最小全域木の頂点
        if (pa != to) { //訪問済みでないなら
        val[to] += val[cu];
        dfs(to, cu);
    }
}

int main() {
    cin >> N >> Q;//頂点数、辺数
    rep(i, 0, N - 1) {
        int a, b; cin >> a >> b; //繋がり
        a--; b--;
        E[a].push_back(b);
        E[b].push_back(a);
    }

    rep(q, 0, Q) {
        int p, x; cin >> p >> x; //pの最小全域木、x点
        p--;
        val[p] += x;
    }

    dfs(0);

    rep(i, 0, N) {
        if (i) printf(" ");
        printf("%d", val[i]);//表示
    }
    printf("\n");
}
```
Ubuntu SoftEtherVPNクライアント

#### 準備
SoftEtherダウンロードセンターに行き、ホームディレクトリに保存
https://www.softether-download.com/ja.aspx

```
$ cd
$ tar xf softether-vpnclient-v4.38-9760-rtm-2021.08.17-linux-x64-64bit.tar.gz
$ cd vpnclient/
$ make
$ cd
$ find vpnclient -type f -exec chmod 644 "{}" \;
$ find vpnclient -type d -exec chmod 755 "{}" \;
$ chmod 700 vpnclient/vpn*
$ sudo chown -R root.root vpnclient # これは実行しないのが何かと便利かも
$ sudo mv vpnclient /usr/local
```

#### 設定
```
$ sudo /usr/local/vpnclient/vpnclient start
$ sudo /usr/local/vpnclient/vpncmd
vpncmd コマンド - SoftEther VPN コマンドライン管理ユーティリティ
SoftEther VPN コマンドライン管理ユーティリティ (vpncmd コマンド)
Version 4.38 Build 9760   (Japanese)
Compiled 2021/08/17 22:32:49 by buildsan at crosswin
Copyright (c) SoftEther VPN Project. All Rights Reserved.

vpncmd プログラムを使って以下のことができます。

1. VPN Server または VPN Bridge の管理
2. VPN Client の管理
3. VPN Tools コマンドの使用 (証明書作成や通信速度測定)

1 - 3 を選択: 2

接続先の VPN Client が動作しているコンピュータの IP アドレスまたはホスト名を指定してください。
何も入力せずに Enter を押すと、localhost (このコンピュータ) に接続します。
接続先のホスト名または IP アドレス:$(ENTER入力)

VPN Client "localhost" に接続しました。

VPN Client>NicCreate
NicCreate コマンド - 新規仮想 LAN カードの作成
仮想 LAN カードの名前: vpn1

コマンドは正常に終了しました。

VPN Client>AccountCreate
AccountCreate コマンド - 新しい接続設定の作成
接続設定の名前: $(接続設定名)

接続先 VPN Server のホスト名とポート番号: $(ホスト名):$(ポート番号)

接続先仮想 HUB 名: $(仮想HUB名)

接続するユーザー名: $(ユーザ名)

使用する仮想 LAN カード名: vpn1

コマンドは正常に終了しました。

VPN Client>AccountPasswordSet
AccountPasswordSet コマンド - 接続設定のユーザー認証の種類をパスワード認証に設定
接続設定の名前: $(接続設定名)

パスワードを入力してください。キャンセルするには Ctrl+D キーを押してください。

パスワード: $(パスワード)
確認入力  : $(パスワード)


standard または radius の指定: standard

コマンドは正常に終了しました。

VPN Client>AccountStartupSet $(接続設定名)
AccountStartupSet コマンド - 接続設定をスタートアップ接続に設定
コマンドは正常に終了しました。

VPN Client>AccountConnect $(接続設定名)
AccountConnect コマンド - 接続設定を使用して VPN Server へ接続を開始
コマンドは正常に終了しました。

VPN Client>exit
```

#### 起動
```
# インターフェイス名はvpn_$(仮想LANカード名)になる(ようだ)。
$ sudo /usr/local/vpnclient/vpnclient start
$ sudo dhclient vpn_vpn1
$ ifconfig vpn_vpn1
vpn_vpn1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.30.203  netmask 255.255.255.0  broadcast 192.168.30.255
        inet6 fe80::5c35:6ff:fe2c:bf0c  prefixlen 64  scopeid 0x20<link>
        ether 5e:35:06:2c:bf:0c  txqueuelen 1000  (イーサネット)
        RX packets 6176  bytes 557371 (557.3 KB)
        RX errors 0  dropped 4  overruns 0  frame 0
        TX packets 2685034  bytes 1883323317 (1.8 GB)
        TX errors 0  dropped 434851 overruns 0  carrier 0  collisions 0
# VPNが確立しない時は以下が有効かも。
$ sudo ifconfig vpn_vpn1 down
$ sudo ifconfig vpn_vpn1 up
```

#### 参照リンク

ダウンロード make 設定 起動

https://narwhale.net/post-239/

ダウンロード make サービス登録

https://qiita.com/atomyah/items/0edda0c93a4147381aa5

リンク切れか、大丈夫か

https://www.gadgets-today.net/?p=5792

apt-get install softether-vpnclient

https://nij.hatenablog.com/entry/2021/07/27/ubuntu_20.04%E3%81%ABsoftether_VPN%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

VPNサーバー

https://tek2tech.com/softether-vpn-server-on-ubuntu-2004/
クラッシュした際にすぐログが消えてしまう場合の対処法

# はじめに
アプリを作っていると必ず一度はクラッシュさせてしまいますよね。
そんな時にログを見ようとして、消えてしまう、、、少しイラッとさせられてしまう事象かと思いますが、そんな時の対処法を忘れないよう残しておこうと思います。

# 対処法
対処法としては非常に簡単です。
logcatタブを開いて右上にある以下のスピナーを**Show only selected application**から**No Filters**に変更することで、消えてしまったログを確認することが可能です。

![スクリーンショット 2022-03-01 18.29.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354522/986401e5-b254-e4a7-4bca-a4436a6cddf7.png)

ただし注意点として、名前の通りフィルター無しの状態でログが流れてくるので、ログの量が膨大になります。
クラッシュした際に確認する場合のみ使用することをお勧めします。

# さいごに
この設定知っている方も多いかもしれませんが、知らないととんでもなく辛いことになりますよね。
消える前に頑張ってコピー、、、なんて事にならないよう頭の片隅にでも覚えておくと良いかもしれません。
Eloquent Builderを使ってDB接続先を動的に切り替える方法

# Eloquent Builderを使ってDB接続先を動的に切り替える方法

```
$c = new Customer();
$c->setConnection('db_replica')->newModelQuery()->where('id', 1)->get();
// もしくは
$c->setConnection('db_replica')->where('id', 1)->get();

// これはNG (connection情報が失われて、default に繋がれてしまう)
$c->setConnection('db_replica')->query()->where('id', 1)->get();
```

`Illuminate\Database\Query\Builder` はドキュメントにあってその通りだけど、モデル使う場合が情報量少なくてちょっとハマったので共有でした
```
\DB::connection('db_replica')->table('customers')->where('id', 1)->get();
```
Automation Anywhere V11⇒A360移行時のグローバル値（その２）

# はじめに
「 https://qiita.com/etoootak/items/4cf2ec16317c4f7c562d 」の続きです。
ここでは、AAApplicationPathとAABasePathを書いてみます。

変換後のBotでグローバル値AAApplicationPathとAABasePathが現れた場合に、どのような値をグローバル値として登録すればよいのか、という話です。

https://docs.automationanywhere.com/bundle/enterprise-v2019/page/enterprise-cloud/topics/migration/global-values-for-migration.html

# AAApplicationPath
Automation Anywhere V11の Tools / Options / Advanced Settings にあるApplication Pathの値に相当します。Automation Anywhere V11のシステム変数AAApplicationPathと同じです。
ただ、V11のAAApplicationPathをBot呼び出しに使っていた場合は変換後のAutomation 360では現れません。Open Fileなどで使っていた場合はAutomation 360のグローバル値として現れるようです。

![aaaplicationpath.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632535/ac701fc1-f616-87e1-e576-24d30b161080.jpeg)

また、このグローバル値に関しては詳しい説明ページがあります。
https://docs.automationanywhere.com/ja-JP/bundle/enterprise-v2019/page/enterprise-cloud/topics/migration/how-aaapplicationpath-is-migrated.html


# AABasePath
こちらは、V11の Tools / Options / Runtime Settings の「"Start In" Path of Task」に相当します。
デフォルトではデスクトップです。
例えば、V11の Log to Fileで testlog.txt へ出力するとデスクトップにtestlog.txtが作られますが、このBotをA360移行するとAABasePathが現れます。
![aabasepath.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632535/644cd656-59e8-d79b-87e5-da48e743dd73.jpeg)

乱暴にいうと相対パスを使っているV11のBotをA360移行するとAABasePathが現れるので、グローバル値AABasePathの登録が必要です。
でも、普通は相対パス使わないですよね。

# まとめ
デフォルトで考えるなら、以下をグローバル値に登録すれば問題ないかと思います。ただ、Automation 360という新しい環境を使うのですから、ファイルの置き場などは見直した方がよいでしょう。


```
AAApplicationPath
C:\Users\ユーザ名\Documents\Automation Anywhere Files
```


```
AABasePath
C:\Users\ユーザ名\Desktop
```
二分探索

計算量 
ソート n logn
1回 logn
```c++
vector<int> a = {1, 14, 32, 51, 51, 51, 243, 419, 750, 910};

// 目的の値 key の index を返すようにする (ない場合は -1)
int binary_search(int key) {
    int left = 0, right = (int)a.size() - 1; // 配列 a の左端と右端
    while (right >= left) {
        int mid = left + (right - left) / 2; // 区間の真ん中
        if (a[mid] == key) return mid;
        else if (a[mid] > key) right = mid - 1;
        else if (a[mid] < key) left = mid + 1;
    }
    return -1;
}

int main() {
    cout << binary_search(51) << endl;
```
エラーに遭遇してから誰かに聞くまでに出来ること

※ 新人プログラマ、エンジニア向けの内容となっています。
## みんな悩むこと
エラーが発生した際にどのように対処していますか？
- 何か悪いことして怒られているので反省している
- 意味のわからない単語が並んでいて原因がよくわからない
- 参考通りに書いたのにエラーになったのは、きっと運が悪かっただけなので、もう一度実行する
- 赤い画面出た、スクショして分かる人に聞く

少し極端な内容ですが、完全に笑い事にできない内容も含まれているのではないでしょうか？
エラーとの付き合いは開発を続けていく上で、ずっと続いていくものだと思います。
今回は、まだ付き合い方がよくわかってない人向けに、私の考える初歩的な内容をお伝えしたいと思います。

## エラーに遭遇してから出来ること
#### エラー文を読む
まずはエラー文を読みましょう。
ただ赤い画面とか、読まずに「たぶんこんなこと」って推測するのは危ない。

#### エラー文を直訳してみる
エラーを理解するためのファーストステップです。
未知の言語から知っている言語に近づきます。例えば…

「undefined method 'name' for nil:NilClass」
　　→「nil：NilClassの未定義のメソッド 'name '」
　　　→ NilClassの中に'name'メソッドが定義されてない

#### エラー文をググってみる
1. **環境情報をキーワードとして加える**
　「undefined method 'name' for nil:NilClass」　
　　ではなく
　「**Rails** undefined method 'name' for nil:NilClass」　
　「**Rails Install** undefined method 'name' for nil:NilClass」　など

1. **一般的なワードに分解して検索する**
　「undefined method **'create_qiita'** for nil:NilClass」　
　　**'create_qiita'** は、自分のプログラム内で定義したメソッドなので検索しても引っかからない
　「undefined method nil NilClass」　
　　のような一般的っぽいワードだけにして検索してみる

#### 直近の作業を整理して推測してみる
多くの場合は、直近の変更内容からエラーが発生しているので、作業を振り返ってみましょう。直接コードを編集してない場合でも、コマンドの実行やIDEの設定の変更などで、意図せずに変更してしまっている場合があります。
#### エラーが発生してなかったバージョンまで戻してみる
gitやIDEのツールを使ってエラーが発生してないバージョンに戻せるなら、戻してエラーが発生するまでの変更を1つずつ再現してみましょう。
## 最後に
ここまでやって分からなければ、**誰かに聞く！**

　といった感じで私が思う「エラーに遭遇してから誰かに聞くまでに出来ること」をご紹介しました。ですが、「今回ご紹介したことを全てやってから誰かに聞いてくださいね」というわけではありません。
　特に職業プログラマ、エンジニアとして開発されている方は、時間をかけて自力で解決した方がいいのか、スピードを優先して助力を得た方がいいのか、その判断も非常に大事なスキルになってくると思いますので、その時々で考えてみてください。
BLF のダンプを取ってみた

こんにちは、wattak777 です。
## いきさつ

ひょんなことから BLF ファイルを解析することになりまして。
ところが手元に CANalizer も無く、再生も何かのファイルへのエクスポートもできず。
さて、どげんしたら…と、途方に暮れていたら、

[【完全版】pythonでblfファイルのCANデータをcsv化する【詳細解説付き】](https://syachiku-python.com/python%E3%81%A7%E3%80%81blf%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AEcan%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92csv%E5%8C%96%E3%81%99%E3%82%8B/)

というページを発見。

しかし、サンプルを見るとどうやら dbc というデータベースファイルが必要であり、しかも CAN 通信の特定の MSG ID しか取ってこず、本来欲しかったダンプでは残念ながら無く。

## 作ってみた

…と、いうことでみんな大好き Python で作ってみました。
インストールは上述の記事にもありますが、以下の 2 つのコマンドにて行います。

```
$ pip install cantools
$ pip install python-can
```

お試し実装したソースが下記。

```python
import cantools
import can
import sys

filename=sys.argv[1]
print(filename)

blfdata = can.io.blf.BLFReader(filename)
blfdata = list(blfdata)

outputfile = open('output.txt', 'w')
for msg in blfdata:
	msg = str(msg)
	column = msg.strip().split()
	outputfile.write(column)
	outputfile.write('\n')

outputfile.close()
```

出力させてみると、こんな感じ。[^1]
```
['Timestamp:', '1234567890.123456', 'ID:', '123', ...]
　　：
```
後はこのリファレンスを元に必要なデータを column 変数のインデックスを設定して改めて出し直して必要なダンプを取る、という形で行いました。

## タイムスタンプについて

ちなみにタイムスタンプの数字はマイクロ秒単位で出ているようですが、整数部分は UTC タイムゾーンにおける 1970 年 1 月 1 日 00:00 からの通算秒のようですね。

リアル時刻に戻すときは注意が必要ですが、再生機構を作りたい場合は 1 つ目のデータのタイムスタンプからの経過秒数だったり、それぞれのひとつ前のデータのラップタイム秒数を計算させて出力する方が良いかもしれません。

以上です。
## 参考文献
[【完全版】pythonでblfファイルのCANデータをcsv化する【詳細解説付き】](https://syachiku-python.com/python%E3%81%A7%E3%80%81blf%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AEcan%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92csv%E5%8C%96%E3%81%99%E3%82%8B/)


[^1]: 秒数、IDについてのみ記載しています。自分の手元にある BLF は「DLC」「Chennel」といったフィールドがありましたが、一般的かどうかわからず割愛いたしました。
SPSS Modeler ノードリファレンス 5-5 Cox

# 5-5 Coxノード[モデル作成タブ]
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/a49abced-c7b8-2156-61b7-dd0bb1f74ca2.png)

# 1.ノードの目的
最終的な状況が判定されていない打ち切りデータのフラグ型データを予測するCox比例ハザードモデルを作成します。Cox回帰とも呼ばれ、回帰分析の一種になります。

*打ち切りデータとは、たとえばメルマガ購読の解約・継続を調査したい場合、データ収集時点では一部の読者は購読を解約し、他の多くの読者は購読を継続状態で、購読継続中の読者はいつ購読を解約するか購読期間がわからないデータです。つまり、最終的な状況が判定されていないデータです。

# 2.解説動画(60秒)
<iframe width="560" height="315" src="https://www.youtube.com/embed/c9rzJOK78QE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 3.クイックスタート
製品に同梱されているサンプルストリームteleco_coxreg.strを開きます。このストリームのシナリオの１つは、ある通信会社の「顧客の解約までの期間」に注目し、解約に関係する要因を調査することです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/97f9fdd8-2fd1-9b71-a864-420ce81d44be.png)

使用するデータには、顧客の属性情報(年齢、婚姻状況、性別、世帯人数など)や商品の利用状況(無料通話、設備、長距離通話など)、そして、解約までの期間と解約状況があります。
ここでは「解約」フィールドを対象とし、それ以外は入力としています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/644e9b38-2d46-9bd9-1356-0223a29e65b5.png)

サンプルストリームに接続されている「解約」のCoxノードを参照します。
フィールドタブでは、[生存期間:]に、解約までの期間である「期間」フィールドを指定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/8c910eca-1200-e599-57d6-35f52adeb283.png)

モデルタブでは、[方法:]で変数選択の方法として「ステップワイズ」を選択しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/883b2658-2790-e8a7-bed4-8b17b063962b.png)

エキスパートタブでは、[モード:]は「エキスパート」で、出力のプロットでは、☑生存関数、☑ハザード関数を選択しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/36c053bb-2fa5-15b3-0133-3a219cc68966.png)


Coxノードを実行して、生成されたCoxモデルナゲットを参照します。
[方程式中の変数]テーブルの最後のステップのフィールドとExp(B)に注目します。Exp(B)は、ハザード比のことで、解約に対するリスクを表します。１を基準にして、上回れば解約リスクが上がる、下回れば解約リスクが下がることを意味します。
たとえば、コールカードの所有以外の条件はすべて同じで、コールカードを所有しない場合とする場合では解約のリスクが1.6倍になります。設備の利用以外の条件はすべて同じで、設備を利用しない場合とする場合では解約のリスクが0.15倍になります。
これらから、どの要因が解約に影響があるかが読み取れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/485db6ed-eb21-9a1f-5dc0-26a71c6d0cdf.png)

生存関数プロットを参照すると、契約の期間が40ヶ月を過ぎたころから累積の契約者率が下がっていく傾向が読み取れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/4861c166-6dd9-6891-2df0-74b140f925a2.png)


# 4.Tips
今回のように予測の対象が打ち切りデータの場合はCox回帰が使用できます。また、予測の対象が打ち切りデータでない場合はロジスティック回帰を使用します。

サンプルストリームには、モデルの評価、予測、予測固定客数の追跡、スコアリングの手順もありますので、5.参考情報のチュートリアルも合わせてご覧ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855260/d4d9e699-7454-90ff-7f15-7251b0391205.png)


# 5.参考情報
## チュートリアル
https://www.ibm.com/docs/ja/spss-modeler/18.3.0?topic=churn-building-suitable-model

## 利用データ
SPSS Modeler 同梱のtelco.sav

## ノードのヘルプ
https://www.ibm.com/docs/ja/spss-modeler/18.3.0?topic=models-cox-node

## SPSS Modelerノードのリファレンス 
https://qiita.com/416nishimaki/items/503037195081d3323eee
/usr/bin/ssh: No such file or directory



# 事象 : 多段階接続してSCPを実行したらエラーになった
- 環境 : Windows10 Pro バージョン21H1

```bash
$ scp ~/Desktop/file.sh targetserver:.
/usr/bin/ssh: No such file or directory
lost connection
```

## 原因 : scpコマンドが使うsshコマンドがないから
`/usr/bin/ssh`というパスなので、最初は踏み台サーバの問題かとも思ったが、コマンドの場所を確認しておかしいことに気が付いた。

1. どうやらscpコマンドは「/{GitBashのインストールディレクトリ}/usr/bin/」にあるGitBashのものを使用しているようだ
1. そのscpコマンドは同じ「/{GitBashのインストールディレクトリ}/usr/bin/」にあるはずのssh.exeを使おうとしている
1. しかし、「/{GitBashのインストールディレクトリ}/usr/bin/」にssh.exeはないので「そんなものはない」と言われている
    1. 本来はあるのかもしれないが、消してしまったのだろうか・・・

```bash
# sshコマンドはOpenSSHのものを使っている
$ which ssh
/c/WINDOWS/System32/OpenSSH/ssh

# scpコマンドは「/usr/bin/」にあるものを使っている
$ which scp
/usr/bin/scp

# Windowsなのに「/usr/bin/」？
$ cd /usr/bin/
# エクスプローラーで開いてみると「{GitBashのインストールディレクトリ}\usr\bin」のことだった
$ start .
# そして「/usr/bin/」にはssh.exeがない・・・
$ ls -la | grep ssh
-rwxr-xr-x 1 ponsuke 1049089  366436  1月 29 07:33 ssh-add.exe
-rwxr-xr-x 1 ponsuke 1049089  348015  1月 29 07:33 ssh-agent.exe
-rwxr-xr-x 1 ponsuke 1049089   12676  1月 29 07:33 ssh-copy-id
-rwxr-xr-x 1 ponsuke 1049089  888417  1月 29 07:33 sshd.exe
-rwxr-xr-x 1 ponsuke 1049089  495976  1月 29 07:33 ssh-keygen.exe
-rwxr-xr-x 1 ponsuke 1049089  455485  1月 29 07:33 ssh-keyscan.exe
-rwxr-xr-x 1 ponsuke 1049089   21618  1月 29 07:33 ssh-pageant.exe
```

### 対応 : OpenSSHのsshコマンドのシンボリックリンクを作る
ないものはないので、使っているOpenSSHのsshコマンドのシンボリックを「/usr/bin」につくってscpコマンドでも使ってもらうことにした。
参考 : [Windowsでシンボリックリンクを作る | DevelopersIO](https://dev.classmethod.jp/articles/make_windows_symbolic_link/)

1. コマンドプロンプトを**管理者権限**で起動する
1. シンボリックリンクを作る
1. もう一回scpする

```bash
# (コマンドプロンプトで実行)シンボリックリンクを作る
>mklink {GitBashのインストールディレクトリ}\usr\bin\ssh C:\Windows\System32\OpenSSH\ssh.exe
{GitBashのインストールディレクトリ}\usr\bin\ssh <<===>> C:\Windows\System32\OpenSSH\ssh.exe のシンボリック リンクが作成されました

# (いつものターミナルで実行)もう一回scpする
$ scp ~/Desktop/file.sh targetserver:.
Enter passphrase for key 'C:\Users\ponsuke/.ssh/fumidai.pem': 
Enter passphrase for key 'C:\Users\ponsuke/.ssh/id_rsa': 
file.sh
# めでたく転送できた
```
Lambda(Node.js)で別アカウントのECSサービス情報を取得する

# はじめに
Node.jsを使ってスイッチロールして別アカウントのECS情報を取得する際に実装方法が分からなかったので、備忘として残しておきます。
Lambda＋Node.jsの情報少なすぎひん...？

# 設定内容
## 権限設定
Lambdaを作成する前に以下の設定だけ先にやっておきます。
色々試しながらだったので、もしかしたら漏れがあるかもしれません。

* アクセス元のIAMロールにAssumeRole権限追加
* アクセス先のIAMロールにECSへのアクセス権限追加(ECSFullAccess)
* アクセス先のIAMロールの信頼関係にアクセス元のIAMロールを追加

## Lambda実装
初めてNode.js書くので不要な実装とか含まれているかもしれませんが、実際に実装したプログラムは以下です。
普段Pythonしか書かないのでcallbackとか全然頭に入ってこなかった。。

```
const AWS = require('aws-sdk');
const sts = new AWS.STS({apiVersion:'2011-06-15',region:'ap-northeast-1'});
const roleArn = "arn:aws:iam::123456789123:role/service-role/switch_role_name"
const roleSessionName = "test"

exports.handler = (event) => {
    var params_sts = {
        RoleArn: roleArn,
        RoleSessionName: roleSessionName
    };
    
    sts.assumeRole(params_sts, function(err, data) {
        if (err) {
            console.log(err, err.stack);
        } else {
            var accesskey = data.Credentials.AccessKeyId;
            var secretaccesskey = data.Credentials.SecretAccessKey;
            var SessionToken = data.Credentials.SessionToken
            
            const ecs = new AWS.ECS({apiVersion:'2014-11-13',region:'ap-northeast-1',accessKeyId: accesskey,secretAccessKey: secretaccesskey, sessionToken: SessionToken});
            
            var params = {
                services: [
                    "service_name"
                ],
                cluster: "cluster_name"
            };
            ecs.describeServices(params, function(err, data) {
                if (err) console.log(err, err.stack);
                else     console.log(data);
            });
        }
    });
};
```

## 実行結果
以下のような実行結果が取得されました。

```
{
  services: [
    {
      serviceArn: 'arn:aws:ecs:ap-northeast-1:123456789123:service/cluster_name/service_name',
      serviceName: 'service_name',
      clusterArn: 'arn:aws:ecs:ap-northeast-1:123456789123:cluster/cluster_name',
      loadBalancers: [],
      serviceRegistries: [],
      status: 'ACTIVE',
      desiredCount: 0,
      runningCount: 0,
      pendingCount: 0,
      launchType: 'FARGATE',
      platformVersion: 'LATEST',
      platformFamily: 'Linux',
      taskDefinition: 'arn:aws:ecs:ap-northeast-1:123456789123:task-definition/sample-task:1',
      deploymentConfiguration: [Object],
      deployments: [Array],
      roleArn: 'arn:aws:iam::123456789123:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS',
      events: [Array],
      createdAt: 2022-03-01T01:18:27.525Z,
      placementConstraints: [],
      placementStrategy: [],
      networkConfiguration: [Object],
      schedulingStrategy: 'REPLICA',
      createdBy: 'arn:aws:iam::123456789123:user/user1',
      enableECSManagedTags: true,
      propagateTags: 'NONE',
      enableExecuteCommand: false
    }
  ],
  failures: []
}
```

# おわりに
Node.jsのわからな過ぎてかなりハマってました。
あとはLambdaでSDKを利用する場合は、SDKのバージョンが2系になるのですが、Googleで調べると3系が多く出てきていて最初はその違いに気づくのに時間がかかりました。。
エラー確認

A+B を出力してください。
1≤A,B≤5000

```c++
int main() {
    long long A, B;
    cin >> A >> B;

    // この 2 行を追加するだけ！
    assert(1LL <= A && A <= 5000LL); // A の値が範囲外ならエラーを発生させる
    assert(1LL <= B && B <= 5000LL); // B の値が範囲外ならエラーを発生させる

    cout << A + B << endl;
    return 0;
}
```
Blenderで作った3DモデルをUnityに持っていく方法

ネットに参考になる記事はいくつかあったのですが、テクスチャとかその辺絡めての説明があったりなかったりだったので、備忘録も兼ねてメモです。

# 環境
- macOS Monterey
- Unity 2021.2.9f1
- Blender 3.0.0

# 謝辞

だいたいこの動画（人？）のおかげです。ありがとうございました。

https://www.youtube.com/watch?v=LxeMgT4Ox7U


# 目的

Blenderで用意した3DモデルをUnityに持っていきたい。

ちなみにBlenderではシェーダーでマテリアル作ったりでできますが、現時点の僕の知識ではもっていく方法がわかりません。過去の僕へ、諦めてください。

テクスチャは画像にして持っていけますが、Unityに持っていくと剥がれたりするのでそのあたりのやり方も記載します。


# 手順

## 1. 3Dモデルを用意する

今回は適当になんか用意しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/b82f9559-9399-1ffb-db4a-7b6ed3ea3423.png)

弊社ロゴをテクスチャにしてみましたが、キモい何かがうまれました。

## 2. BlenderからFBX形式でexportする

もし、exportしたいObjectを指定したい場合は、下記作業する前に「Scene Collection」でShiftとか使って選択しておきます。

`File → export → FBX(.fbx)` を選択します。


下の画像のようにパラメータをセットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/1ddc9bd0-c524-8a4e-d9c1-64ddffaf6d1f.png)

- Path Mode: Copyを選択して右のファイル入れみたいなところをクリックしてアクティブにする（でも多分いらないかも）
- Include
    - Limit to → Selected Objects: Export画面を選択する前にExportしたいObjectを選択しているならこれにチェックする
    - Object Type: Blender側にもカメラとかのObjectがあるが、Unity側では使わない（と思う）ので、AmatureとMeshだけ持っていく
- Transform
    - Apply Scalings: 「FBX ALL」を選択する。こいつを選択すると他パラメータ（軸）とかがUnity環境に最適な感じになる
    - Apply Transform: チェックを入れる


「Export FBX」押すと、指定したディレクトリにこれまた指定した名前のｆｂｘファイルが生成されます。

## 3.1 Unityでimport

どこでもいいのですが、 `Assets/` 以下にfbxファイルをドラック＆ドロップします。そしたらimportされます。やったね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/61bd3893-51c9-1227-7161-69ac257b4735.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/6810f6f2-f396-732e-7fb4-3fab2dbfe196.png)


・・・ケテ・・・タスケテ・・・


でも、テクスチャはアタッチされていません。

## 3.2 (テクスチャがあれば)テクスチャ画像をUnityへimportし貼り付ける

fbxでimportしたモデルのマテリアルはなぜかUnityでは編集できないです。Blenderで設定したテクスチャ画像をファイル名そのままでUnityにドラック＆ドロップすると良いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/6976767f-a581-eeab-c94f-5915025e7def.png)


同じ画像ファイル名であれば、自動でテクスチャがアタッチされます。


それでもうまくいかない場合は、自分でマテリアルを用意してテクスチャをアタッチ、マテリアルをモデルへアタッチしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/9e2bf133-67bc-1553-8291-be45fbd5dddd.png)


これを


こう

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101690/68c4bbe7-e66f-b239-0767-8be884ab91d3.png)

できました。


ただ、Blenderで設定した見た目通りにはならないかもです。


# まとめ

大変だった
【初心者】IBM Cloud FoundryでのFlaskアプリのデプロイがHerokuより簡単だった

Herokuに関してはネット上に色々と情報があったのですが、IBM Cloudに関してはほとんど情報なく困っていました。
限られた情報で試行錯誤しながらデプロイしてみたところ結果的にHerokuより簡単で驚いたので共有したいと思います。

IBM Cloudは他の方法でもウェブアプリを作れるようなのですが、試してみたところピンと来たのがこのIBM Cloud Foundryだったのでもっと良い方法がある場合は教えていただければと思います。

GitやDockerは使わずにIBM Cloud CLIの操作だけでwebアプリ公開ができました。

# はじめに
今回開発に至っては、基本的にこちらの情報を参考にさせていただきました。本当に感謝です。

https://qiita.com/makaishi2/items/21cbdb99444fb50e01e1

本記事で使用した環境は以下の通りです。

```
MacOS Monetrey v12.2.1
Python 3.9.6
```
流れとしては、以下のステップを踏みます。

1. IBM Cloudでアカウントを作る。
1. IBM Cloud CLIをインストールする。
1. 自分のプログラムをPushする。

# IBM Cloudアカウント作成

まずは、アカウントを作成してください。ここでつまづくことはないと思うので割愛します。

https://cloud.ibm.com/registration

# IBM Cloud Foundryでの設定

ログインしたらIBM Cloud Foundryを選択して以下から作成をしてください。
料金プランは無料を選んでいます。ロケーションに日本はないと思うのでシドニーなど任意の国を選んでください。
この記事はPythonアプリなのでリソースの構成はPythonを選びます。
ページの下の方に組織名とスペース名が書いてあると思うのでロケーションと併せて覚えておいてください。
IBM Cloud CLIの設定で使用します。

![スクリーンショット 2022-03-01 17.21.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/594366/f7471292-aa23-c47f-0c99-9ffe182fc7d2.png)



# IBM Cloud CLIのインストールと設定

以下のインストラクションに則ってインストールしてください。
マイクロサービスであればスタンドアロンタイプで問題ないです。

https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli&locale=ja

Macであれば以下のコマンドをターミナルに入れていただければすぐにインストールが開始します。
それ以下も色々と書いていますが、操作はこれだけで問題ないと思います。

```
curl -fsSL https://clis.cloud.ibm.com/install/osx | sh
```

その後ロケーションと組織、スペースを登録が登録されていないと表示されますので、指定のコマンドを打って登録します。
ロケーションはIBM Cloud Foundry設定時に決めたところにしましたが、日本でも大丈夫なのかなと思います。
ご心配でしたら登録内容と合わせて設定してみてください。（すみません・・・）

# ローカル環境でのフォルダ準備

任意のフォルダを作成してターミナル上で移動します。（＜＞を外して入力）

```
cd ＜任意のフォルダ＞
```

mkdirでフォルダを作ってもらってもOKです。（＜＞を外して入力）

```
mkdir ＜任意のフォルダ名＞
```

そのフォルダ内に以下4つのファイルを揃えてください。これが最小限なのかと思います。
-　name.py （プログラム本体。nameは任意の名前に変更してください。） 
-　requirements.txt　（必要なモジュール。バージョンを書かなくても動きます。）
-　Procfile　（拡張子なし）
-　manifest.yml　（下記参照）

```name.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
from os.path import join, dirname

#中略　ご自身のFlaskアプリを記入ください。Flaskなど必要なモジュールも追加してみてください。
#別のHTMLをご用意する場合は、render_templateなどご記入ください。参照記事に詳しく書いてあります。

port = os.getenv('VCAP_APP_PORT', '8000')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(port), debug=True)
```

以下一例です。cloudantは参照した記事の通り残しているのですが、無くても動くと思います。
```requirements.txt
Flask>=1.0.0
cloudant==2.13.0
flask
pandas
```

以下Procfile。name.pyは仮ですので、ご自身のプログラム名を入れてください。
```
web: python name.py
```

```manifest.yml
---
applications:
 - name: <任意の名前。プログラム名とは異なってOKです。（カッコは取ってください。）>
   random-route: true
   memory: 128M
```

# デプロイ
参照した記事ではcf pushのみとなっていますがコマンドが変わっているようです。
ただこれをするだけでデプロイ完了です。（以下ターミナル）
ブラウザ上でログインしてダッシュボードから作成したアプリを探すとURLが見つかりますのでそこから飛んでみてください。
```
ibmcloud cf push
```
Numpyro HMCで直線回帰

#### Numpyro HMC
Numpyro HMCを用いた単純な直線回帰MCMCのコードを書きます。HMCは低棄却率のMCMCサンプリング法の一つで、特に高次元だと効率が良くなるそうです。以下の説明は自己満なので、読まずに信じてはいけません。

```python
import jax
from jax import random
import jax.numpy as jnp
import numpyro
import numpyro.distributions as dist
from numpyro.infer import MCMC, NUTS, HMC
import numpy as np
import arviz
import matplotlib.pyplot as plt
import pandas as pd
```
ここでは、単純な直線回帰を考えます。データの誤差が正規分布するとして、
$$\mu=mx+b\\\\
 y\sim\mathcal{N}(\mu,\sigma)$$
としましょう。

```python
#真のパラメータ
m_true = 0.3
b_true = 0.5
sigma_true = 0.2

np.random.seed(0)
N = 50
x = np.random.uniform(-np.pi,np.pi,N)
y_obs= m_true*x+b_true+np.random.normal(0,sigma_true,N)
plt.scatter(x,y_obs)
X = np.linspace(-np.pi,np.pi,N)
plt.plot(X,m_true*X+b_true,c="red")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2392455/8a16c854-a1d7-1323-0338-38c917cfcd10.png)
これに対して、モデルフィットを行います。データの誤差は無知として、正規分布すると思ってlikelihoodを書いてみましょう。
```python
# set model                                                         
def model(X, y=None):
    N = len(X)
    m = numpyro.sample("m",dist.Uniform(-10,10))
    b  = numpyro.sample("b",dist.Uniform(-10,10))
    sigma = numpyro.sample('sigma', dist.Uniform(0,5))
    mu = m*X+b
    numpyro.deterministic("chi2dof", jnp.sum((y-mu)**2/sigma**2)/N)
    #numpyro.sample("y",dist.Normal(mu,sigma),obs=y)
    loglike = -0.5*jnp.sum(((y-mu)**2)/(sigma**2)) - N*jnp.log(sigma) - 0.5*N*jnp.log(2*jnp.pi)
    numpyro.factor("loglike", loglike)
```
Pyro系はlog likelihoodを明示して書く必要がないようですが、numpyro.factorで指定することも可能です。
ここでは、データの各点の誤差が独立な正規分布すると仮定しているので、
$$\ln{L}=\ln\Pi^{N}_{i}\frac{1}{(\sqrt{2\pi\sigma_i})^{N}}\exp{-\frac{1}{2}{\left(\frac{y_i-\mu}{\sigma_i}\right)^2}}$$
としています。
```python
# setting up the sampler                                                   
nuts_kernel = NUTS(model)
num_warmup, num_samples = 300, 3000
mcmc = MCMC(nuts_kernel,num_warmup=num_warmup, num_samples=num_samples,num_chains=1)
```
num_warmupは、HMCの各linkにおけるサンプリング長を決めるためのburn-inのようなもの(?)です。
```python
# sampling                                                                 
mcmc.run(random.PRNGKey(0), x, y = y_obs)
mcmc.print_summary()
sample = pd.DataFrame(mcmc.get_samples(group_by_chain=False))
```
```
                mean       std    median      5.0%     95.0%     n_eff     r_hat
         b      0.47      0.03      0.47      0.42      0.51   4438.33      1.00
         m      0.29      0.02      0.29      0.26      0.32   4707.75      1.00
     sigma      0.19      0.02      0.19      0.16      0.23   3559.17      1.00

Number of divergences: 0
```

```python
fig = arviz.plot_trace(mcmc,show=True,figsize=(12,6),compact=False)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2392455/5d474dad-4beb-3bc5-7a02-e3b279fb14ff.png)

```python
import corner
fig = corner.corner(sample,truths=(b_true,1,m_true,sigma_true),show_titles=True,truth_color="red")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2392455/cf450f92-963a-a5ee-73c5-7122b1e0a2a2.png)

今回はHMCの恩恵を感じることはありませんでしたが、Numpyro HMCの使い方を少しは把握したと思います。
【SwiftUI】縦向きiPadで2カラム表示(サイドバー表示)する

NavigationViewをiPadで使用すると標準スタイルやDoubleColumnNavigationViewStyleの場合、初期状態で横向き2カラム、縦向き1カラム(サイドバー非表示)になります。

![Simulator Screen Shot](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515818/da27cd3c-c7cf-4dd5-b832-2c636f48d91a.jpeg)

これを横/縦向きどちらも2カラム表示にします(「設定」標準アプリみたいな感じ)。

# 手順
### Introspect for SwiftUIをインストール
NavigationView内部のUISplitViewControllerクラスのプロパティを変更したいためIntrospectを使用します。

+ [リポジトリ](https://github.com/siteline/SwiftUI-Introspect)
SwiftPMとPodsに対応しています。インストール手順はリポジトリを参照ください。


### 設定
```swift
import Introspect
//--------------
         NavigationView{
                List{
                  Text("Primary Menu")
                }
                Text("Second")
            }.introspectSplitViewController{
                svc in
                svc.preferredDisplayMode = .oneBesideSecondary
                svc.preferredSplitBehavior = .tile
                
            }.navigationViewStyle(DoubleColumnNavigationViewStyle())
```
introspectSplitViewControllerでNavigationView内部のUISplitViewControllerを取得します。
preferredDisplayMode = .oneBesideSecondaryで2カラム表示を設定し、
svc.preferredSplitBehavior = .tileでタイル表示にします(初期設定だとオーバーレイ表示)。

### 補足
上コードList内にNavigationLinkを設置した場合、NavigationLinkの遷移Viewが2カラム目のViewとして表示されます(つまり通常のNavigationViewの挙動と同じです)。

# 参考
+ [Split view on iPad portrait mode results in a useless view being presented on startup](https://stackoverflow.com/questions/69715797/split-view-on-ipad-portrait-mode-results-in-a-useless-view-being-presented-on-st)
+ [Managing UISplitViewController displayMode with orientation change & multitasking in iOS 14?](https://developer.apple.com/forums/thread/653061)
ubuntuにNASをマウントする

# はじめに
ubuntuにNasをマウントしたときにいい感じの記事を見つけるのに時間がかかったのでメモとして残す

# 環境
ubutnu 20.04lts
cifs-utils 2:6.9-1

# インストール
```
sudo apt-get install cifs-utils
```

# マウント
/etc/fstabを編集
```
user$ nano /etc/fstab
```

```bash:/etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# *
# *(いろいろ書いてる)
# *
/swapfile                                 none            swap    sw              0       0

#以下の行を追加
//{IP}/{Nas上のパス} {ubuntu上のパス} cifs user,uid=1000,gid=1000,username={NasUser名},password={NasPassword}
# 例
#//192.168.10.1/date /mnt cifs user,uid=1000,gid=1000,username=test_user,password=test_pass
```

マウント コマンド
```bash
# /etc/fstabに書いてる全てをマウントするなら
sudo mount -a
# 特定のパスのマウントのみ実行するなら
sudo mount {ubuntu上のpath}
# 例 sudo mount /mnt
```

# アンマウント
```bash
# 特定のubuntu上のpathのみアンマウント
sudo umount {ubuntu上のpath}
# 例 sudo umount /mnt

# 全てをアンマウントするのは、把握していないすべてのマウントが解除されるのでやめたほうがいい
sudo umount -a
```
オムニチャネルのプレゼンス状況の権限設定（ユーザの権限セットの設定を注意）

オムニチャネルのプレゼンス（busyとon break）が設定して
エジェントの状況に表示したくない場合、
プロファイルのサービスプレゼンス状況のアクセス権を設定します。
![オムニチャネルprofile.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442974/e4294ffa-0e36-7262-9865-d59e1987eea6.png)


もしプロファイルのサービスプレゼンス状況のアクセス権を設定しました、
busyとon breakも表示される場合、
該当ユーザの権限セットを見てサービスプレゼンス状況のアクセス権の設定が正しいかどうかと確認します。
![オムニチャネル.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/442974/dd44ce47-5780-e74a-5e0e-2e9e05e30b48.png)
PHPにて、GoogleSpreadSheetApiでの操作方法

シートの先頭に行を挿入する方法
```php
$spreadsheet_id = 'hogehoge';
$requests =
    ['insertDimension' => [
        'range' => [
            'sheetId' => 'sheet_idを入れる',
            'dimension' => "ROWS",
            'startIndex' => 0,
            'endIndex' => 1
        ]
    ],
    ];
$batchUpdateRequest = new \Google_Service_Sheets_BatchUpdateSpreadsheetRequest(
    array(
        'requests' => $requests
    )
);
$sheets->spreadsheets->batchUpdate($spreadsheet_id, $batchUpdateRequest);

```

シートを追加する方法
```php
$spreadsheet_id = 'hogehoge';
$body = new \Google_Service_Sheets_BatchUpdateSpreadsheetRequest([
    'requests' => [
        'addSheet' => [
            'properties' => [
                'title' => "hoge" . time(),
            ]
        ]
    ]
]);
$response = $sheets->spreadsheets->batchUpdate($spreadsheet_id, $body);
$new_sheet_id = $response->getReplies()[0]->getAddSheet()->getProperties()->sheetId;
var_dump($new_sheet_id);
```
【PhpStorm】改行すると'}'が挿入されてしまうのをなくしたい

# 背景

業務でPhpStormを利用しているが、たまにファイルによっては'{'の後に改行を入れると強制的に'}'が挿入され、'}'が二重になってしまう問題が発生していた。
（筆者がこの問題にあたったファイルは.tagで終わるファイル）

![スクリーンショット 2022-03-01 16.36.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242071/fba370d1-814b-b012-6ee7-69e77f131787.png)
![スクリーンショット 2022-03-01 16.36.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242071/22e91a4d-44f0-a2aa-3d78-b2e494860f88.png)

'{'を入力した時点で'}'ができるので、改行したときには挿入される必要ないのが、挿入されてしまうので困りもの。
消せばいいだけなのだが、毎回挿入されるとストレスになるので解消したときの方法を備忘録として。

# 原因と解消方法

PhpStormのスマートキー設定でEnterを押すと対となる'}'が挿入されるように設定されていたので、設定を解除する。

1. `PhpStorm > Preferences`か`⌘ ,`で環境設定を開く
1. `Editor > General > Smart Keys`を開く
1. `Enter`の中から`insert pair '}'` のチェックボックスからチェックを外す

![スクリーンショット 2022-03-01 16.51.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2242071/300dd3bd-aa35-2143-7055-3e9e9c3d4b84.png)

これで改行したときに対となる'}'が挿入されなくなった。

もし同じ問題にあたった人は是非。
30代でテックキャンプに入学してみて

プログラミング学習を続けていると、YouTubeなどでも様々なプログラミングスクールの告知がでてきます。

多くのスクールが存在するのでどこに入学すればいいのか迷う方も多いと思うのでその時の情報の1つになればと思い記事にします。

1、なぜテックキャンプに入学を決めたのか
2、実際にテックキャンプに入ってみて
3、卒業後
４、メリット・デメリット
５、まとめ


## 1、なぜテックキャンプに入学を決めたのか

Excelの使い方を勉強しようとYouTubeを漁っている時に広告で「テックキャンプ ライト」(2日間の体験版です)の存在を知り申し込んだのがキッカケです。


今もあるのかは不明ですが、テックキャンプ ライトでは
・Scratch
・HTML　CSS
・Ruby
に少し触れることができました。

0の状態から書籍やYouTubeで独学を続けるよりは効率的だと思い入学しました。

その時も様々なスクールが存在していたことは知っていたのですが、オフラインで考えていたのでテックキャンプ一択でした。

## 2、実際にテックキャンプに入ってみて
同時期に入学した数名でチームが編成され、
朝の瞑想や、各時間に報告をする仲間ができます。

メンバーにも恵まれて、在学中はかけがえのない時間になりました。

メンターの質もよくネット上で問題になっていますが、
少なくとも私が在学中に対応して頂いた多くのメンターさんはいつでも丁寧に対応してくださいました。

何度も質問するので仲良くなったメンターさんにはなんでエンジニアになったのかを聞いて見たりしたのですが、
皆さん気さくに答えてくださっていました。

原則カリキュラム内のことは聞けないので、
カリキュラム内のエラーもこんな時はどこをポイントにして調べるのかなどをずっと質問して一緒に調べてみたりしていました。

##　卒業後
勉強会などに赴いたりしたのですが、テックキャンプ卒業生はかなり多いです。
そこからはなして盛り上がることも多々ありました。

MENTAで相談しているエンジニアもテックキャンプの卒業生でしたし、転職について相談していたエンジニアの方も元テックキャンプのメンターさんでした。

## ４、メリット・デメリット
メリット
・同期の受講生と一緒に学べる
・時間が定まっているので生活リズムが安定する
・卒業生が多いので会話の起点になることが多い
・カリキュラムは十分な量でした（個人的には）

デメリット
・受講料はそれなりの価格になる（だから覚悟にもなる）
・お世話になったメンターの方に挨拶はできない

## ５、まとめ
個人的に高校や専門学校で授業をしていた経験があるので強く思うのですが、
学費が高額であろうと安価であろうと結局自分がやらないとエンジニアへキャリアチェンジはできません。

スクールの環境を使ってどうすれば自分が一番頑張れるのか考えるのが最適解だと思います。
なのでタイミングよくテックキャンプの体験授業があったのは本当に良かったです。

卒業してから４ヶ月ぐらい転職期間があったのですが、
その間もずっと勉強していました。

当然ですが結局は自分次第なので、
少しでも自分に合った環境で学習できるようにスクールを選んでみてはいかがでしょうか。
Gmailで独自ドメイン（結果編）

Amazon SESを使って通常のGmailで独自ドメインを利用……しようとした。

https://qiita.com/Sand-River/items/4c0dbb16b70e11ec594a

# 結果的な構成

色々あって結果的には以下の構成となった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/554633/2a82d120-ddf8-bffe-2643-ee8317655d79.png)

- GSuite Legacyは塩漬け
- メールの受信はCloudflareのEmail Routing機能を利用してGmailに転送
- メールの送信はAmazon SESを利用してSMTPで配信

この構成で、少なくとも受信・送信ともにSPF/DKIMについてはPASSしてる。

## 「色々あって」の部分
Amazon SESはGmailからSMTPを利用した送信機能についてはほぼ不満がなかったが、受信したメールを素敵に転送するハードルが高かった。

- 添付ファイルを付けて転送しようと思うとSendRawMailを使わざるを得なかったが、その場合ってDKIMの情報を維持しようとするとAuthenticated Received Chainとか自前で実装するの……？
- 検証済みドメインしかFromに利用できないので、SESで転送すると書き換えざるを得ない。複数人宛のメールとかをうまく返信しようとすると頭痛い。

## Cloudflare素敵

- Email Routingはβ版だけど、特に不足・不満はない。
    - エイリアス多用してたので、catch-allが使えるのは嬉しい。そうでないと宛先不明を多発させそう。
    - 転送ルールにエイリアスや正規表現が使えるともっと嬉しいのだけれど……。
    - 費用は今の所タダみたい。
- ドメインの管理もDNSも引っ越した。基本、DNSをCloudflareに預けることで利用できるサービスが多い。
    - DNS管理がタダなのは個人に嬉しい。ドメイン維持費も原価。
- TOTPのMFAが使える。当たり前のようだけど、お名前.comはSMSやメールでのMFAしか使えなくってストレスだったので。
- アカウント管理も複数人のメンバーで行える。登録したメールアカウントをCloudflareで管理しているドメインだけにしてると死にかねないのでありがたい。

# 今後

塩漬け中のGSuite Legacyの脱却なり凍結なりを検討予定。
VContainer で注入されるクラスに EntryPoint をつけても動かない

# 環境
 - Unity 2020.3.25 LTS
 - VContainer 1.10.0

# 解決法
`[Inject]`されるだけで他のクラスに注入されないクラスなら `EntryPoint` を呼べるので、  
必要な `EntryPoint` へのインターフェイスだけを付けたクラスを用意してそのクラスに注入し、間接的に動かしてもらえばよい。

# 状況
UniRx + VContainerを使ってMV(R)Pで設計を進めると、基本的にはViewはuGUIと紐づくので `MonoBehaviour` に、それ以外をできるだけピュアC#クラスにしてイベント駆動する、という方針になるかと思います。  
(私もまだ初心者なので何がベストプラクティスなのかは把握できていません)

表示されるものにだけ `MonoBehaviour` をつけて、それ以外はピュアC#クラスで書いておき、依存関係はすべてDIに任せる、整った世界ですね。

ところで、JobSystemなどで「1フレーム以内には終わらない処理なので定期的に進捗を確認する」というような処理をMVPのどれが担当するかを考えると、ロジックなのでModelに持たせたくなると思います。

`MonoBehaviour` なしに `PlayerLoop` に相当するタイミングで任意のC#クラスを動かす仕組みとして、`VContainer` は `EntryPoint` を提供しています。

じゃあそれを使おう、ということで `Model` クラスに `Update()` のタイミングで呼ばれるインターフェイス、`Tick()` をつけて、無事……動きません。

# 原因
`VContainer` に依存関係を教える `LifeTimeScope` 内では、注入用のクラスの定義と`EntryPoint`用のクラスの定義が別になっているためです。

```C#
using VContainer;
using VContainer.Unity;

public sealed class YourLifeTimeScope : LifeTimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        // 注入されるクラスの生成方法の定義
        builder.Register<Model>(LifeTime.Singleton);

        // EntryPoint を呼ぶためのクラスの定義
        builder.RegisterEntryPoint<Presenter>();

        // 両方書くことはできるが Register<T>() とは別にもう一つ生成され、
        // DI されるものと EntryPoint されるものが異なる状態になる
        builder.RegisterEntryPoint<Model>();

        // Scene 中に配置した GameObject についている Component に
        // Register<T>() 済みのクラスを注入するための定義
        builder.RegisterComponentInHierarchy<View>()
    }
}
```

理由としては、`EntryPoint` が呼ばれるクラスを`LifeTime.Singleton`以外で好き勝手生成していたら、副作用が生じる関数を実装していた場合実行回数が定まらなくなり大変なことになってしまうので、`EntryPoint` は専用で管理して注入用のクラスとは別けておく設計にしたと考えられます。

# 対策

![VContainer_EntryPoint_tips.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1131238/61b7033e-a2d8-75bf-11fd-2f853368817d.png)

要は、「ほかのクラスに注入するクラスに`EntryPoint`をつけなければいい」というわけで、  
`EntryPoint` を呼ぶための専用のクラス、上図では `ModelEntryPoint` を作り、`Model` は `ModelEntryPoint` に注入して間接的に動かしてもらえばよい、ということになります。

一応動かすだけなら  
- `Model` も `Monobehaviour` にして Unity標準のPlayerLoopを使う
  (ただしSceneに配置しておく必要はある)
- `Presenter` に `EntryPoint` の定義も任せる
  (ただし`Presenter`の本来の責務: イベント処理の結合を定義　とは関連の薄い処理が混ざる)

といった方法もありますが、クリーンな設計を保つ意味でこのやり方がよいと思います。

# 参考
Official:
[VContainer](https://vcontainer.hadashikick.jp/ja/)

Qiita:
[VContainer入門](https://qiita.com/sakano/items/b91e01f7fc0a946090ac)
[【Unity(C#)】UniRxとExtenjectでMV(R)Pやってみる](https://qiita.com/OKsaiyowa/items/1e2477ace3c397b166aa)
Node.js: fetch の使い方

こちらを参考にしました。
[node-fetch/node-fetch](https://github.com/node-fetch/node-fetch)

ライブラリーのインストール

```bash
npm install node-fetch
```

```js:fetch01.mjs
#! /usr/bin/node

import fetch from 'node-fetch'

const url = 'https://api.github.com/users/ekzemplaro'
const response = await fetch(url)
const body = await response.text()

console.log(body)
```

実行結果

```text
$ ./fetch01.mjs | jq
{
  "login": "ekzemplaro",
  "id": 7269588,
  "node_id": "MDQ6VXNlcjcyNjk1ODg=",
  "avatar_url": "https://avatars.githubusercontent.com/u/7269588?v=4",
  "gravatar_id": "",
  "url": "https://api.github.com/users/ekzemplaro",
  "html_url": "https://github.com/ekzemplaro",
  "followers_url": "https://api.github.com/users/ekzemplaro/followers",
  "following_url": "https://api.github.com/users/ekzemplaro/following{/other_user}",
  "gists_url": "https://api.github.com/users/ekzemplaro/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/ekzemplaro/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/ekzemplaro/subscriptions",
  "organizations_url": "https://api.github.com/users/ekzemplaro/orgs",
  "repos_url": "https://api.github.com/users/ekzemplaro/repos",
  "events_url": "https://api.github.com/users/ekzemplaro/events{/privacy}",
  "received_events_url": "https://api.github.com/users/ekzemplaro/received_events",
  "type": "User",
  "site_admin": false,
  "name": "Uchida Masatomo",
  "company": null,
  "blog": "https://ekzemplaro.org",
  "location": "Tochigi,Japan",
  "email": null,
  "hireable": null,
  "bio": null,
  "twitter_username": null,
  "public_repos": 33,
  "public_gists": 0,
  "followers": 3,
  "following": 0,
  "created_at": "2014-04-12T03:29:10Z",
  "updated_at": "2022-02-10T08:40:41Z"
}
```

## 確認したバージョン

```text
$ node --version
v19.8.1
```
Amplify Lambdaのトリガーでミスした件

# Amplify Lambdaのトリガーでミスした件

## はじめに
Amplify CLIでS3にLambdaトリガーを設定した際に、トリガーが永続的に発火しLambdaの請求額が上がってしまったので再発防止+覚えておくように書きます。

## 概要
AWS Amplifyで開発をしています。以下に簡単な概要を記述します。
クライアントは、ある特定のファイルをAmplifyで作ったStorage(S3)にプットします。それと同時にAPIでDynamoDBにレコードを挿入します。

この際にS3にプットされたファイルオブジェクトが正しいデータが否かを確認するためにLambdaトリガーをAmplifyで設定します。

このLambdaはDynamoDBにそのファイルのデータがあるかを確認し、あればそのままファイルをS3に保存し、ない場合はそのS3オブジェクトを削除するというものです。

## ループまで
Amplify cli で storageにトリガーを設定し、プッシュしました。

```
$ amplify update storage

Select from one of the below mentioned services: Content (Images, audio, video, etc.)

Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) · yes

Select from the following options · Choose an existing function from the project
Select from the following options · LambdaTrigger

$ amplify push
```

上記設定で `LambdaTrigger` というLambdaがS3のトリガー設定に反映されます。
そしてそれをAWSに反映させるため、 `amplify push` を実行しました。

しかしこれが問題でした。
このAmplify cli の設定ではLambdaのトリガーがput時とremove時の両方に設定されるようです。
自分はこれを知らずにそのままで大丈夫かと思っていました。

そんなことを知らずにこのLambdaトリガーのテストを実行します。

まずはDynamoDBにレコードを追加し、S3にデータをプットします。
これはちゃんとした動作をし、Lambdaトリガーが発火しましたが、DynamoDBにレコードが存在するのでS3オブジェクトは正常に動作しました。

### ここから事件が
次にDynamoDB側に何も設定しないで、S3にデータをプッシュします。
するとLambdaトリガーが発火し今度はDynamoDBにレコードは存在しないので、S3オブジェクトはLambdaによって削除されます。
簡単なテストを実行確認したのにち自分の中で正常お通りに動いていると認識し、とりあえずそのままで次の作業を進めました。

少し時間がたってから料金がやばいことになっているという連絡がありました。
どうやらLambdaの実行回数が大変なことになっているということです。

最初は何のことかわかりませんでしたが、Lambdaの設定にS3オブジェクトを置いたときと削除したときにLambdaトリガーが発火する事を知りました。

その設定のため、

```
正規ではないデータが挿入される => 削除する => 削除したのでLambdaトリガー実行 => レコードが見つからないのでデータの削除 => 削除したので、、、
```

がループするという形になっていました。 
どうして、S3にオブジェクトが存在しないにも関わらずLambdaがループしたのかは恐らくLambdaのソースコードの書き方が悪く、S3オブジェクトの有無に関わらず削除の実行をしていたからだと思います。

## 反省点
反省点として
* Amplify cli でStorage Trigger の仕様を理解していなかった（確認していなかった）。
* Lambdaののソースコードの自体の書き方が良くなかった。
* そもそもS3の設定をamplifyだけしか確認しなかった。

などが挙げられます。
Amplify cliではそもそもput時とremove時の設定を分けることができないようなので、例えばLambda側でアクションで分岐したり、AWSコンソールでremove時はしないように変更したり、CDKで設定をしたりなど様々な方法があったかと思います。
关于saleforce项目只读设置的一些注意事项

Why are System Administrators Able to Edit fields Marked as Read-Only on the CRM Page Layout?
系统管理员仍然可以修改那些readonly的field。这是因为sys的profile里面，有个叫做Edit Read Only Fields的地方。所以你在pagelayout上把某个field设置成只读，但是实际画面上，管理员仍然可以修改。
想要彻底实现画面上的只读，就只有使用其他的profile，确保Edit Read Only Fields是false。同时设置pagelayout的只读

Under Administrative Permissions on the System Administrator profile, there is a setting called Edit Read Only Fields.  If this is marked as True, any user that belongs to the System Administrator profile is able to edit Read Only fields.  If the System Administrator profile is ever cloned, this setting carries over to the new profile.
【JavaScript】JSONファイルの読み込み方

JavaScriptでのJSONファイルの読み込み方をメモしておく。

# JSONファイルの読み込み方
まず、JavaScriptでJSONファイルを読み込むためには、``fs``モジュールの``readFileSync()``を使用する。

```javascript
fs.readFileSync(<ファイルパス>, <encoding>)
```
``encoding``はオプションなので省略できる。しかし、ファイルから読み込んだだけでは文字列として扱われる。
そのため、JSON文字列をJavaScriptのオブジェクトに変換する必要がある。変換には、``JSON.parse()``を使用する。
書き方は引数にJSONとして解析したい文字列を指定する。
上記の読み込みと合わせて書くと以下となる。

```javascript
const fs = require('fs');    // モジュールの読み込み

const json = fs.readFileSync(<ファイルパス>, <encoding>)
const data = JSON.parse(json)

// まとめて書く
const data = JSON.parse(fs.readFileSync(<ファイルパス>, <encoding>))
```
CCNA勉強 Section19 Wireless LAN　1

# ワイヤレスLAN
半二重通信で通信している
CSMA/CA(Carrier Sense Multiple Access/ Collision Avoidance)で制御
2.4と5Ghz

電波干渉が起こらないようにする
b5 g4

カバレッジホールができないように
オーバーラップが起こらないように

Localモード
FlexConnect:WLCが切れても独立して動作

#### WLAN構成範囲
BSS(Basic Service Set)
単一のAPとそのクライアントで構成される範囲
BSSIDはAPのMACアドレス

ESS(Extended Servie Set)
複数のBSSで構成されるネットワーク
ESSIDは、ESS内で各BSSを識別するために付与されるもの

より強い電波のAPに接続を切り替えることをローミングという

#### 端末とAPの通信
1. APが定期的にビーコンを送信している。クライアント側から探す場合はESSID情報をブローブ要求で送信する。プローブ応答が得られたら認証ほ行う。
2. 認証要求　認証応答

#### IEEE802.11のフレーム
フレーム制御フィールド:どんなフレームかを示す
管理
制御
データ

# WLANの構築
#### 自立型AP(Autonomous Access Point)
httpsによるGUI操作　コンソール接続・TELNET/SSH接続によるCUI操作の両方に対応
集中管理型AP(Lightweight AP)
WLCで一括設定・管理

暗号化やビーコン送信はAPがやる(役割分担)

Cisco Meraki
設定をクラウド上でやる

#### 集中型AP
CAPWAP(Control and Provisioning of Wireless Access Point)
トンネルを2つ形成し、データはカプセル化して転送
・制御メッセージ
UDP5246を使用。認証・暗号化
・データ(AP→WLC)
UDP5247を使用。平文のまま転送

物理ポートについて
サービスポート:スイッチのアクセスポートとLANケーブル接続。CLI操作
ディストリビューションシステムポート:スイッチのトランクポートとLANケーブルで接続。別名サービスポート。スイッチ側はEherchannelのモードをonにする
冗長ポート
コンソールポート

論理インターフェース
サービスポート：アウトオブバンド管理
ディストリビューションポート　
　　Managementインターフェース：インバウンド管理
　　Virtualインターフェース：代理DHCPサーバ？WEB認証など
 Dynamicインターフェース：SSIDとVLANのマッピング
npmでキャッシュをクリアする

Gitから参照しているパッケージのインストール時、下記のようなエラーになることがありました。

```bash
$ npm i
```

```console
npm ERR! Could not install from "node_modules/xxxxxxxxxxxxxxxxxxxxxxxxx" as it does not contain a package.json file.
```

```console
npm ERR! code ENOLOCAL
npm ERR! Could not install from "node_modules/xxxxxxxxxxxxxxxxxx" as it does not contain a package.json file.
```

教えてもらったのですが、下記のようにするとキャッシュがクリアされ、正しく動くようになりました。

```bash
$  npm install -g npm@latest && npm cache clean -f
```
外部のサーバーでPDFを作ってSalesforceから呼び出す

以前開発した時に参考にした資料をまとめておきます。


# PDF/Excel関係
+ [OpenOffice.org/PDFを出力する](https://www.masatom.in/pukiwiki/OpenOffice.org/PDF%25A4%25F2%25BD%25D0%25CE%25CF%25A4%25B9%25A4%25EB/)
+ [LibreOffice既定フォントの変更](http://mount.on.coocan.jp/sakusaku/new4-2.html)
+ [Create a Custom PDF Format](https://support.accountingseed.com/hc/en-us/articles/217193698-Create-a-Custom-PDF-Format?mobile_site=true)
+ [PythonでExcelファイルを扱うライブラリの比較](https://note.nkmk.me/python-excel-library/)