OmniAICreator/Qiita-1.07M huggingface dataset:
より良いアラート：新たなDatabricksワークフロー通知の発表

[Better Alerts: Announcing New Workflows Notifications \- The Databricks Blog](https://www.databricks.com/blog/2023/01/03/better-alerts-announcing-new-workflows-notifications.html)の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

# 信頼できるワークフローの実行を助けるよりリッチな通知

[Databricks Workflows](https://www.databricks.com/jp/product/workflows)は、Databricksレイクハウスプラットフォームと密に連携されている完全マネージドとのオーケストレーションサービスです。ワークフローは複雑なインフラストラクチャを管理することなしに、すべてのクラウドで信頼できるデータワークフロー、分析ワークフロー、MLワークフローを構築する際のAirflowやAzure Data Factoryに対する素晴らしい代替案となります。データチームが夜に安心して眠れるように、ワークフローをお使いのオペレーションスタックに容易に組み込めるようにwebhookを通じてジョブの通知を受け取れる機能を発表できることを嬉しく思っています。さらに、能率化されたメール通知もロールアウトします。

# Slackやwebhookを通じたワークフロー通知

新たなwebhook通知の宛先として、オペレーションモニタリングシステムにワークフローの通知を気見込むことが可能となりました。また、ジョブ実行の失敗のような重要なイベントに関して皆様のチームに最新状況を簡単にお伝えできるように、Slackとの直接的なインテグレーションを提供します。これらのよりリッチな通知によって、データチームはお使いのツールの中で直接コラボレーションできるようになります。
![](https://cms.databricks.com/sites/default/files/inline-images/db-445-blog-img-1.png)
*新たなネイティブSlack通知のサンプル*

今後のインテグレーションではMicrosoft TeamsやPagerDutyにも通知を送れるようになります。この機能は現在はパブリックプレビューです。[詳細はこちら](https://docs.databricks.com/workflows/jobs/jobs.html#alerts-job)を参照ください。

# 能率的になったメール通知

また、我々はメール通知にさらなるコンテキストとモダンなデザインを取り入れることで能率化を行いました。通知メールには、処理時間やエラーメッセージのような重要な情報が含まれるようになりました。
![](https://cms.databricks.com/sites/default/files/inline-images/db-445-blog-img-2.png)
*エラーメッセージを含む新たなメール通知のサンプル*

この新機能はワークスペースごとにオプトインすることで、すべてのお客様ですぐにご利用いただけます。管理者はワークスペースの設定を通じてこの機能を有効化することができます。

# Databricksワークフローを使い始める

完全マネージド、インテグレーションされたレイクハウスのオーケスとレーターが提供する生産性のブーストを体験するために、すぐに初めてのDatabricksワークフローを作成することをお勧めします。[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)をお試しください。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
オブジェクト指向について

要するにクラスを付与するもののこと

オブジェクト指向型とに対になるのが関数型

- クラスからインスタンスを呼び出す時コンストラクターを呼び出す

置き換わらないようにする

大文字始まりのものはクラス！

上書きしてしまう

新しくインスタンスを作成する

インスタンス化とは

static  関数名(thisが中で使用できない)

ジェネレーター
贅肉削ぎ落としたInputコンポーネント@Vue3（修正版）

強気のタイトルにしてしまったけどVue3触って日が浅いので、（なるべく）贅肉削ぎ落としたInputコンポーネントくらいにしておきます:baby:
Vue3+TypeScriptにおける低レイヤーコンポーネントの基本形。
使いまわせそうだったのでメモメモ:pencil2:

```Input.vue
<template>
  <input v-model="value" v-bind="defs.originals" />
</template>

<script setup lang="ts">
import { computed, InputHTMLAttributes } from 'vue'

interface Emits {
  (e: 'update:modelValue', value: string): void
}
const emit = defineEmits<Emits>()

interface Props extends InputHTMLAttributes {
  modelValue: string
}
const props = defineProps<Props>()

const defs = computed(() => {
  const { modelValue, ...originals } = props
  return { modelValue, originals }
})

const value = computed({
  get: () => defs.value.modelValue,
  set: (value: string) => {
    emit('update:modelValue', value)
  }
})
</script>
```

```呼び出し元.vue
<template>
  <div><Input v-model="value" /></div>
  <div><Input v-model="value" type="text" style="background-color: red" /></div>
  <div><Input v-model="value" type="password" style="background-color: green" /></div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import Input from './components/form/Input.vue'

const value = ref('xxxxxxx')
</script>
```

実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62209/8806a23e-a075-227d-18e2-661d05a2b492.png)
【Ruby】オブジェクトをコピーするときの注意

書籍Effective Rubyを読み進めて浅い（shallow）コピーと深い(deep)コピーというものを学びましたので、簡単にまとめます。

# 参照と複製について
Rubyでオブジェクトを複製する場合は参照と複製の二つがあります。
それぞれの使い方により、後々に思いもよらぬ動きになるので使い分けておくことが必要です。

## 参照について
参照は1つのオブジェクトをもとに参照して、代入する動きです。
この場合は`=`を使用します。

```ruby
a = ["Alice","Bob","Jack"]
b = a 
p a # => ["Alice","Bob","Jack"]
p b # => ["Alice","Bob","Jack"]

p a.object_id # => 47242730754220
p b.object_id # => 47242730754220

b[0] = "Rian"

p b # => ["Rian", "Bob", "Jack"]
p a # => ["Rian", "Bob", "Jack"]

p a.object_id # => 47242730754220
p b.object_id # => 47242730754220
```
`a`の配列に`=`をbを代入すると同じ配列が出力されます。

`object_id`メソッドで確認すると同じオブジェクトIDであることが確認できます。

参照された`b`の配列に`Rian`という新しいオブジェクトを追加した場合、参照元の`a`の配列が変化しているのがわかります。

この時のオブジェクトIDも変わりありません。

同じオブジェクトを作るという点では良い影響がありますが、参照元の他のオブジェクトにも反映されてしまうことに注意が必要です。

## 複製について
他のオブジェクトへの影響を回避する方法を考えてみます。

これは`複製`という方法で同じオブジェクトを作る必要があります。
`複製`はdupメソッドかcloneメソッドを使います。

```ruby
a = ["Alice","Bob","Jack"]
b = a.dup 
p a # => ["Alice","Bob","Jack"]
p b # => ["Alice","Bob","Jack"]

p a.object_id # => 47067846268240
p b.object_id # => 47067846268200

b[0] = "Rian"

p b # => ["Rian", "Bob", "Jack"]
p a # => ["Alice", "Bob", "Jack"]

p a.object_id # => 47067846268240
p b.object_id # => 47067846268200
```
`dupメソッド`を使った場合、オブジェクトIDが違うことがわかります。

これは、`a`の変数に代入された配列と同じものがbに代入されていますが、新しい同じ配列を代入していることがわかります。

このため、`b`の変数に新しいオブジェクトを追加しても、元のaの変数には影響がないことがわかります。

この挙動は`cloneメソッド`も同じです。

# 浅いコピー(shallow copy)

先ほどの例をもう少し深ぼりしてみます。
dupメソッドで複製した配列の要素のオブジェクトIDを見てみると、実は同じオブジェクトIDを出力します。
```ruby
a = ["Alice","Bob","Jack"]
b = a.dup 
p a # => ["Alice","Bob","Jack"]
p b # => ["Alice","Bob","Jack"]

p a.object_id # => 47067846268240
p b.object_id # => 47067846268200

b[0] = "Rian"

p b # => ["Rian", "Bob", "Jack"]
p a # => ["Alice", "Bob", "Jack"]

p b[1].object_id # => 47240279609360
p a[1].object_id # => 47240279609360
```

これは、オブジェクト自身を複製しているだけで、参照先までは複製できません。
これを浅いコピー(shallow copy)と言います。

`dupメソッド`と`cloneメソッド`の違いは、cloneで複製された場合にfreeze、特異メソッドなどの情報も含めた複製を作成します。

以下はfreezeを指定した場合です。
```ruby
a = ["Alice","Bob","Jack"]
a.freeze
b = a.clone
c = a.dup

p a.frozen? # => true
p b.frozen? # => true
p c.frozen? # => false
```

浅いコピーを理解した上で、参照先まで複製できる深いコピーの仕方を学びます。

# 深いコピー(deep copy)
Marshalモジュールを利用して複製します。

```ruby

a = ["Alice","Bob","Jack"]
b = Marshal.load(Marshal.dump(a))

b[0] = "Rian"
p a # => ["Alice", "Bob", "Jack"]
p b # => ["Rian", "Bob", "Jack"]

p b[1].object_id # => 47182587105860
p a[1].object_id # => 47182587106120
```

配列の要素までオブジェクトIDが違うのがわかり、完全な複製ができていることがわかります。
ただし、Marshalできないオブジェクトが含まれている場合は複製できないので注意しましょう！

## 参考

- [Ruby 3.2 リファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/method/Object/i/clone.html)
- https://spirits.appirits.com/doruby/9883/
- https://style.potepan.com/articles/27809.html
model.state_dict()でRuntimeError: CUDA error: out of memory が発生した時の解決方法

## 背景
　0番のGPUで学習し保存したモデルを1番のGPU上でロードしようとしたところ、以下のようにエラーが発生した。

```python:
import torch

model.state_dict(torch.load("model.pth")["state_dict"])
# RuntimeError: CUDA error: out of memory

```



## エラーの原因
　どうやらtorch.load()はデフォルトで、モデルを保存した際にモデルが存在していたメモリ上で、モデルのロードを行う設定になっているらしい。自分の例で言うと、0番のGPU上のモデルを"model.pth"に保存したため、torch.load("model.pth")を行うと、0番のGPU上でモデルのロードが行なわれていた。
　0番のGPUは他のモデルの学習中でメモリが占有されていたため、モデルをロードすることができずに、out of memory のエラーを吐いたわけである。

## 解決方法
### 方法1
　torch.load()のパラメータmap_locationで、モデルをロードするdeviceを1番のGPUに指定することで解決した。
```python:
import torch

device = "cuda:1"
model.load_state_dict(torch.load("model.pth", map_location=device)["state_dict"])

```
　このようにして、モデルのロードを1番のGPU上で行うことが可能になった。

### 方法2
　この方法は、モデルをロードする時ではなく、モデルを保存する時の対策である。したがって、既に保存済みのモデルをロードしたい時には使えない。
　torch.save()でモデルを保存する前に、モデルを別のメモリに移す、という方法である。
```python:
import torch

model.to('cpu')
torch.save(model.state_dict(), 'model.pth')
```
　上の例では、モデルをCPUに移してから保存しているので、モデルをロードする際にはCPU上で読み込みが行われます。
パーソナルファイアウォール とは

## 勉強前イメージ

個人的なファイアウォール？

## 調査

### パーソナルファイアウォール とは

そもそもファイアウォールとは外部との通信を監視して不正なアクセスを遮断する仕組みになります。
基本的に導入するもので標準で搭載されているものもあります。

そして、パーソナルファイアウォールとは
家庭用や仕事で利用するコンピュータ向けに開発されているファイアウォールになります。
インターネットなどの外部との通信を監視、不正なアクセスは社団を行います。
personal firewallの略で、PFWとも言われます。

### ファイアウォールとパーソナルファイアウォールの違い

2つの違いは以下になります。

- パーソナルファイアウォール : パソコンの中に設置して入ってくる不正な通信を防ぐ
- ファイアウォール : 組織などのネットワークの入り口に設置して不正なアクセスを防ぐ

## 勉強後イメージ

確かにファイアウォールいうても
パソコンにも入ってるし、機器もあるし・・・・
linuxでいう、firewalld的なやつってことかな？

## 参考

- [パーソナルファイアウォールとは？仕組みやメリット、設定方法について徹底解説](https://cybersecurity-jp.com/column/35443)
- [パーソナルファイアウォール【personal firewall】PFW](https://e-words.jp/w/%E3%83%91%E3%83%BC%E3%82%BD%E3%83%8A%E3%83%AB%E3%83%95%E3%82%A1%E3%82%A4%E3%82%A2%E3%82%A6%E3%82%A9%E3%83%BC%E3%83%AB.html)
年末年始を楽しむために拡張機能を開発したら便利すぎた

# はじめに
年末・年始はNetflixを見まくってやろうと思いマイリストに気になる映画とアニメをぶち込んでいたところ、数が多すぎて何が何だか分からなくなってしまいました。

ということで、マイリストを好きな名前でグループ化できる拡張機能を開発してみたら結構便利だったので紹介したいと思います！
面白いと思っていただけたらいいねお願いいたします！！

# 開発したChrome拡張
👇こちらになります！無料でお使いいただけるので是非使ってください！

https://chrome.google.com/webstore/detail/create-groups-in-netflix/bicnagkkmhehedgogcopdadkakldblfk

# なにができる
Netflix上の気になる映画やアニメを好きなグループに分類して管理することができます。

マイリストに追加したのはいいものの、その数が多すぎて目当ての映画を探すのに時間がかかってしまった経験はありませんか？僕はめちゃくちゃあります。。

ですが今回開発した拡張機能を使ってからはそういった悩みはなくなった気がします。
自分で開発したからバイアスかかってるだけだろ！と言われたらそれまでですが🙄
![ホ_ー_ム_-_Netflix_-_Google_Chrome_2022-12-30_16-58-08_Trim_AdobeExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838672/739dd18f-e949-eebb-abf7-10f939682e11.gif)

### 使い方について
画像のに元々元々ある評価ボタンの右横に追加ボタンが増設されます。
このボタンを押していただくことでお気に入りに追加することができます。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838672/df5c7c61-f177-36c0-9bad-f08f8010ac4e.png" alt="netflix_btn.PNG" />

次にヘッダーに追加された「My Category」を押下してもらうと、お気に入りに追加した映画一覧が表示され、カテゴリーの作成や分類もこの画面で行うことができます。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838672/d3c8c856-f850-4b9e-914c-bc31bf17bf17.png" alt="netflix_itiran.PNG" />
分類した動画ごとに次のように表示されます。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838672/fc3387e8-8fe6-f2cb-2a7c-a0626a334a67.png" /><img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838672/a85572d2-d0a2-9c58-5445-db18fda32c99.png" alt="netflix_fullscreen.PNG" />

### すぐ使える！！
インストールするだけで簡単に使えますので、Netflixライフのお供に是非ご活用下さい！

https://chrome.google.com/webstore/detail/create-groups-in-netflix/bicnagkkmhehedgogcopdadkakldblfk
PHPerが学ぶPython③ 例外処理

おいっす。豚野郎です。
Pythonの変数・配列・クラスの説明は書いてきたので、
最後に例外処理の記事を書けば言語としての基礎は全て書いたことになるのかなと思います。

そのため今回は、基礎としては最後として「例外処理」について書いていきます。

<font color="Red">※ 注意：内容はPHPか何かの言語を書いたことがある方向けなので、
説明を割愛している箇所は多々ありますので、ご了承ください。</font>

**バージョン：3.9.1**

【過去の記事】
■ PHPerが学ぶPython① 変数・配列・タプル

https://qiita.com/pig_buhi555/items/301f7c8eabd1a5fd121f

■ PHPerが学ぶPython② クラス

https://qiita.com/pig_buhi555/items/ab6108d6fc03ccfffbe4

# 1. 実行時の例外
以下が発行する例外の種類です。
①. NameError：変数定義されていない時
②. ZeroDivisionError：0で割り算を行った時
③. TypeError：数値と文字列等、型が異なる値を計算しようとした時
④. ValueError：文字列を整数に変換しようとした時、変換対象の文字列が数字ではない等、型が不適切な時

例として、①を試してみます。

```exception.py
try:
    print(test)
except NameError:
    print('変数が定義されていません')
```
```
$ python exception.py
変数が定義されていません
```

例外は、複数記載することが可能です。
```exception.py
try:
    test = 'テスト'
    test += 1
except (NameError, TypeError):
    print('今回はTypeError')
```
```
$ python exception.py
今回はTypeError
```

その他のエラー・エラーがない時・エラーがあっても無くても実行したい場合は以下の様に記載します。
```exception.py
try:
    test = 1
    test += 1
except (NameError, TypeError):
    print('今回はTypeError')
except Exception:
    print('その他のエラー')
else:
    print('エラーなし')
finally:
    print('エラーがあっても無くても表示')
```
```
$ python exception.py
エラーなし
エラーがあっても無くても表示
```

# 2. 特定のタイミングで例外発生
以下ではValueErrorの例外を発生させます。
```exception.py
raise ValueError
```
```
$ python exception.py
Traceback (most recent call last):
  File "パス/exception.py", line 1, in <module>
    raise ValueError
ValueError
```

エラーメッセージを表示させたい時は、以下の記載をします。
```exception.py
try:
    raise ValueError('変数定義')
except ValueError as msg:
    print(msg, 'のエラー')
```
```
$ python exception.py
変数定義 のエラー
```
※ 必ずmsgにしなければならないわけではないです。

# 3. 自分で例外を作る時
```exception.py
# Exceptionクラスを継承する
class SampleError(Exception):
    message = 'エラーを発生させています'

error_message = SampleError.message

try:
    raise SampleError
except SampleError:
    print(error_message)
```
```
$ python exception.py
エラーを発生させています
```

特別な処理を行わない場合、passと記載します。
```exception.py
class SampleError(Exception):
    # 特別な処理を行わない場合
    pass

try:
    raise SampleError
except SampleError:
    print('エラーを発生させています')
```
```
$ python exception.py
エラーを発生させています
```

# 感想
例外も終えたので、基礎はこれでOKかなと思います。
GitHub ActionsでFirebaseエミュレータをキャッシュする

GitHub ActionsでFirebaseエミュレータを使用したテストを実行すると以下のように怒られたので対応しました。
```
⚠  It appears you are running in a CI environment.
You can avoid downloading the Firestore Emulator repeatedly
by caching the /home/runner/.cache/firebase/emulators directory.
```

# 対応

インストールされるエミュレータのバージョンは `firebase-tools` のバージョンに依存するらしいので、

```yaml
- name: Install firebase-tools
  run: |
    yarn global add firebase-tools
    echo "FIREBASE_VIRSION=$(firebase --version)" >> $GITHUB_ENV
```

で環境変数に `firebase-tools` のバージョンを保存し、それをkeyにしてキャッシュする以下のstepを追加しました。

```yaml
- name: Cache firebase emulators
  uses: actions/cache@v3
  with:
    path: ~/.cache/firebase/emulators
    key: ${{ runner.os }}-firebase-emulators-${{ env.FIREBASE_VIRSION }}
```

## ワークフロー全体

ワークフロー全体は以下のようになります。

```yaml:.github/workflows/test.yml
name: Test

on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-node@v3
        with:
          node-version: "14"
          cache: "yarn"

      - name: Install dependences
        run: |
          yarn install

      - name: Install firebase-tools
        run: |
          yarn global add firebase-tools
          echo "FIREBASE_VIRSION=$(firebase --version)" >> $GITHUB_ENV

      - name: Cache firebase emulators
        uses: actions/cache@v3
        with:
          path: ~/.cache/firebase/emulators
          key: ${{ runner.os }}-firebase-emulators-${{ env.FIREBASE_VIRSION }}

      - name: Run test
        run: |
          firebase use dev
          firebase emulators:exec --only firestore 'jest firestore'
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
```
Python向けのMakefile的なビルドツール「JTCMake」を作った話

# はじめに

俺は非IT系のJapanese Traditional big Companyでエンジニアをしている。
業務で遭遇した課題にインスパイアされてJTCMakeという汎用的なビルドツールを2022年の7月頃からプライベートで開発してきた。そろそろ安定してきたので宣伝させてほしい。

- [Github](https://github.com/sugi-a/jtcmake)
- [使い方](https://sugi-a.github.io/jtcmake/index.html)
- [PyPI](https://pypi.org/project/jtcmake/)

# 機能概要

JTCMakeは構造的タスク管理および差分ビルドのためのツールである。当初想定していた用途は機械学習パイプラインの管理だが、より一般にファイル生成タスクの自動化に使うことができる。

基本的な機能はMakefileのようにファイル作成のルールの集合を定義して差分ビルドをすることだが、さらに以下のような特徴がある。

1. 入力ファイルの内容にもとづいてタスクの実行要否を判定可能（入出力ファイルのタイムスタンプベースでの判定も可能）
2. ルールはPythonで記述
3. 出力ファイルのディレクトリ階層構造をタスクが持つ階層構造と対応させてシンプルに扱うことができる
4. MypyやPyright等の型検査器を使ってルールの記述を検査できる
5. ファイルやルールの依存関係や出力ディレクトリツリーを可視化する便利ツールを標準搭載

ビルドルールをPythonで記述するのでシェルスクリプトと比較して複雑な処理が書けるほか、Windowsでも使えるというメリットがある[^windows]。さらに2〜5（特に4の型検査）の性質により大規模なプログラムをあまり苦労なく書くことができる。

3は何を言ってるかわかりづらいと思うが、例えば機械学習というタスクは前処理・訓練・評価という3つのサブタスクからなり、さらに評価は推論、性能指標の計算、エラー分析のための可視化などのサブタスクからなる、という具合に階層構造を持っており、各タスクにおいて生成されるファイルはタスク階層に類似したディレクトリ構造で管理されるのが一般的である。その性質をソースコード上でも自然かつ非冗長な形で表現する仕組みがあるくらいの意味。

[^windows]: Windows云々は所属組織固有のニーズであり一般的ではないという認識はあります

# モチベーション

俺は大学時代に機械学習絡みの研究のようなことをしていてその当時からデータの前処理やモデル訓練、評価結果の集計などを差分ビルドできたらなあという気持ちがあった。具体的にはJupyter Notebook上でパイプラインの一部をちょっと変えた後で更新の必要なセルだけ手動で選んでちまちま実行するのが疲れるとか、コマンドラインフラグに応じて特定のルーチンだけ実行するオレオレ部分実行機能を備えたシェルスクリプトを書くのがしんどい等の問題意識があった。そもそもシェルスクリプトで制御構文を書きたくないし、どうせ機械学習でPythonを使うのでワークフローも可能な限りPythonで管理してシェルスクリプトは最小限にしたいとも考えていた。

Makefileを使えばよいという意見はあって実際たまに使っていたが、`$<`、`$$`などの記法の可読性の悪さ、シェルスクリプトの表現力が小ささ、生成物がMakefileから遠い場所にある場合のパス記述の冗長さ等々であまり実用的だと思えなかった。JTCに就職してからは開発環境がWindowsになってそもそもMakefileが使えなくなった[^nmake]。

[^nmake]: NMAKEというVisual Studio版のMakefile的なものはありますが使い方を覚える気が起きませんでした。ソフトウェアのビルドに使う想定なのであまり複雑な処理の記述には向いていないという点はMakefileと同じだと思います。

仕事で某古の統計的音声認識ツールキットを触ったことも動機のひとつである。そのツールはデータの入出力がファイルベースになっており便利なPythonラッパーはない。さらに統計的音声認識の性質上、学習や推論のコンポーネントが複雑で入出力ファイルの管理が煩雑になりやすい。そのため学習・推論の全工程を宣言的に記述してファイルや処理をノードとする計算グラフを可視化できると良いと思われた。

以上の経緯があって何かよいツールはないかと探してみた。ビルドツールというとCMakeやBazel、Rakefile等が有名だし機械学習のワークフロー管理についてもMLflowなどいくつか知られたものがあるが俺のニーズとマッチしていない気がしたので結局自分で作ることにした。車輪の再発明ではあるがJTCMakeがフィットするようなケースが少しはあると思う。

# 大変だったこと

Pythonの型ヒントのエコシステムに手こずらされた。
JTCMakeはビルドルールの記述にDSLを用いず通常のPythonの構文で行う仕様になっている。これにより開発者はパーサ作成の手間を省くことができ、ユーザにとってはDSLの学習コストが発生せず、Pythonの静的型検査を応用してルールの記述を検証できるというメリットが有る。一方でPythonの型システムが未成熟なこともあり、型ヒントが活きるような（≒なるべくAnyを避ける）APIを設計する難易度が高く、これを考えるのが開発において最も大変だった。例えばTypescriptのIntersection型やMapped Type、Conditonal Typeに相当するものが無くて型パズルの自由度が低かったりtyping_extensionsにしかない型はどれか調べる必要があったり、Pyright[^pyright]は対応しているがMypyは対応していない型への対応方法を考えなければならなかったりして消耗した。

[^pyright]: 自分はVimにcoc-pyrightというプラグインを入れて開発していますが、利用者の多いVSCodeのPython拡張も裏はPyrightらしいのでMypyとの不整合は問題になるんじゃないかと思います（があまり聞かない）。なおPyrightの方が最新の型に対応していて型検査が賢い気がしているのでJTCMake本体の型検査はPyrightのみでやっています。

# 終わりに

まだコードは荒削りなところがあるしAPIも改善の余地があるかもしれないが最低限の完成度にはなったと感じている。自分以外のユーザがいないツールをミリ改善することにあまりモチベーションがわかなくなったのでこの記事を以て一旦成仏とする。質問や改善要望が来たら対応します。
googlehomeのブロードキャストは、同じWi-Fiネットワークでなくても出来る

# google home デバイスが意外と面白い。
4年ほど前にGoogle Homeを買った。なにをやらせるわけでもなく、単に興味半分で。
「声で命令するよりマウスで操作したほうが早いだろう」と思いつつも。
しかし、いろいろと喋ってくれるので、意外と楽しかった。（本題とは外れるので省略）
その後、Google Nest Miniを2台買った。リンクさせると更に楽しい。

# その一つに、ブロードキャストがある。
1つのデバイスから、全デバイス（又は特定のデバイス）に一斉放送する感じだ。
例えば、朝、台所のデバイスから「ご飯出来たよ」と話せば、各部屋に置かれたデバイスから「ご飯出来たよ」と流すことができる![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628816/e9e7c1dd-187d-e1a4-d9fd-3d0921a810f0.png)

この方法については多くの記事で書かれていますが、その全てで

:::note info
ブロードキャストできる条件
 **すべてのデバイスが同じ Wi-Fi ネットワークに接続されていること** 
:::


っと書かれている（GoogleNestヘルプにも）。
しかし、

:::note warn
警告
そんなことはない！県をまたがってもブロードキャストできる（単なるバグかもしれないけど）
:::

# ブロードキャストは異なるWi-Fiネットワークでもできる #
#### 我が家では、他県に住んでいる娘のGoogleNestとお互いブロードキャストできている。 ####
ブロードキャストのやり取りで会話することもある。
また、娘は、時々テレビも蛍光灯も点けてくる。我が家は、まるでポルターガイスト状態（まぁ、これはスマホからもできる仕様ですが）。
娘はマンション住まいで共同のLAN環境なので、勿論、プロバイダーも違えばネットワーク自体も違う。
なんでぇ？
娘のGoogleNestは、我が家で使っていたものを持って行ったものなので、そのためとは思うが（そうでないと、縁もゆかりもない世界中のデバイスにブロードキャストできてしまうことになる）、真剣に調べようとはしていない。
 **誰かわかる人、試して教えて欲しいです。**

# まだまだ将来に期待できる #
Google HomeをESPマイコンやセンサーと組み合わせて、声で操作したり、喋らせたりと色々遊べる。
Google Home自体も機能が進化しているので将来が楽しみである
streamlit上で化学構造式を一覧で表示する方法

# はじめに
streamlit上で分子の描画に困ることがあります。
今回利用する`mols2grid`というライブラリを利用するとsmiles記法による分子の構造式を描画することが可能です。
[こちら](https://github.com/cbouy/mols2grid)にgithubリンクを張っています。

# 環境
* python3.9.16
* rdkit 2022.9.3
* streamlit 1.12
* mols2grid 1.1.0

# コード中身
```python:app.py
import mols2grid
import pandas as pd
import streamlit as st
import streamlit.components.v1 as components

#アプリタイトル
st.title("分子構造描画アプリ")

# smilesのカラムを含むデータフレームを作成。今回はQM9のデータの一部を利用
df_qm9 = pd.read_excel("qm9.xlsx")

# streamlit上に表示
st.write(df_qm9)

# mols2gridを利用して構造式をstreamlit上に描画
raw_html = mols2grid.display(df_qm9, smiles_col="smiles")._repr_html_()
components.html(raw_html, width=900, height=900, scrolling=True)
```

# 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449785/b28fb21d-afa8-ecc9-5709-6526f0e0ffa9.png)
このような画面が出力されます。
右下の`Sort by`をクリックすることで、任意のカラムの情報を用いてソートすることができます。

今回はpandasのデータフレームを使いましたが、そのほかにもrdkitの`molオブジェクト`や`sdfファイル`を読み込むことが可能です。
```python
import mols2grid

# sdfファイルを読み込む場合
mols2grid.display("molecules.sdf")
# molオブジェクトを読み込む場合
mols2grid.display(mols)

```

本記事では割愛しますが、そのほかに3D構造を表示したり、popupを表示させたりすることも可能です。
この辺りはdocumentに詳しく書かれているのでそちらを参照してください。
Shopifyのマルチパス機能に対応していないSNSログインアプリは使ってはいけない

ShopifyでSNSログインできるアプリを作ろうと思って調べていたら、ん？と思うことがあったので記事にしました

# ShopifyでSNSログインを実現するのはどうするか
Plusプランで使えるマルチパスという機能を使うことで実現可能です（月額2000ドルしますが...）

https://www.shopify.com/jp/plus/pricing?_bt=627726282453&_bk=shopify%20plus&_bm=e&_bn=g&_bg=132661213862&gclid=CjwKCAiAqt-dBhBcEiwATw-ggJ1mO4WrZZ8EjnsLn_pAUbMSUzqXMadgaEXGTgv8J9rqwRcReGPM5RoCdB8QAvD_BwE&gclsrc=aw.ds

例えばこのアプリ

https://apps.shopify.com/socialplus?locale=ja

ADVANCEDプランに
> ▼Shopify Plusでご利用可能になる追加機能です▼
ソーシャルログインで簡単登録

とあります
他のプランはあくまで「会員IDとLINE IDを連携」という表現です

:::note warn
つまりPlusプランに入る以外に**安全に**SNSログインを実現することはできません
:::

# でもPlusプランじゃなくてもSNSログインできるアプリが普通にある

Shopifyのアプリストアを見ると、プランに関わらずSNSログインできるアプリが存在します
例えばこの辺

https://apps.shopify.com/oxi-social-login?locale=ja

https://apps.shopify.com/open-signin?locale=ja

https://apps.shopify.com/one-click-social-login?locale=ja

これらは大体以下のような流れでSNSログインを実現しています
```
1. ショップのSNSログインボタンクリック
2. 別ウインドウでSNSログイン画面表示
3. SNSログイン後、アプリ側にアカウントを作成してSNSのIDと紐付ける。この時メールアドレスはSNS情報のメールアドレス、パスワードはランダムな文字列で生成
4. shopify apiで上記アカウントを作成
5. 別ウインドウの中で、visibility hiddenのshopifyログイン画面（フォームが非表示になっているログイン画面）に遷移し、javascriptで上記メールアドレスとパスワードでフォームが非表示になっているログイン画面をポストする
6. 成功したらログインしていたウインドウを閉じて、ショップ側の画面をリロードする
```
つまり、Shooifyのログインの仕様が変わったら(例えばreCAPTCHAが必須になったとか)突然ログインできなくなる可能性もあります
そもそも、アプリ側で平文のパスワードを持っているということになるので、セキュリティ的に問題があります
これらのアプリを普通におすすめアプリとして紹介してるブログ等もありますが、使うべきではないと思います

# ちなみに

上記アプリは公開されてから何年も経ってますし、どういう見解なのかShopifyに聞いてみました
![スクリーンショット 2023-01-07 16.25.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322555/43f8d56e-2b42-8151-d4c9-373bb8dfd816.png)
とのこと、なんともモヤモヤしますが、良くないことは認識しているよう
アプリ使用停止したら既存ユーザーがログインできなくなってしまうからでしょうか
Rails7 - rails console ( pry-rails ) で何故かActiveModelのデータ(インスタンス情報)が表示されない

# 問題

昔のバージョンではこれで色々データが表示されてたはず

```rb
User.last
=> #<User:0x4420>
```

# 解決

とりあえずattributesで表示できた

```rb
User.last.attributes
# => {"id"=>3,
#  "email"=>"example@example.com",
#  "remember_created_at"=>nil}
```

# その他

to_json も試したが何故か引数が必須になっている
only 指定は動くが except 指定が動かない

```rb
User.last.to_json(only: [:id])
# => "{\"id\":3}"
```


```rb
User.last.to_json(except: [:id])

#  ArgumentError: wrong number of arguments (given 0, expected 1)
```

# 環境


Rails 7.0.4


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
【iOS 16.x】シミュレータの言語を変えようとするとハングアップする

# はじめに
XCodeのSimulatorで言語設定を変えようとしたら、設定が反映されず、「言語を設定中」なままになってしまったので、直し方ないかと探したら、すぐに見つかったので、備忘録として
![スクリーンショット 2023-01-07 16.07.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/cd468ee6-a551-fbe7-506c-70b20aec914a.png)

# 更新履歴
2023.1.7 初回投稿

# 環境
- macOS Monterey 12.5.1
- Simulator:Version 14.1 (986.5)
- XCode：Version 14.1

# 参考にしたサイト
https://seishin.me/fix-ios-16x-simulator-hangs-on-language-change-issue/

# 手順
1. XCodeを起動し、Window>Devices and Simulatorsを選択し、該当のSimulatorのUDIDをコピーする
![スクリーンショット 2023-01-07 15.42.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/f5fc543c-e7f8-7383-ad44-50e1f19a76e9.png)

2. Simulatorの設定plistファイルをターミナルで開く
    ```
   open ~/Library/Developer/CoreSimulator/Devices/[SIMULATOR_UDID]/data/Library/Preferences/.GlobalPreferences.plist
    ```
3. plistが開くので、「AddRow」で「AppleLocale」と「AppleLanguages」を追加する
![スクリーンショット 2023-01-07 16.05.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/9961/27e0f9d6-99be-d9b0-3569-9680f17e7d61.png)

4. AppleLanguagesはTypeをArrayにし、Item 0のValueに「ja_JP」(日本語の場合)、AppleLocaleはTypeをStringのままで、Valueに「ja_JP」を入力


4. plistを閉じて保存して、Simulatorを再起動させればOK

以上
塩パスワード

# 塩パスワードとは

個人的にやってるパスワード管理方法です。（造語）

「セキュリティと可用性を両方保ちたいなあ」と思って考えました。

# 塩パスワードのやり方

## 塩を作る

まず自分にとって覚えやすい（自分だけが知っている） **『文字列改変ルール』** を頭の中に1つ作ります。

例えば…

* 先頭に『lycoris-recoil』を付ける。
* 先頭から3文字分消して、末尾に『yuruyuri』を付ける。

などです。

この **『自分だけが知っている文字列改変ルール』** が **『塩』** となります。

ちなみに『塩』という名前は『パスワードからハッシュ値を算出する時のソルト』からとってきました。

https://medium-company.com/salt/

## パスワードは塩を入れて作る

例えば特定サイトのアカウントを新規作成する時についてです。

まず、ランダムで長くて複雑な文字列を用意します。
仮に用意した文字列が『2-H907g9K--F』とします。

この文字列に『塩』を入れます。
仮に『塩』が『先頭にlycoris-recoilを付ける』だとします。

『塩』を入れた後の文字列は『lycoris-recoil2-H907g9K--F』となります。
これが『正式なパスワード』となります。

## 控えておきたい時は

パスワードを紙やデータで控えておきたい時は…
```『サイト名』『2-H907g9K--F』```
のように **『塩を入れる前の文字列のみ』** にします。

仮にこのパスワードの控えが漏れたとしても、
```『サイト名』『2-H907g9K--F』```
という情報から『正式なパスワード』を導き出される可能性はかなり低いかと思われます。

なぜなら **『塩を入れないと正式なパスワードにならない』** し **『その塩は本人の頭の中にしか存在しない』** から。

# 塩は使いまわし

基本的に『塩』1つだけ作って、使いまわします。
サイトごとに『塩』を作っていたら「このサイトってどういう塩でパスワード作ったっけ？」という面倒が発生していまう為です。

# 塩を作る時のポイント

## 塩はメモしない

塩をメモしたら『その塩が漏れたら』という考慮が発生して、いたちごっこが始まってしまいます。
なので **『塩は自分の頭の中で作って、どこにもメモしない』** のがポイントになります。

## 何かしらの文字列を追加する

『先頭1文字だけ除去』という簡単なルールだと、試されて破られる可能性があります。
『先頭3文字を切り取って末尾に追加』というルールも、色んなアナグラムをされるうちに破られる可能性があります。

**『パスワード控えの中に存在しない（推測が難しい）何かしらの文字列を追加する』** という要素がルール内にあるとよいです。
「その文字列は一体何だ？」というところから解読（ハッキング）がスタートするので、破られる可能性が低くなります。

## 他人が取得できる情報を追加文字列に使わない

『誕生日を末尾に追加する』といったルールだと、試されて破られる可能性があります。

# パスワード使いまわしはダメ

どんなに複雑なパスワードを設定しても、いろんなサイトで使い回しはダメです。
どこか1サイトでもパスワードが漏れたら、芋ずる式に他のサイトにもログインされてしまいます。

これは塩パスワードとかそれ以前の問題かと思います。
Amplifyで設定するAppSyncの証明書期限の更新方法

## 概要
Amplifyのチュートリアルでバックエンドの構築して数日経つとバックエンドとの通信ができなくなっていました。どうやら、Amplifyで設定するAppSyncの証明書の期限がデフォルトで7日間となっており、7日を過ぎるとAPIコールができないということが、この原因でした。なので、今回は証明書の更新方法を記載します。

## 手順
早速、AppSyncの証明書の期限更新手順を記載します。まず、Amplifyで設定したデフォルトのGraphQL(AppSync) APIキーの有効期限は7日間です。私は、DBから値が取って来れなくなっていたので、その原因を調べていたところ、APIコール時にUnauthorizedExceptionというエラーが返ってきていることに気がつきました。AWSマネージメントコンソールからAppSyncのAPIを確認しても証明書の期限が切れていることがわかるので確認してみてください。

#### 更新手順
以下のコマンドでAPIキーの証明書期限を更新できます。
コマンドを実行するディレクトリは、色々amplifyとかを設定したディレクトリです。
```terminal
amplify api update
```
色々と出力がコンソールに返ってきますが、下の問いがでたら、日数を入力してください。
そのまま何も入力せずにEnterを押すとデフォルトで7日間に設定されてしまいます。
```terminal:
✔ After how many days from now the API key should expire (1-365):ここに数字を入れる
```
これで更新が完了したので、APIコールしてみてください。動いたかと思います。
また、この証明書の更新方法だと最大でも365日で期限切れになり、その度に手動での更新になってしまうので、どのようにこれを管理すれば良いかは別途考える必要がありそうです。

## おわりに
- 今回は、Amplifyで設定するAppSyncの証明書期限が7日で切れてしまうということで、その証明書の更新方法について記載しました。
- とあるプロジェクトで必要なので、AppSyncを勉強中です。
GASのgetThumbnail()がうまく行かなくて苦労した

最近サムネイルを取得するコードをGASで書く機会があったのですが、何度もエラーに悩まされたので、解決方法（**正直なんでうまく行っているのか明確にはわかっていないが**）を備忘録的に残しておこうと思います。

同じエラーに悩んでいる人の解決につながれば幸いです。

# やりたかったこと
①スプレッドシート上に記載のあるダウンロード可能な動画へのURLを取得
②ダウンロードした動画を、動画用のGoogle Driveに保存して、そのままその動画のサムネを取得
③取得したサムネ画像をサムネ画像用のフォルダに保存

実際に記載したコードはこちら。

```javascript:コード
function getVideos() {
  var sheetName = "シート名";
  var ss        = SpreadsheetApp.getActiveSpreadsheet();
  var sheet     = ss.getSheetByName(sheetName).getDataRange().getValues();

  var lastRow = ss.getLastRow();

  var video_url_cell  = "動画のURLが記載されている列番号";
  var video_name_cell = "設定したい動画名が記載されている列番号"
  
  for(let i = 1; i < lastRow ; i++){
    var video_url  = sheet[i][video_url_cell];
    var video_name = sheet[i][video_name_cell];

    downloadVideo(video_url,video_name);
  }
  getThumbnail();
}

function downloadVideo(url, name){
  var responce = UrlFetchApp.fetch(url);
  var fileBlob = responce.getBlob().setName(name);

  // 動画を保存したいフォルダのID
  var folderID = "###################";

  var folder = DriveApp.getFolderById(folderID);
  var file   = folder.createFile(file);
}

function getThumbnail(){
   //動画が格納されているフォルダを取得
   var videoFolderID = "###################";
   var videoFolder   = DriveApp.getFolderById(videoFolderID);
   
   //サムネイルを格納するフォルダ取得
   var thumFolderID = "###################";
   var thumFolder   = DriveApp.getFolderById(thumFolderID);
   
   var files = videoFolder.getFiles();
   
   while(files.hasNext()){
     var file    = files.next();
     var imageId = file.getId();
     
     let newFile   = DriveApp.getFileById(imageId);
     var thumbnail = newFile.getThumbnail();
     var fileName  = newFile.getName();
     
     thumFolder.createFile(thumbnail).setName(fileName);
    }
}
```
```terminal:エラー内容
Exception: Argument cannot be null: blob
```

こんな感じで何度やっても上記のエラーが出てきました。
「**URLから動画を保存→サムネ取得を一回の実行でやるとエラー**」が起きるのですが、
**getVideos()とgetThumbnail()を別々で実行するとうまくいきました**。

# 解決策
**動画の保存とサムネの取得のコードを分けて、それぞれ別で実行する。**


# まとめ

めちゃくちゃ妄想ベースで考えると、**DriveAppのcreateFileって、一度処理をクローズしないとC言語のfclose的な操作がされないのでは？** とか思っていました。
そのため、プログラム実行上はまだ確定したファイルになっていなくて、nullと表示されてしまうとか…

ちょっとDriveAppの仕様までたどる元気がなかったので、いったんこの疑問はタンスにしまっておきます。
なにか知っている方は教えていただけますと幸いです（白目）
kotlinならStringBuilderよりもbuildStringを使おう

# kotlinで文字列連結する場合
Javaでもkotlinでも文字列連結は+でもできますが、回数の多い繰り返しの中で処理するのは効率がよくありません。
Javaであれば、StringBuilder（か、StringBuffer）を使います。kotlinでもこれらが使えますが、標準関数であるbuildStringを使ったほうが、よりkotlin的な書き方ができます。

```kotlin:StringBuilderの場合
        val str = StringBuilder()
        for(  i in 1..10) {
            str.append(i)
            str.append("\n")
        }
        println(str.toString())
```
StringBuilderの場合はstrはStringBuilder型です。最後にStringに直すにはtoString()が必要です。
for { ・・・ }ループとStringBuilderのnewが同列に並んでいます。

```kotlin:buildStringの場合
        val str = buildString {
            for (i in 1..10) {
                append(i)
                append("\n")
            }
        }
        println(str)
```
buildStringの場合、strはString型です。
for { ・・・ }のループが、buildStringの引数の関数、{ ・・・ }の中に入ります。
また、
```kotlin
    append("あ", "い", "う") // 複数可能
    appendln("おおおおお") // 改行付
```
と言った使い方もできます。
ERC20とERC721を1からわかりやすく解説


## はじめに

今回は「**ERC20**」、「**ERC721**」について1からわかりやすく解説していきます！

「**ERC20**」がわからなかったり、なんとなく理解している非エンジニアやこれから「**ERC20**」を理解しようとしているエンジニアの方向けの記事になります。

丁寧に解説している分だいぶ長くなってしまっているので時間があるときに一気に読んでみるのをお勧めします。

## ERC20とは

### 統一されたトークン規格

ERC20とは、「**Ethereumブロックチェーンで動作する統一されたトークン規格の1つ**」です。

「**統一されたトークンの規格**」というのは、「**共通のルールを持っているトークン**」という事です。

共通のルールを持っていないと、トークンごとにウォレットが必要になります。

新しいトークンを触るごとにウォレットを作っていては、だいぶめんどくさいですよね。

そこでERC20というトークン規格に沿って設計することで1つのウォレットで管理できるようになります。

また、ERC20は「**代替性トークン**」とも呼ばれています。

「**代替性トークン**」とは、あるトークンと同じ価値を持つ他のトークンと交換できるということです。

反対に交換できないのでNFTを代表する「**非代替性トークン**」です。

### ICOへ参加しやすくなる

ICOとは、「**イニシャル・コイン・オファリング（Initial Coin offering ）**」の略称で、仮想通貨を新規発行して資金調達する方法の1つです。

手順としては以下になります。

1. あるプロジェクトが新規仮想通貨の開発目的や方針を公開。
2. 賛同する投資家や支援者が、既存の仮想通貨で新たに発行された仮想通貨を購入する。
3. プロジェクト側は集めた資金でプロジェクトやサービスの開発を行う。
4. 投資家や支援者はプロジェクトが提供するサービスを利用したり、仮想通貨の価格上昇により資産を増やすことができる。

これがICOです。

では、これがERC20にどう関係してくるのでしょうか？

先ほども述べたようにERC20は「共通のルールを持っているトークン」という特徴を持っています。

それによりウォレットを新たに作る仮想通貨ごとに用意する必要がありません。

そのため、既存のウォレットを使用できるため、ICOに参加するハードルが下がります。

ちなみにERC20を扱えるウォレットとして代表的なものとして、「[Metamask](https://metamask.io/)」や「[Trust Wallet](https://trustwallet.com/ja/)」、「[MyEtherWallet](https://www.myetherwallet.com/)」などがあります。

### 誰でも作成可能
ERC20トークンは誰でも作ることができます。

そのため現在存在するERC20トークンの種類はめちゃくちゃ多いです。

https://etherscan.io/tokens

上記から現在のERC20トークンの種類を確認できるのですが、現在（2022年12月31日）では73万5000種類のERC20トークンがあります（年末も年末にこの記事を書いています）。

### 追加機能を実装可能
この章の前半で説明したように、ERC20には統一された規格があります。

これはあくまで最低限の機能は統一された規格で設計されています。

そのため、トークンごとに追加機能を実装することが可能です。

新規発行するERC20トークンごとに以下のように役割が異なってくるため、役割に応じて追加機能を実装することが可能になります。

#### ステーブルコイン
ドルや日本円などの法定通貨や市場で取引されるコモディティ（金やプラチナなど）などの価格に連動するように設計された暗号通貨。

#### DeFiトークン
分散型金融（DeFi）プロトコルを支援するトークン。

スマートコントラクトで管理されているため中央管理者が存在しない。

仮想通貨を預け入れて利息を得たり、流動性を提供して独自トークンを得る、仮想通貨を貸し出して利回りを得るなどして稼ぐことができる。

#### ユーティリティトークン
特定のコミュニティやサービスなどを利用するにあたり実用性があるトークンのこと。

具体的には以下のような用途が想定される。

- サービス内で物を購入したり利用料を支払う。
- 利用者の行動に対してインセンティブを支払う。
- 所有することでコミュニティの意思決定に関われる。
- 保有している量や期間に応じて報酬が発生する。

さらに詳しく見るには以下の記事を参考にしてください！

https://chaldene.net/erc20

https://chaldene.net/erc20-deposit-widthdraw

## ERC721とは

この章ではERC721の特徴について紹介していきます。

ERC721は「**非代替性トークン**」です。

「**非代替性トークン**」について調べると以下のような定義がされていました。

> NFT（非代替性トークン）とは、ビットコインやドル紙幣のように全く同じ価値を持つ "代替可能 "な資産ではなく、それぞれが固有のものである特殊なトークンのことを指します。NFTは1つ1つが固有のものであるため、美術品や録音物、仮想現実の不動産やペットなどのデジタル資産の所有権を認証するために使用できます。

https://www.coinbase.com/ja/learn/crypto-basics/what-are-nfts

1つ1つがユニークなトークンであるため、交換することができないということです。

NFT（Non Fungible Token）と呼ばれるものこそ、まさに「**非代替性トークン**」のことです。

さらに詳しく見るには以下の記事を参考にしてください！

https://chaldene.net/erc721

## 最後に

今回は「**ERC20**」と「**ERC721**」について解説していきました！

だいぶ長かったので、最後まで読んでいただきありがとうございます。

そしておつかれさまです！

もし何か質問などがあれば以下のTwitterなどからDMしてください！

https://twitter.com/cardene777
シェル芸（Bash）で『競技プログラミングの鉄則』1章の問題を解く

# はじめに

米田優峻さんの書籍『競技プログラミングの鉄則』を購入したので、シェル芸（Bash）を用いて問題を解いていこうと思います。ただし、Bashは処理速度がCやC++などと比べるとそこそこ遅いため、解けない問題はC++で解きます。また、応用問題まで解いていると大変なので、今のところ応用問題は飛ばすことにします。よろしくお願いいたします。

# Amazonリンク

https://amzn.asia/d/2j7oJR0

# 問題集（AtCoder）

https://atcoder.jp/contests/tessoku-book

# 序章 : 競技プログラミング入門

「競技プログラミングとは何か」ということや、コンテストの種類、本書の構成などが書かれている章です。また、GitHubにソースコードも公開されているとのことです。参考にしましょう。さらに、AtCoder上で自動採点も行えるとのことです。利用しましょう。

# 1章 : アルゴリズムと計算量

アルゴリズムと計算量についての説明があります。読みましょう。

## 1.1 : 導入問題

まずは簡単な問題から始まります。

### A01 - The First Problem

`read`で標準入力から値を受け取り、`$(())`の中で計算、`echo`で標準出力しましょう。

```bash
read n
echo $((n*n))
```

## 1.2 : 全探索（1）

全探索問題を解いていきます。

### A02 - Linear Search

問題の流れとしては全探索を行う処理を書くべきですが、Bashには`grep`があるので使っちゃいましょう。まず、入力の値が一行になっているので`sed`でスペースを改行に置換します。その後、`X`の値を`grep`で探します。最後に三項演算子的な感じで`Yes` or `No`を出力します。

三項演算子的な処理は以下のように書けます。

```bash
コマンド && 最初のコマンドが正常終了時に実行するコマンド || 最初のコマンドが異常終了時に実行するコマンド
```

参考記事↓

https://orebibou.com/ja/documents/shellgei/

提出コードは以下の通りです。

`grep -q`は`@kkdd`さんのコメントを参考にいたしました。ありがとうございます！

```bash
read n x
sed 's/ /\n/g' | grep -q ^$x$ && echo Yes || echo No
```

## 1.3 : 全探索（2）

引き続き全探索の問題を解いていきましょう。

### A03 - Two Cards

Bashでは標準入力を配列に入れたいとき、以下のように書きます。

```bash
declare -a p=()
read p
```

参考記事↓

https://qiita.com/terra_yucco/items/63133e3ce24ae6f21f2d

提出したコードは以下の通りです。二重のfor文を使って全通りの足し算を行い、`echo`で出力します。そして、その出力の中から`grep`で`K`の値を探して、`Yes` or `No`を出力します。

```bash
read n k
declare -a p=() q=()
read p
read q
for i in ${p[@]};do
  for j in ${q[@]};do
    echo $((i+j))
  done
done | grep -q ^$k$ && echo Yes || echo No
```

## 1.4 : 2進法

2進法に関する問題を解いていきます。

### A04 - Binary Representation

`bc`を使って10進数を2進数に変換しましょう。

参考記事↓

https://www.mazn.net/blog/2013/02/24/854.html

`obase=2`で出力を2進数に指定、`ibase=10`で入力を10進数に指定します。その後に`N`の値を`bc`に渡します。また、2進数の値を10桁で出力する必要があるので`printf`を使ってあげましょう。

```bash
read n
printf '%010d\n' $(echo "obase=2;ibase=10;" $n | bc)
```

## 1.5 : チャレンジ問題

1章の最後に少し難しいチャレンジ問題があります。

### A05 - Three Cards

この問題では計算量が`O(N^2)`となる解法が紹介されていますが、残念ながらBashではそれでも間に合いません。

以下が`TLE`するコードです。

```bash
read n k
a=0
for((i=1;i<=n;++i));do
  for((j=1;j<=n;++j));do
    d=$((k-i-j))
    if((d>=1&&d<=n));then
      a=$((a+1))
    fi
  done
done
echo $a
```

仕方がないのでBash(C++)で解いていきましょう。

```bash
cat << 'EOF' >> a.cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
  int n, k, a=0;
  cin >> n >> k;
  for(int i = 1; i <= n; ++i) {
    for(int j = 1; j <= n; ++j) {
      int d = k-i-j;
      if(d >= 1 && d <= n) ++a;
    }
  }
  cout << a << endl;
}
EOF
g++ -o a a.cpp
./a
```

純粋なBashでも解ける方法があれば、教えていただけますと幸いです。

# おわりに

本記事は、随時更新していきます。

シェル芸と『競技プログラミングの鉄則』とAtCoderとAtCoder Problemsに感謝を。

ありがとうございます。

楽しいです。

# コメントいただいた改善点

`@kkdd`さん（2023/01/09）

- `grep -q`で不要な出力を無しに
- 三項演算子的な構文についての正確な説明

# ブログの移転

新しくブログを始めました。AtCoder関連の記事もあります。

https://yusukekato.jp/20230420.html
Linuxmint(ubuntu)でIKEv2方式のVPNに接続する


## 目次
- [VPNサーバーの準備](#VPNサーバーの準備)
- [IKEv2利用の準備](#IKEv2利用の準備)
- [IKEv2の設定をする（ここが罠）](#IKEv2の設定をする（ここが罠）)
- [あとがき](#あとがき)

***
Linux mintいいですよね？その辺で2~3万で買えるSkyLakeのCore i5の端末が２〜３万円で転がっているのでそれに載せて遊んでいます。
しかし、外出中にメインPCにアクセスしたくなったので、VPNを構築してRemminaでRDP環境を構築していきたいと思います。(Remminaの使い方はそのへんにあるのでググってください)
IKEv2接続するに辺り、持病のADHDが発症して躓いたため、覚書を記しておきます。

## VPNサーバーの準備
1. はじめに
私はルーターにVPSサーバー機能があったのでそちらを使いました。
(ASUS RT-AX3000)

2. プロトコル
サーバーのプロトコルですが、基本的にはOpenVPNかIKEv2あたりにしましょう。今回はIKEv2にしています。
接続する端末の相性(AndroidはIKEv2あまりうまくいかない)や主義思想の兼ね合いによって好きなのにするといいです。
<seq>VPNサービスを利用しようとしてるけどIKEv2で構築してしまったので、そっちはまた後日</seq>

また、ルーターが二重ルーターなっていないかなど確認しておいてください。
ASUSでの例ですが、このような感じにVPNを設定してください。
(IPSec VPN/IKEv2の設定)
[OpenVPN](https://www.asus.com/jp/support/FAQ/1044397/)

3. メモ
ドメイン名(サーバーIPアドレス)や事前共有鍵（PSKの場合）、ユーザ名、パスワードは覚えておいてください。
このとき証明書も取得しておいてください。(.pemの拡張子)

## IKEv2利用の準備
### strongswan導入
strongswanと同ネットワークマネージャー拡張をインストール
```
sudo apt install strongswan network-manager-strongswan
```
**Debianではパッケージが用意されています。`network-manager-strongswan`**
<seq>参考：https://scienceboy.jp/88io/2020/10/ikev2-vpn/</seq>


ここまでできたら、右下のネットワークマネージャー（インターネット設定）よりIKEv2の設定ができるようになります。
・・・が、認証方式がPre-shared keyの場合気をつけることがあります。
(ここから本編)

## IKEv2の設定をする（ここが罠）
### Pre-shared Key以外
ダイアログの通りにやればできる、知らんけど
addres: サーバIP
Certificate： 取得した(生成した)証明書(.pem)
username: 
passsword: 

### Pre-shared Keyの場合
**事前共有鍵やpasswordの桁数が少ないとダイアログでは設定できないようです。**←ポップアップメニューに書いてあってすごい当たり前だった
![[PSK_cant_pushing.png]]
password: PSKには20桁は必要です。とのこと

- じゃあどうするか？
手入力でやりましょう。
とはいったものの、筆者は何度か躓いてここまできたので、<u>**EAPで一旦事前共有鍵以外の情報を入力し、/etc/ipsec.secretsに事前共有鍵を追記**</u>しています。

**/etc/ipsec.conf**の中身
leftid=`PUBLIC_IP`はサーバIPアドレス，ドメインの場合は`@example.com`のように先頭に`@`をつけるのを忘れないこと。
```
conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=PUBLIC_IP
    leftcert=server-cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=192.168.1.0/24
    rightdns=8.8.8.8,8.8.4.4
    rightsendcert=never
    eap_identity=%identity
    ike=chacha20poly1305-sha512-curve25519-prfsha512,aes256gcm16-sha384-prfsha384-ecp384,aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024!
    esp=chacha20poly1305-sha512,aes256gcm16-ecp384,aes256-sha256,aes256-sha1,3des-sha1!
```

**/etc/ipsec.secretsへ追記**
PRE-SHARED_KEYに事前共有鍵を入力
```
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

~~~

%any : PSK "PRE−SHARED_KEY"

~~~
```

参考
- https://yryr.me/linux/ubuntu-20-04-1-lts/strongswan-ipsec-ikev2-vpn-server.html
- https://wiki.strongswan.org/issues/2620


## あとがき
設定してから日が経ってしまったので、あまり何をどうしたかは覚えていない。
~~というか、PSKから設定流用してIKEv2として接続できるのか理解できておらず、もやもやしている。
逆に新しくユーザ追加してパスワード要件満たしたにも関わらず、接続できないのもわからん、とりあえず今日はここまで~~
この記事を読まれて、バッカモーンって怒鳴り付けてこられるかたいらっしゃるならご教示ください。
至らぬところは前向きに直します。
とりあえず、致命的な問題でなければ作業を進めたいのでこのままにします。
対戦よろしくおねがいします。
ビルドとは


# ビルドとは

ビルドとは、プログラムをコンピュータが実行できる状態にする作業です。

具体的には、コンパイルとリンクです。


# コンパイルとは

コンパイルは、プログラミング言語（人間が理解できる言語）を機械語（01の言語）に変換することです。

コンピュータ（CPU）は機械語のプログラムしか実行できませんのでコンパイルが必要です。

コンパイルをするためのソフトウェアをコンパイラと言います。コンパイラはプログラミング言語ごとに異なります。


# リンクとは

リンクとは、コンパイルされたファイルをまとめることです。

開発時に１つのファイルにプログラムを書いていくとするととんでもない行数になってしまいますので、複数のファイルに分けて書いていきます。プログラムの実行時にはそれらをまとめて実行するというイメージです。

リンクを行うソフトウェアがリンカです。


# まとめ

ビルドとは、コンパイルとリンクを行ってプログラムを実行できる状態にすることです。
【Kotlin Server】開発モードを有効にする方法

# はじめに
明けましておめでとうございます。
新年一発目の記事ですが、最近はサーバー側のお手伝いをする機会が多く、後に思い出せるよう設定をまとめていきたいと思います。
今回は開発モードを有効にする方法をまとめます。

# 開発モードを有効にする方法
デバッグモードを有効にする方法は二つありますので、両方紹介します。

一つ目はapplication.confに記述する方法です。

```application.conf
ktor {
    development = true
}
```
application.confはresources直下に作成する設定ファイルですが、こちらに記述することで有効にできます。
次に、gradleに記載する方法です。

```build.gradle
application {
    applicationDefaultJvmArgs = listOf("-Dio.ktor.development=true")
}
```

以上になります。
両方同じ結果になるはずなので、お好きな方で記述するのが良いかと思います。
注意点として当然ではありますが、あくまで開発モードなので、本番には有効にならないよう注意が必要ですね。

# さいごに
年明け早々全力疾走しています、このまま今年は駆け抜けたいと思います！
Rails7 で deviseが何かと変


# 問題

サインインではエラーが起こるたびエラー文が増える

![image](https://user-images.githubusercontent.com/13635059/211134046-6b3c348f-1b79-4546-abe2-0ebae10843e1.png)

サインアップではそもそもエラーが表示されない

![image](https://user-images.githubusercontent.com/13635059/211134041-fbf99cd7-bd26-491b-99ba-8f9b8eead855.png)

サインアップに成功してDBにはユーザーが登録された場合でもエラーが起こる

```
undefined method `user_url'
```


参考

https://abillyz.com/vclbuff/studies/460

# 解決

どうやらHotWireと「相性」が悪いらしい
Viewの送信ボタンで無効化することで従来のdevise画面の挙動に戻った

```erb
  <div class="actions" data-turbo="false" >
    <%= f.submit "Sign up" %>
  </div>
```

```erb
  <div class="actions" data-turbo="false">
    <%= f.submit "Log in" %>
  </div>
```



# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
Font Awesomeのクイックスタートガイド

## 概要
- [Font Awesome](https://fontawesome.com)はweb等で使用できるアイコンを提供してくれるサービス。
- [Font Awesome](https://fontawesome.com)のアカウント作成法と簡単な使用法を説明する。

## アカウントを新規に作成して、アイコンを使用する。
[Font Awesome](https://fontawesome.com)をブラウザで開き、「Start for Free」をクリック
　　　↓
メールアドレスを入力して、「Send Kit Code」をクリック
　　　↓
メールが届くので、「Confirm Your Email Address」をクリック
　　　↓
ブラウザが開くので、パスワードを入力して、「Set Password & Continue　→」をクリック
　　　↓
姓名、使用開始年、お気に入りアイコンを入力して「All set. Let’s go!　→」をクリック
　　または
「No thanks. Let’s skip this step for now」をクリック
　　　↓
①にスクリプトが表示されるので、「Copy Kit Code!」をクリックしてコピー
　　　↓
コピーしたスクリプトをhtmlファイルの`<head>`と`</head>`の間にペースト
　　　↓
②の検索窓で、例としてdogを検索すると犬に関連するアイコンが表示されるので、どれかをクリック
　　　↓
右上にHTMLのコードが表示されるので、クリックしてコピー
　　　↓
コピーしたスクリプトをhtmlファイルの`<body>`と`</body>`の間にペースト

ここまでの作業を行ったindex.htmlの例を以下に示す。

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample of index.html</title>
    <script src="https://kit.fontawesome.com/xxxxx.js" crossorigin="anonymous"></script>   <!-- xxxxxはアカウントごとに異なる -->
  </head>
  <body>
    <p>Hello ICON!</p>
    <p><i class="fa-solid fa-dog"></i></p>   <!-- 犬のアイコンを挿入 -->
  </body>
</html>
```

## 既存のアカウントで、アイコンを使用する。
[Font Awesome](https://fontawesome.com)をブラウザで開き、右上の「Sign In」をクリック
　　　↓
メールアドレスとパスワードを入力して、「Sign In」をクリック
　　　↓
「Your Kits」をクリック」
　　　↓
左上に表示されているKit Code（例えば、1a2b3c45deなど英数字のコード）をクリック
　　　↓
①にスクリプトが表示されるので、「Copy Kit Code!」をクリックしてコピー
　　　↓
コピーしたスクリプトをhtmlファイルの`<head>`と`</head>`の間にペースト
　　　↓
②の検索窓で、例としてdogを検索すると犬に関連するアイコンが表示されるので、どれかをクリック
　　　↓
右上にHTMLのコードが表示されるので、クリックしてコピー
　　　↓
コピーしたスクリプトをhtmlファイルの`<body>`と`</body>`の間にペースト

ここまでの作業を行ったindex.htmlの例を以下に示す。

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample of index.html</title>
    <script src="https://kit.fontawesome.com/xxxxx.js" crossorigin="anonymous"></script>   <!-- xxxxxはアカウントごとに異なる -->
  </head>
  <body>
    <p>Hello ICON!</p>
    <p><i class="fa-solid fa-dog"></i></p>   <!-- 犬のアイコンを挿入 -->
  </body>
</html>
```

## アイコンのスタイルを変更する。
アイコンのサイズを変更する。　※[こちら](https://fontawesome.com/docs/web/style/size)を参考にした。

```html
    <p><i class="fa-solid fa-dog fa-2xl"></i></p>   <!-- アイコンを大きくする -->
```

アイコンに動きを与える。　※[こちら](https://fontawesome.com/docs/web/style/animate)を参考にした。

```html
    <p><i class="fa-solid fa-dog fa-flip"></i></p>   <!-- アイコンを反転させる -->
```

ここまでの作業を行ったindex.htmlの例を以下に示す。

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample of index.html</title>
    <script src="https://kit.fontawesome.com/xxxxx.js" crossorigin="anonymous"></script>   <!-- xxxxxはアカウントごとに異なる -->
  </head>
  <body>
    <p>Hello ICON!</p>
    <p><i class="fa-solid fa-dog"></i></p>   <!-- 犬のアイコンを挿入 -->
    <p><i class="fa-solid fa-dog fa-2xl"></i></p>   <!-- アイコンを大きくする -->
    <p><i class="fa-solid fa-dog fa-flip"></i></p>   <!-- アイコンを反転させる -->
  </body>
</html>
```
ALE が virtual-text に対応していました

[ALE](https://github.com/dense-analysis/ale) は NeoVim や Vim で動作する非同期でコードの Lint を行ってくれるプラグインです。ALE の v3.3.0 よりデフォルトで全行に対し virtual-text へエラーやワーニングのメッセージが出力されるようになりました。

https://github.com/dense-analysis/ale/releases/tag/v3.3.0

[Help](https://github.com/dense-analysis/ale/blob/a18472cc58b85ba4e037fe3ad4c3f525aef74a85/doc/ale.txt#L2291-L2301)を見るとデフォルトは全行表示になっています。
こんな感じになります。
![all.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960076/891e1235-8a9b-2f01-1f25-62840561a115.png)
現在行だけにしたい場合は `let g:ale_virtualtext_cursor = 1` を .vimrc や init.vim 設定します。
![current.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960076/7315cde5-23b6-4ce2-ab40-0e89ae2e1e42.png)
無効にする場合は `let g:ale_virtualtext_cursor = 0` に設定します。viretual-text の部分が表示されなくなっていることが分かります。
![disable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960076/db290589-757e-f89a-d1c4-659b627de8f4.png)

## 余談
↓の記事を参考にして [null-ls.vim](https://github.com/jose-elias-alvarez/null-ls.nvim)というプラグイン経由で [cspell](https://cspell.org/) というスペルミスを検知するツールを動かそうとしていました。そしてそのタイミングで ALE を今回の v3.3.0 にしてしまいました。

https://zenn.dev/kawarimidoll/articles/ad35f3dc4a5009

このとき、 ALE も cspell に対応しているので、ALE による virtual-text のメッセージと null-ls.vim による virtual-text のメッセージが両方表示されるという現象が起こりました。
下図の灰色のメッセージはALEによるもので赤色のメッセージが null-ls.vim によるものです。
![with_null_ls.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2960076/da54a1ac-4abe-442f-529e-acc66be544fc.png)

私は cspell のメッセージを vim に表示するには null-ls.vim を使うしかないと思い込んでいたのと、 ALE が最新版から virtual-text に対応していることを分かっていなかったので「なぜ、virtual-text が重複して表示されるんや？ null-ls.vim 消してもメッセージ表示されるのはなぜなんや？」状態になり一日の時間を溶かしました。
まぁ、いい経験にはなりました（笑）
サービスロールとは

## AWSサービスロールとは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733779/6bc2966f-70bd-39db-cc59-cea89a5c56c6.png)
IAMロール新規作成画面で、信頼されたエンティティを
「AWSのサービス」を選択したIAMロールのこと。

※IAMロール＝サービスロールと考えて問題ない。
GitlabへのSSH接続がTimeoutする場合の処方箋

# Issue

タイトル通り、SSHでGitlabへアクセスしようとしたら以下のようなTimeoutがでてアクセスできない。

```bash
ssh -vvv git@gitlab.com
OpenSSH_9.0p1, LibreSSL 3.3.6
debug1: Reading configuration data /Users/{user_name}/.ssh/config
debug1: /Users/{user_name}/.ssh/config line 6: Applying options for gitlab.com
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: include /etc/ssh/ssh_config.d/* matched no files
debug1: /etc/ssh/ssh_config line 54: Applying options for *
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/Users/{user_name}/.ssh/known_hosts'
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/Users/{user_name}/.ssh/known_hosts2'
debug1: Authenticator provider $SSH_SK_PROVIDER did not resolve; disabling
debug1: Connecting to gitlab.com port 20.
ssh: connect to host gitlab.com port 20: Operation timed out
```

# Investigation

上記の結果からもOpenSSHやLibreSSLのバージョンも確認してみたが古いわけでもない。
ということで使用しているconfigファイルを確認してみる。
しかし、[gitlab](https://docs.gitlab.com/ee/user/ssh.html#configure-ssh-to-point-to-a-different-directory)の設定方法を見ても特段まずい設定をしているわけでもない。
gitlab.comへのpingも通るけど通らないってことはネットワーク的にFirewallでブロックされているのではないかと困ったので公式ページのEngineerブログをあさってみる。

```bash
Host gitlab.com
  HostName gitlab.com
  User git
  Port 22
  PreferredAuthentications publickey
  IdentityFile /Users/{user_name}/.ssh/id_rsa
```

# Solution

[GitLab.com now supports an alternate git+ssh port](https://about.gitlab.com/blog/2016/02/18/gitlab-dot-com-now-supports-an-alternate-git-plus-ssh-port/) が解決してくれた。
ネットワークが監視されていて22番ポートでは接続ができない場合があるとのこと。
そんなときに解決する方法としてGitlabが提供している2番目のSSHサーバーを使うという方法だ。
変更点はPort番号とHostname。

```bash
ssh -i /Users/{user_name}/.ssh/id_rsa -p 443 git@altssh.gitlab.com
PTY allocation request failed on channel 0
Welcome to GitLab, {git_user_name}!
Connection to altssh.gitlab.com closed.
```

接続できた。ただし毎回上記のように設定するのはめんどくさいのでconfigファイルを以下のように変更する。

```bash
Host gitlab.com
  HostName altssh.gitlab.com
  User git
  Port 443
  PreferredAuthentications publickey
  IdentityFile /Users/{user_name}/.ssh/id_rsa
```

これで再度接続してみる。
```bash
ssh git@gitlab.com
PTY allocation request failed on channel 0
Welcome to GitLab, @{git_user_name}!
Connection to altssh.gitlab.com closed.
```

アクセスできた。git pullもcloneもできたので一件落着。
ただし、よく公式ページを見れば書いてあった。。。。
僕みたいな残念な人間が他に出ないことを願います。。。lol
[alternative-ssh-port](https://docs.gitlab.com/ee/user/gitlab_com/index.html#alternative-ssh-port)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169015/ad31f674-dcbd-9dce-52ae-6b4f3f1131de.png)
WinUI3でListViewを使った時のメモ

# やり方

XAML
```xml
<ListView x:Name="listView">
    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding text1}"></TextBox>
            <TextBox Text="{Binding text2}"></TextBox>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
```
C#
```C#
public class Lists
{
    public string text1 { get; set; }

    public string text2 { get; set; }
}

public static List<Lists> Llist = new List<Lists>();
public MainWindow(){
    ...
    Llist.add(new Lists{text1:"aaa",text2:"bbb"});
    Llist.add(new Lists{text1:"ccc",text2:"ddd"});

    listView.ItemsSource = Llist;
}
```
これでできます。自分が詰まったのでメモ
Windowsのリモートデスクトップ接続でマイクの音声が入らないときのトラブルシューティング

## 問題発生の経緯
リモートワークをしているなかで、会社支給の端末を自分が利用している端末で作業したいことがあります。机の上のスペースは限られているなかで、複数の端末を机の上に並べるのは、とても窮屈だからです（机のスペースの余裕は、脳のリソースに直接関係していると考えます）。
ある日、支給端末（リモート接続先）で会社の人と打ち合わせをしようとTeamsを開いて通話したところ、なぜか向こうに音声が伝わってないようでした。一回こちらから掛けなおす形で結局、そのときはリモート接続を切って支給端末を操作して難を逃れました。けど、何回もこんなことあったらたまらないよね、と思うのでここに書き残しておきます。

## 前提条件の確認
リモート接続で支給端末に繋いで設定したはいいものの「なぜかマイクの音がリモート先の端末に入らない」現象が起きました。どのような状況かというと、次のようなものです。
- リモートデスクトップ接続確認画面
![リモート デスクトップ接続 2023_01_07 14_13_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2621197/c7965042-76c6-423f-1f28-3e7379e6b4b6.png)
- リモート接続先の設定画面（サウンド）
![設定 2023_01_07 14_09_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2621197/155788ef-ccdf-5c08-c450-1643320985b1.png)
もしリモート接続におけるオーディオ設定が何もできてない場合は、以下を参照してください。
[リモートデスクトップ接続でマイクを認識させる方法 [Windows]](https://so-kai-app.sakura.ne.jp/blog/3448/2021/05/31/)

## トラブルシューティング
リモート接続元の設定も確認しましょう。
- リモート接続元の設定画面（サウンド）
![設定 2023_01_07 14_25_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2621197/aba29b82-4a51-0b53-82b0-ed81205e9eda.png)
この<b>マイク(Virtual Desktop Audio)</b>というのは、VRをリモートでプレイするときに利用する<b>Virtual Desktop Streamer</b>というアプリケーションで利用するオーディオインターフェースで、自分の場合はMeta Quest2を使っているので、それがマイクの入力インターフェースとなっています。なので、マイクの設定を次のように変えてあげることで、ちゃんとリモート接続元も入力インターフェースが整えてあげましょう。

- リモート接続元の設定画面（マイクテスト）
![設定 2023_01_07 14_33_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2621197/c5dbc720-ee0f-79a0-e2f8-98399e54d89d.png)

- リモート接続先の設定画面（マイクテスト）
![設定 2023_01_07 14_53_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2621197/e692df72-ecd0-98da-591d-2d5255f73102.png)



## まとめ
当たり前ですが、接続先と接続元でそれぞれの想定するオーディオインターフェースが設定できていることが前提となっていました（灯台下暗し）。もしリモート接続先の設定で困ったときは、必ずそれぞれの端末のオーディオ設定を、一度確認してみてください。そして、設定後はちゃんとマイクテストを行うようにしてください。