OmniAICreator/Qiita-1.07M huggingface dataset:
【Excel】TRUEは"TRUE"じゃないらしい

# はじめに
最近Excelを触り始めたのですが、ちょっと悩まされたので備忘録として…。
こんなん常識なのかもしれないので、何番煎じの記事かわかりませんが…。

# やりたいこと
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/d845e48f-a6bc-a3ed-7af0-f4f299c5be2c.png)
期待していた値と、DBに登録された値が合致しているかを見るだけの表！

ただし、登録日時は正確な期待値を作るのが難しいので、厳密には見ない！
そういうところは期待値のセルに"any"と入れて、
**期待値のセルが"any"だったら問答無用で"TRUE"**と表示
**それ以外なら、期待値と登録された結果を比較してTRUEかFALSE**を表示！

# やったこと１
つまり、こうだな！

```:検証合否のとこに入れる式
=IF(B2="any","TRUE",EXACT(B2,B3))
```

完成！
### 結果
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/ba56318d-57fb-5486-4748-6b4cfa73e75b.png)
…えっ…気持ちわる………。
期待値"any"のところだけ、**左詰め**になっています。

気持ち悪すぎる…（いやサクッと中央揃えにしちゃえばいいじゃんとか言わないで）
# やったこと２
何が悪さしているのかしばらく探っていたところ、この記事に出会いました。
[Excel Tips[論理値 - TRUEとFALSE - ] - Office TANAKA](http://officetanaka.net/excel/function/tips/tips59.htm)

先ほどの数式で"any"の時は**"TRUE"（文字列）**を返し、それ以外はEXACTで**TRUE（理論値）**を返していました。
だから表示のされ方が違ったんですね…。
これを解決するには、最初の式で"TRUE"としていた所を以下のように変更します。

```:検証合否のとこに入れる式
=IF(B2="any",TRUE(),EXACT(B2,B3))
```

[TRUE 関数 - Office サポート - Microsoft Support](https://support.microsoft.com/ja-jp/office/true-%E9%96%A2%E6%95%B0-7652c6e3-8987-48d0-97cd-ef223246b3fb)

### 結果
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/78dc169f-086d-9f9c-956e-de2e732cc6bb.png)

これで最初の画像のように全部中央揃えになりました！

# 最後に
ちょっと遊びました。こっちのほうが長いです。
セルに「false」と入力し、
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/81d2d4ca-5fac-4217-83f4-38437242eddf.png)
Enterキーを押すと
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/ec6f1e58-7bca-4db2-fe7a-befa90edb9a8.png)
勝手に大文字になりました！
これも、文字列じゃなくて理論値になっているようですね。

じゃあ、と気になって色々なFALSEを入力しました。
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/7a0675f2-f4b6-28c9-e1a8-91ad81e2da76.png)
これをCOUNTIFで見たらどうなるんだ…？
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/6a70e773-017a-1a91-afa5-b83d6f2d31f7.png)
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/31c3635a-b376-5b6b-a129-ed704b0ed5e8.png)
「2」…？
"FALSE"って書いたのに、そっちがカウントされるの…？
そして文字列の"FALSE"はカウントされないの…？
じゃあ…
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/17e1df67-1031-315f-e2e0-07f25384d4d2.png)
'FALSEを条件にすればどうだ…？
![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479415/9b81125f-1856-9c50-33dc-6c78619d62c3.png)
「0」
………文字列の"FALSE"をカウントするにはどうすればいいんだ…！！！

ググっても出てこないので実験した結果、__*（アスタリスク）を前に入れれば条件に引っかかるっちゃ引っかかる__
ということがわかりましたが、ほかにも引っかかってしまう文字列が出てきてしまうので、解決できているか怪しいですね…。
（せめて一文字なら妥協できんこともない、と、?FALSEにしてみましたがダメでした…）
（そもそもLEN関数で'FALSEの文字数を見たら5文字でした）

本当はこっちのほうが本編より調べたので本編にしたかったところですが、調べきれずに断念…。
いや、まあわざわざ文字列のFALSEをカウントしたい人はいないかもしれませんが…。
[Rails6]AWS本番環境でActionCableの使用ができるようにする

# はじめに
Rails6を使用して、AWSにデプロイするもActionCableで実装したチャットが一切機能しなくて困ったので、その時の解決方法を以下に記述します！

#結論

3点の設定が必要です。
1.cable.yml
2.production.rb
3.rails.conf(もしくはアプリケーション名.conf)

# cable.ymlの設定

cable.ymlとはActionCableを使用する際の設定を記述するファイルです。
ここでは、本番環境の仕様設定に編集を加えます。

###adapter(前提知識)
ActionCableは、サーバーから送信された情報をクライアントへ提供する仕組みになります。その仕組みと連携するためのものをadapterと言います。

使用できるadapterの種類には、以下表の3通りがあります。

|  種類  |  内容  |
| ---- | ---- |
|Asyncアダプタ|  無料で導入が可能だが、パフォーマンスの面から本番環境での利用は非推奨。ただしポートフォリオ程度なら問題ない   |
|Redisアダプタ|本番環境での利用が推奨されている。、EC2を用いたデプロイを行う際に、幾らか料金がかかる|
|PostgreSQLアダプタ|本番環境での利用が推奨されている。PostgreSQLを使用していることが条件|

以上を踏まえ、以下のように編集しましょう

```cable.yml
development:
  adapter: async
test:
  adapter: test
production:
  adapter: async # 追記
  # adapter: redis 削除
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  channel_prefix: mini_talk_app_production
```

#production.rbの設定

ActionCableは、指定されていないオリジンからのリクエストを受け付けない設定になっているはずです。
これでは使い物にならないので編集しましょう！

```config/environments/production.rb
Rails.application.configure do 

# 省略
#どっかに記述してOK
  ActionCable.server.config.disable_request_forgery_protection = true
  config.action_cable.url = "ws://【Elastic IP】/cable" 
  config.action_cable.allowed_request_origins = ['http://【Elastic IP】']

# 省略

end
```

#rails.confの編集

Nginxの設定ファイルにあたるrails.confを編集しましょう。
ActionCableを用いる場合、サーバーサイドへのリクエストを受け取る役目のあるWebサーバーにて、websocketの仕組みを用いるための設定をする必要があります。

```/etc/nginx/conf.d/rails.conf
upstream app_server {
  server unix:/var/www/アプリケーション名/shared/tmp/sockets/unicorn.sock;
}

server {
  #省略
# -------------------ここから-------------------
# /cableのパスに対してwebsocketの仕組みを用いるための設定
  location /cable {
    proxy_pass http://app_server/cable;
    proxy_http_version 1.1;
    proxy_set_header Upgrade websocket;
    proxy_set_header Connection Upgrade;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
# -------------------ここまでを追記-------------------
  #省略
}
```

ここで僕がはまったのは、一行目の〇〇_serverとproxy_pass http://〇〇_server/cable;は一致させてください。訳もわからずコピるとエラーになります！


#再起動

あとはターミナルでお馴染みの再起動！！！

```terminal
# 読み込み直し
sudo systemctl reload nginx

# 再起動
sudo systemctl restart nginx
```
これで使えるようになりました！！

#おわりに
今は細かいところは分からなくても、「とりあえず知っている」ってとても大切なことなので、是非記事の方熟読してみてください！！！
均一な乱数と正規分布に従う乱数をグラフにしてみる

Jupyter を使ってやってみます。

####均一な乱数

```
import numpy as np
import matplotlib.pyplot as plt

n = 500  # サンプル数
x = np.random.rand(n)
y = np.random.rand(n)

plt.scatter(x, y)
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/44a4cf99-6c57-3e78-a025-b047ca856d13.png)


####正規分布に従う乱数

```
import numpy as np
import matplotlib.pyplot as plt

n = 500  # サンプル数
x = np.random.randn(n)
y = np.random.randn(n)

plt.scatter(x, y)
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/8210647c-2eb0-e9be-2a4e-ef3bf14e4206.png)
【Vue.js】Vuetify導入でfiber.nodeがないって言われた

##概要
既存のVueCLIプロジェクトにVuetifyをインストールしたら下記のエラーがでた。

```terminal:terminal
・・・
(project_path)/node_modules/fibers/bin/darwin-x64-79/fibers.node` is missing.
・・・
```

その時のエラー回避方法を残す。

##環境
<dl><dt>OS: </dt><dd>macOS Catalina</dd>
<dt>Version:</dt><dd>10.15.6</dd>
<dt>@vue/cli:</dt><dd>4.5.4</dd><dl>

##解決方法
fiber.nodeの再インストールで回避。作業場所はpackage.jsonが置いてあるVueCLIプロジェクト

```terminal:terminal
$ npm uninstall fibers
$ npm install fibers
```
データウェアハウスのブロックサイズの初期値

OSSのデフォルトのブロックサイズを調べたのでメモしておきます．

## Hadoop

128MB

> Data Blocks
HDFS is designed to support very large files. Applications that are compatible with HDFS are those that deal with large data sets. These applications write their data only once but they read it one or more times and require these reads to be satisfied at streaming speeds. HDFS supports write-once-read-many semantics on files. A typical block size used by HDFS is 128 MB. Thus, an HDFS file is chopped up into 128 MB chunks, and if possible, each chunk will reside on a different DataNode.
> 
> [Apache Hadoop 3.0.0 – HDFS Architecture](https://hadoop.apache.org/docs/r3.0.0/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html) (Oct. 20, 2024 accessed)

## Amazon Redshift

1MB

> Typical database block sizes range from 2 KB to 32 KB. Amazon Redshift uses a block size of 1 MB, which is more efficient and further reduces the number of I/O requests needed to perform any database loading or other operations that are part of query run.
> 
> [Columnar storage - Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/dg/c_columnar_storage_disk_mem_mgmnt.html) (Oct. 20, 2024 accessed)

## Snowflake

50MB-500MB (非圧縮時)

> All data in Snowflake tables is automatically divided into micro-partitions, which are contiguous units of storage. Each micro-partition contains between 50 MB and 500 MB of uncompressed data (note that the actual size in Snowflake is smaller because data is always stored compressed). Groups of rows in tables are mapped into individual micro-partitions, organized in a columnar fashion. This size and structure allows for extremely granular pruning of very large tables, which can be comprised of millions, or even hundreds of millions, of micro-partitions.
> 
> [Micro-partitions & Data Clustering | Snowflake Documentation](https://docs.snowflake.com/en/user-guide/tables-clustering-micropartitions) (Oct. 20, 2024 accessed)
Python で数学グラフを表示してみる

Jupyter を使ってやってみます。

####べき乗
$y=x^5$

```
import numpy as np
import matplotlib.pyplot as plt

def exponentiation(x):
    a = 5
    return x**a  # xのa乗

x = np.linspace(0, 10)
y = exponentiation(x)  # y = f(x)

plt.plot(x, y)
plt.xlabel("x", size=14)
plt.ylabel("y", size=14)
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/44feb6b0-6de7-53e5-dc93-985fd6fd4c99.png)

####平方根
$y=\sqrt x$

```
import numpy as np
import matplotlib.pyplot as plt

def square_root(x):
    return np.sqrt(x)  # xの正の平方根

x = np.linspace(0, 100)
y = square_root(x)  # y = f(x)

plt.plot(x, y)
plt.xlabel("x", size=14)
plt.ylabel("y", size=14)
plt.grid()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/bd9804e9-e5e3-2e10-910f-38b7b8f49422.png)

####多項式
$y=4x^3+3x^2+2x+1$

```
import numpy as np
import matplotlib.pyplot as plt

def polynomial(x):
    return 4*x**3 + 3*x**2 + 2*x +1

x = np.linspace(-10, 10)
y = polynomial(x)  # y = f(x)

plt.plot(x, y)
plt.xlabel("x", size=14)
plt.ylabel("y", size=14)
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/3d30ca2d-779d-c261-8c25-25adb0b88b63.png)

####三角関数
$y=\sin x$
$y=\cos x$
$y=\tan x$

```
import numpy as np
import matplotlib.pyplot as plt

def sin(x):
    return np.sin(x)   # sin(x)

def cos(x):
    return np.cos(x)   # cos(x)

def tan(x):
    return np.tan(x)   # tan(x)

x = np.linspace(-2*np.pi, 2*np.pi)  # xの範囲を指定
y_sin = sin(x)
y_cos = cos(x)
y_tan = tan(x)/np.pi/10

plt.plot(x, y_sin, label="sin")
plt.plot(x, y_cos, label="cos")
plt.plot(x, y_tan, label="tan")
plt.legend()

plt.xlabel("x", size=14)
plt.ylabel("y", size=14)
plt.grid()
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225878/4454daf1-0ad7-3254-0d82-dd4ebe803ee6.png)
ESP-Prog ボードで ESP32 へのアップロードとモニター

「[ESP-Prog ボードで ESP32 のデバッグ](https://qiita.com/tosihr/items/332df49b6d98201ea9a4)」の続編です。先回の記事では、ESP-Prog の JTAG インタフェースを使って、プログラムをデバッグできるようにしました。ESP-Prog には、もう一つプログラムインタフェースがついていて、USB ケーブル1本でプログラムをアップロードしたり、シリアルポートをモニターすることもできます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/69196e1c-2e4d-ac65-6d96-105da24b0e8b.png" alt="ESP-Progの接続2" width="400">
白い輪ゴムがついている方がプログラムインタフェースです。ESP-Prog から出ているピンとブレッドボードを接続するのには、「[HiLetgo 5個セット 40P デュポン ワイヤー ジャンパー ブレッドボード 接続ワイヤー (オス-メス)1P-1Pピッチ 2.54mm 長さ 20CM [並行輸入品]](http://amazon.jp/dp/B013JXP3JM)」を使ってみました。ワイヤーがくっついているので散らばりません。

#前提

- Platform IO で ESP32 のプログラムをコンパイル、アップロード、モニターできていること
- ESP32 開発ボードに電源が供給できていること<br>
  &nbsp;&nbsp;「[ESP-Prog ボードで ESP32 のデバッグ](https://qiita.com/tosihr/items/332df49b6d98201ea9a4)」では、JTAG インタフェースから 5V をESP32 開発ボードに入力できるようにしました。


#ESP32 開発ボードとESP-Prog の接続

##ESP32 開発ボードの電源

ESP32 開発ボードにつながっている USB ケーブルは外してあります。JTAG インタフェースから 5V をとりましたので、プログラムインタフェースの VDD は接続しません。

##ESP-Prog の接続

下図のように ESP-Prog と ESP32 開発ボードを接続しました(Introduction to the ESP-Prog Board の [3.2.2. Communication Interface]
(https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/hw-reference/ESP-Prog_guide.html#communication-interface) にある プログラムインタフェースの図に、接続する ESP32 開発ボードのピンを書き込んだものです)。 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/db995381-e12e-f0bc-f69c-d9530f23f611.png" width="400" alt="PROG.png">

#母艦との接続
ESP-Prog と Mac を USB ケーブルで接続します。

#PlatformIO でプロジェクトの設定

Platform IO でプロジェクトを開いたら、platform.ini を開いて、アップロード用のプロトコル([ESP-Prog - PlatformIO latest documentation](https://docs.platformio.org/en/latest/plus/debug-tools/esp-prog.html#configuration))、モニター用ポート([Introduction to the ESP-Prog Board - ESP-IoT-Solution documentation]
(https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/hw-reference/ESP-Prog_guide.html#the-working-mode-of-usb-bridge)の !Note)、モニター用ポートのスピードを追加して、保存します。


```ini:platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = espidf
; デバッグに ESP-Prog　を使う
debug_tool = esp-prog
; ESP-Prog を使ってアップロード
upload_protocol = esp-prog
; モニター用ポート(ESP-Progを接続した時に現れるポートのうち番号の大きい方)
monitor_port = /dev/cu.usbserial-142101
; モニター用ポートのスピード
monitor_speed = 115200
```

#アップロードとモニター
アップロードとモニターの開始は、ESP32 開発ボードに USB ケーブルをつないでいた時と同じです。違いは、アップロード時に ESP32 開発ボード上の小さなボタン(IO0, EN)の操作が必要がなくなったことです。これは便利です。

##アップロード
PROJECT TASKS から General > Upload を選択します。ターミナルに次のように出てアップロードできました。
![Upload.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/caa5c4fc-833f-1ad1-97de-f57ee287d0c3.png)

##モニター
同様に PROJECT TASKS から General > Monitor を選択します。printf() などで出力したログを見ることができます。
![Monitor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/347fe558-c7a0-f83f-efd4-cd6198c5d5e8.png)

##デバッグ中のモニター
そして、Run > Start Debugging でデバッグ中にもモニターできます。111行目にブレークポイントを張っていますが、直前のprintf() の出力が TERMINAL に出力されています。 
![Debug.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1226137/90b19285-3972-730b-2bb2-f3835886d53e.png)

#まとめ
ESP-Prog を使ってUSBケーブル1本で、ESP32 のプログラムをデバッグしたり、アップロードやモニターすることもできるようになりました。
AndroidでItemDecorationを使ってみた

# 目的

- ItemDecorationを使ってみる。  
題材：カレンダー（Grid形式）   
　　　下記で作成したカレンダーを改造し、月毎のヘッダをItemDecorationで実現する。

https://qiita.com/yoshihiro-kato/items/4d88b0df6e2beb7f3aa3

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254520/4de25718-dd71-9bfa-80f5-0d64acc47e3d.png" width=30%>

# ポイント
RecyclerView.ItemDecorationでできることは、大きく2つ。
 - getItemOffsets
　itemの位置を移動する
 - onDraw、onDrawOver
　itemの下または上に描画を行う

# 実装内容
1. getItemOffsetsで、「月の一週目」の表示位置を１００下方に移動します。
1. onDrawOverで、1で移動した部分にヘッダーの背景とテキスト（月、西暦）を描画します。

```kotlin:DateItemDecoration.kt
class DateItemDecoration: RecyclerView.ItemDecoration() {
    companion object {
        private const val HEADER_HEIGHT = 100
        private val headerBackGroundPaint = Paint().apply {
            color = Color.GRAY
            alpha = 200
        }
        private val headerTextPaint = Paint().apply {
            textSize = 65f
            color = Color.WHITE
        }
    }

    override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
    ) {
        super.getItemOffsets(outRect, view, parent, state)
        if ((parent.getChildViewHolder(view) as DateAdapter.DateAdapterHolder).isFirstWeek) {
            outRect.top = HEADER_HEIGHT
        } else {
            outRect.top = 0
        }
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDrawOver(c, parent, state)
        val size = state.itemCount
        for (i in 0 until size step 7) {
            drawHeader(parent, c, i, i + 7)
        }
    }

    private fun drawHeader(parent: RecyclerView, c: Canvas, prev: Int, now: Int) {
        val prevView = parent.getChildAt(prev)
        val prevHolder =
                if (prevView == null) {
                    return
                } else {
                    parent.getChildViewHolder(prevView) as DateAdapter.DateAdapterHolder
                }
        if (prevHolder.isFirstWeek) {
            c.drawRect(
                    0f,
                    prevView.y - HEADER_HEIGHT,
                    parent.width.toFloat(),
                    prevView.y,
                    headerBackGroundPaint
            )
            c.drawText(
                    "${prevHolder.month} ${prevHolder.year}",
                    0f,
                    prevView.y,
                    headerTextPaint
            )
        }
    }
}
```

コードの詳細は下記参照：
https://github.com/yoshihiro-kato/android-samples/tree/b8bb2489ffb113de50df947400a640fc357a0801/KotlinSampleApplication
【Laravel】join, group by, count(*)したいとき

# やりたいこと
・グループごとの集計人数を出したい
・`SQL`はかけるが`Laravel`の`クエリビルダ`の使い方がわからない

Laravelでなかなかうまくいかなかったので学びを記事にしました。

# テーブル構造
例えば以下のようなテーブルがある時
↓（親）groupsテーブル

| #| name|
|--|-----|
| 1|group1|
| 2|group2|
| 3|group3|
| 4|group4|
| 5|group5|
| 6|group6|

↓（子）usersテーブル

| #| name| group_id|
|--|-----|---------|
| 1|user1| 1|
| 2|user2| 1|
| 3|user3| 2|
| 4|user4| 2|
| 5|user5| 3|
| 6|user6| 4|

# SQL文
「group」ごとに何人の「user」が所属しているかを集計したいとき。
素のSQL文では以下のようになります。

```sql
select
  g.id, g.name, count(u.group_id)
from
  groups g
left outer join users u
on g.id = u.group_id
group by u.group_id;

```

#クエリビルダ
`Laravel`で表すと以下になります。

```php
$groups = DB::table('groups')  //＄groupsは変数にいれてるだけ
             ->leftJoin('users', 'groups.id', '=', 'users.group_id')
             ->select('groups.id', 'groups.name', DB::raw("count(users.group_id) as count"))
             ->groupBy('groups.id')
             ->get();
```
・外部結合は　`leftjoin()`（内部結合してしまうと誰も所属してないグループが無視される）
・グループ化は`groupBy()`
・集計関数は素のSQL文で書く必要あるため`DB::raw()`を使います
（私は集計関数に時間かかりました）

# groupByがうまく動かない問題

私の場合は以下のようなエラーが出ました。

```
SQLSTATE[42000]: Syntax error or access violation: 1055 '（アプリ名.groups.name' isn't in GROUP BY
```

これが書き方なのか何のエラーかわからず解決に時間かかってしましました。

実は `Laravel`のバージョンによってはうまく動作しません。
ちなみに私は`Laravel8`です。

# “strict”モードをfalseは本質的な解決ではない

コメントでご指定をいただき訂正します。
以前は下記のようなやり方で解決したと思っておりました。

<details><summary>“strict”モードをfalseにし本質的な解決ではない以前のやり方</summary><div>

結論いうと
**config/database.phpの “strict”モードをfalseにすることでエラーが表示されなくなります。**

```php:config/database.php
'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'laravel-blog'),
            'username' => env('DB_USERNAME', 'root'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => false,//←この部分をtrueからfalseへ
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

```
これでうまく表示されるようになります！！
`MySQL`のセキュリティみたいなものがデフォルトで厳しく？なってるみたいで
グループ化による重複するレコードが問題みたい・・・です(^\_^;) 
この問題を最初に解決した人スゴイ(\*_*)

</div></details>

実際のエラーの内容は
**「selectで設定したカラムを全てgroup byにせよ」**
ということが再度調べたら判明しました！
このことを私は理解できてませんでした（＠＠）

ご指摘ありがとうございます！

```php
$groups = DB::table('groups')  //＄groupsは変数にいれてるだけ
             ->leftJoin('users', 'groups.id', '=', 'users.group_id')
             ->select('groups.id', 'groups.name', DB::raw("count(users.group_id) as count"))
             ->groupBy('groups.id')
             ->groupBy('groups.name') //←こちらを追加
             ->get();

```

これで**“strict”モードをfalseにすることなく**正常に動作しました＾＾


# 結果
先ほどの`$group`を`foreach`で取り出すと

|groups.id|groups.name|count|
|--|-----|-----|
| 1|group1|2|
| 2|group2|2|
| 3|group3|1|
| 4|group4|1|
| 5|group5|0|
| 6|group6|0|

こんな感じになります (^-^)
これでビューへ渡す変数が一つで済むし表示が簡単になります！
linkを新規タブで開く

- `rel="noopener noreferrer"`持つけるといいみたい
- というのは、`target="_blank"`で新規タブを開いた後のページで、リンク元の中身を書き換えることができるらしい

```html
<a href="リンク" target="_blank" rel="noopener noreferrer">
  aiueo
</a>
```
Amazon Athena「No output location provided」エラーの対処法

#エラー発生
Athenaでパーティション設定時に下記のエラーが発生した。

```
Your query has the following error(s):

No output location provided. An output location is required either through the Workgroup result configuration setting or as an API input. (Service: AmazonAthena; Status Code: 400; Error Code: InvalidRequestException; Request ID: XXXXXXXXXXXXXXXXXXX)

```


![スクリーンショット 2021-05-08 16.24.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/e3ca02ba-9c96-ac03-623e-c4dc05560551.png)

#解決方法
クラスメソッドの[Athena でクエリ実行時に”No output location provided ……”というエラーが表示された時の対処方法](https://dev.classmethod.jp/articles/tsnote-support-athena-error-query-launch-001/)の記事によると、クエリ結果の出力場所を指定する必要があるということ。

おそらく記事作成時とUIが変わり、現在のUIでは「Settings」から設定が可能。
![スクリーンショット 2021-05-08 16.36.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/459cfc06-3156-c423-5281-9bebed9fc9a7.png)
S3パスがわからなくても、「Select」から選択が可能。
![スクリーンショット 2021-05-08 16.39.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449478/9a32f41a-d7d3-051c-dbd4-59cdef68eb05.png)
AI 系サービスを選ぶときに考えるべきポイント

# はじめに
世の中にはいろんな AI 系サービスがあふれてますが、実課題に応じて合理的に選択したいものです。
そのあたりの考えをまとめました。

# やっぱり銀の弾丸はない
最初から身もふたもないですが、やっぱり**銀の弾丸(＝これをやれば万事解決する)**はありません。
AI モデルはあくまで「**ツール**」であり、「**目的**」を設定して価値を見出すのは「**ユーザー**」です。
こればかりはどんなサービスを選ぶにしても避けて通れない考え方です。
ビジネスの場で全く同じ目的はないですからね。

# まずは目的を整理しよう
データ分析の目的は以下3種に大別されます。どれかに該当するはず。

||目的|主な利用主体|仮定は必須？|技術難易度|備考|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|#1|**効率化/属人性排除**|営利企業|〇|★★★|・**プロセスの改善**が進むことでコストの低下や合理化が見込めるようなケース<br>・ex.発注業務の一部を代替可能な AIモデル<br>・ex.有人で検知していた異常を一定の精度で検出するAIモデル<br>・**業務設計や組織的な課題が関係**してくる|
|#2|**付加価値向上**|営利企業|〇|★★★|・ユーザーへの付加価値が向上することで売上向上が見込めるようなケース<br>・LVT(生涯顧客価値)など**手法が確立されている分野も多い**<br>|
|#3|**データマイニング**|研究開発|×|★★★★★|・集めたデータから何らかの傾向を発見する＝データマイニング<br>・アカデミックな研究やそもそも仮説設定ができないケースが該当<br>・「データがあれば何かできそう」もこれに該当<br>・**目的が不明瞭**なまま進行した場合、**実証検証**が失敗することが多い|g|h|i|

ビジネスの場で成功している実証検証の**ほぼ 100% が #1 省人化 もしくは #2 付加価値向上** に該当します。
具体的な経営課題の解消をテーマに掲げる以上は、**目的の明確化**が必須です。
そのうえで仮設立案と検証のサイクルを回し、データ分析の結果をビジネス成果にに紐づけていきます。

注意しなくてはいけないのは、**精度指標の類はモデルの状態を表すもの**であるという点です。
実際にモデルが活用される業務フローを踏まえたうえで、**プロジェクト全体を評価する指標は別途設ける**必要があります。
よってプロジェクトを進める前に以下をしっかり検討すべきでしょう。


- 自社で取り組もうとしている課題は 1-3 どれに該当するのか？
- 1 or 2 であれば AI モデルは自社の業務のどの部分に組み込まれるか？
- 3 であれば仮説を立てて、1-2 の課題に昇華できないか？
- モデルが業務に組み込まれた結果、業務プロセスはどのように変化するか？
- モデルの出力結果は、何らかの形で既存 KPI と紐づけ可能か？


# ツール種別
市場にあるサービスは大きく以下 3 種に分けられます。

- #1 **ピンポイント SaaS 型**
    - 特定の課題解決のために開発された SaaS 型アプリ
    - 既存 SaaS に AI がアドオンされる場合も多い
    - #2 のように外部に API の口出してることも
- #2 **API 連携型**
    - いわゆる「マネージドAI」と呼ばれるクラウド型のサービス
    - API で連携してモデルの結果を取得
    - 従量課金型が多い
    - 自社で管理しているアプリケーションがある場合、すぐに組み込み可能
- #3 **分析基盤 SaaS 型**
    - データ分析基盤と提供する SaaS 型アプリ
    - プラットフォームによっては従量課金型
    - 自社でごりごりモデルを開発する場合
    - ETL や MLOps の連携性高いサービス多し
    
ざっと特徴を整理します。

||AIツール種別|モデル<br>実装難易度|アーキテクチャ設計<br>/システム導入難易度|汎用性<br>(ex.モデルを別のシステムから利用)|拡張性<br>(ex. 外部システムと連携)|
|:--|:--|:--|:--|:--|:--|:--|:--|:--|
|#1|**ピンポイント SaaS 型**|-|★|ベンダー/課題に依存|ベンダーに依存|
|#2|**API連携型**|-|★★|★★★|周辺アーキテクチャの拡張性次第|
|#3|**分析基盤 SaaS 型**|★★★|★★★|★★|実装者の能力とアーキテクチャの拡張性次第|

# じゃあどれを選ぶ？
個人的に重要視している点は以下の通りです。

- **ナレッジ蓄積**
    - 中長期的な組織活性には不可欠
        - 使うだけ、ではなく、その結果をどう見るかが重要
        - 人材育成にも直結
- **拡張性**
    - 拡張性や汎用性が自社の要件に沿うか
    - 将来的に自社の優先度高い要件がベンダーのそれに合致するとは限らないことは留意
- **開発コスト/運用コスト**

以下のようなケースは #1 特化アプリケーション組み込み型の課題解決がフィットします。

- 特定の職務領域における課題
- 地域やコミュニティで個別最適されている課題
    - #2 API 型で代替されにくい
    
そうでない場合には、

- **#2 API 連携型と #3 自社開発 の併用**
    - モデルの開発工数次第でスイッチ
    - 便利な API はどんどん使う
- **過度にパートナーやベンダーに依存しない**
    - 足りないところだけうまく補う
    - 補った分はうまく自社に取り入れる (うまく盗む)

が最もバランスが良いと考えてます。







# まとめ
私もそうですが、データ分析やAIのプロジェクトはどうしても手法に焦点が当たりがちです。
折に触れてこれらの点は見直すようにしたいところです。
ubuntu18でifconfig: command not found


どうやらifconfigを使うのは時代遅れかもしれません。。
> [If you’re still using ifconfig, you’re living in the past](https://ubuntu.com/blog/if-youre-still-using-ifconfig-youre-living-in-the-past)

以下のようにIPアドレスを調べたいがifconfigがない場合は、

```
$ ifconfig
ifconfig: command not found
```

ipコマンドを実行すればよい

```
ip addr show
ip -4 addr show  #ipv4のアドレスを表示する場合
ip -6 addr show  #ipv6のアドレスを表示する場合
```

ipコマンドはifconfig以外の以下コマンドを統合するものらしい

>> [If you’re still using ifconfig, you’re living in the past](https://ubuntu.com/blog/if-youre-still-using-ifconfig-youre-living-in-the-past)
>Are you still using one of the following tools?
/bin/netstat (replaced by ss, for which I’ll dedicate another blog post entirely)
/sbin/ifconfig
/sbin/ipmaddr (replaced by ip maddress)
/sbin/iptunnel
/sbin/mii-tool (ethtool should appropriately replace it)
/sbin/nameif
/sbin/plipconfig
/sbin/rarp
/sbin/route
/sbin/slattach

どうしてもifconfigを使いたい場合は`net-tools`をインストールすれば使えるが、
なるべくipコマンドを使っていった方が時代についていけそう

```
sudo apt install net-tools
```

# 参考
[If you’re still using ifconfig, you’re living in the past](https://ubuntu.com/blog/if-youre-still-using-ifconfig-youre-living-in-the-past)
[Questions about CentOS-7](https://wiki.centos.org/FAQ/CentOS7#What_have_you_done_with_ifconfig.2Fnetstat.3F)
[ifconfigコマンドとipコマンドの違いと比較](https://tech.mktime.com/entry/211)
メソッドに関する超基礎理解

###はじめに
railsのフレームワークに沿ってコードの記述をしていると、CRUD処理の中はメソッドを定義して、それをビューに反映させて〜といった形で、深く考えなくてもプログラムが動きます。
また、Rubyを学習し始めていた際、メソッドは「複数の処理をまとめるもの」という説明で、何となく分かった気になっていました。

そこで今回は改めて、Rubyのメソッドに関して確認しました。

###メソッドについて
まず大前提として、メソッドはクラス内で「何らかの振る舞いをすること」であり、その振る舞い内容をdef~end内に記述していきます。
そしてその振る舞い内容はクラス内で、「〜する。」ということになります。

つまり、

```ruby:
def 勉強する
end
```
となると、勉強するという部分がメソッドになります。
そのためこのメソッドは勉強するという振る舞いをするメソッドとなります。


###引数について
次に引数に関してですが、引数がある場合のメソッドの内容は、「(引数を)〜する。」などの形になります。
そのため引数はメソッドに補足的な情報を与える役割があります。ex)何を、いつ、どんな
なので先ほどの「勉強するメソッド」内に数学という引数を入れると、

```ruby:
def 勉強する(数学)
end
```
となり、「勉強するメソッド」に「何を」という補足情報が入りました。


あとはメソッドの中身を記載して、コードに反映させていきます。

```ruby:
def study("subject")
　puts "私は#{subject}を勉強する。"
end
study("数学") 
=>私は数学を勉強する。
```

###おわりに

メソッドは〜するという振る舞い自体を指しており、引数は振る舞いに補足情報を与えている。
大雑把な理解かもしれませんが、大枠を掴むことはできました。
Google カレンダーを画面の右端に表示したかった(備忘録)

# Google カレンダーを画面の右端に表示したかった

基本的にはリハビリのためにアレコレやってます。

こちらの記事をZennに投稿しなかったのは、
備忘録的にポイポイしてるから。


```js
(function calenderFit() {
    // Google カレンダーを Windows のデスクトップアプリのように扱う方法 
    // https://loumo.jp/archives/18828
    // デスクトップに窓枠無しで表示できるらしいと上記の記事で把握

    // 画面の右側に表示したかった。
    // ついでに起動時に自動でセットできるようにしたかったけど、
    // そちらはWindowsのショートカットからだと現在難しいみたい？
    // アドオンとかが必須になるようなので今回は見送り

    // #region ENUM
    const WIDTH = 255;
    const WINDOW_SIZE = { w: 3000, h: 2000 };

    const DICT = {
        timeHight: styleSet(["mmsF1c", "top", "4%"]),
        dayWidth: styleSet(["WJVfWe", "width", "95%"]),
        leftBarWidth: styleSet(["QQYuzf", "width", "0%"]),
        leftVisibility: styleSet(["dwlvNd", "visibility", "hidden"]),
        dayHeight: styleSet(["YvjgZe", "flex", "0.123 0 auto"]),
        xxxTop: styleSet(["dwlvNd", "top", "50px"]),
        zzzTop: styleSet(["eh5oYe", "top", "90%"]),
        zzzFlex: styleSet(["H7IzGb", "flex", "0.3 0.3 auto"]),
    };


    // #endregion ENUM

    // #region WindowControl

    // windowsだと画面の表示倍率変更すると、
    // ピクセルの座標とか変わります
    const isWindows = window
        .navigator
        .userAgent
        .indexOf("Windows") >= 0;
    const scWidth = isWindows
        ? WINDOW_SIZE.w
        : window.screen.availWidth;
    const scHeight = isWindows
        ? WINDOW_SIZE.h
        : window.screen.availHeight;

    // ness [[ resize => move ]]
    window.resizeTo(WIDTH, scHeight);
    window.moveTo(scWidth - WIDTH, 0);

    // #endregion WindowControl


// Object.KeyValuesで回せばよかったなー
    styleSetter(DICT.timeHight);
    styleSetter(DICT.dayWidth);
    styleSetter(DICT.leftBarWidth);
    styleSetter(DICT.leftVisibility);
    styleSetter(DICT.dayHeight);
    styleSetter(DICT.xxxTop);
    styleSetter(DICT.zzzTop);
    styleSetter(DICT.zzzFlex);

    function mix(fa, fb) { return v => fb(fa(v)); }
    function styleSet(arr) { return () => arr }
    function toArr(elm) { return [...elm]; }

    function classedElms(v) {
        return document.getElementsByClassName(v)
    }

    function styleSetter(f) {
        const [elmClassName, styleName, value] = f();
        function elms(key, act) { mix(classedElms, toArr)(key).forEach(act); }
        function setStyle(k, v) { return e => e.style[k] = v; }
        elms(elmClassName, setStyle(styleName, value));
    }
})()
```
【HTML】VSCodeで複数行をタグで囲いたい時はwrap with abbreviationを使う

※自分のための備忘録記事です。
エディタはVSCodeを使用しています。

##複数行をdiv等のタグで囲いたい時

①囲いたい行を選択する。
②コマンドパレットを[Ctrl] + [Shift] + [P]で開く。
③wrap with abbreviationと入力する。
④div等タグを入力する。（Emmetの入力方法が使える。)


参考記事
[【VSCode】まずはこれを覚えよう！初心者のためのショートカット２９選！【Mac/Win】
](https://skillhub.jp/blogs/234)[Visual Studio Code - コマンドパレットとナビゲーション
](https://murashun.jp/article/programming/visual-studio-code/command-palette-navigation.html)
LeafletでGPSログ(GPX)地図：標高グラフ追加。

LeafletでGPSログ(GPX)地図：標高グラフ追加。
グラフ作成には、「Highcharts」を使用。

詳細は以下。
https://2ndart.hatenablog.com/entry/2021/05/08/160620
![111.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374794/462beec2-1a27-0f15-f789-4b597cf6059d.jpeg)
Gitの本質＝commitとはどういうことか

現場ではSubVersionを使っており、gitはちょろっと触ったことがあったのみ。add→commitを繰り返して適度にpushしてpullリクエストを投げて、コンフリクトを起こしたら慌ててググるみたいな状態だったので、ちゃんと勉強しようと思った。

Gitの本質はcommitだ、というのはなんとなく知ってはいたものの、それがどういうことなのかきちんと理解していなかったので自分の知識定着も兼ねて記事にしておく。

まだまだ初心者なので、間違いがありましたらご指摘いただけると嬉しいですm(_ _)m

##commitは差分ではなくその時点のスナップショット
1.ワークツリーの変更分のみをgit add、git rmし、インデックス(ステージング)環境に反映
2.インデックスの内容をローカルリポジトリにcommit

という流れだからか、commitは変更差分だと思いがちだが、実際はインデックス環境のスナップショットを丸ごと取得している。
なので、後からcommitのSHA-1(commitした時に割り振られる長いやつ)を指定して前のバージョンに戻すことができる。
##branch=ただのポインタ
上記のとおりcommitすると、そのcommitを特定するためのSHA-1が割り振られる。
なので繰り返しになるが、SHA-1が分かっていれば特定のcommit時のスナップショットを取り出すことができる。
ただSHA-1は暗号学的関数というだけあり、長すぎて覚えられない。というか、そもそも覚える目的で作られていない。
これでは頻繁に確認したかったり、別に分けておきたかったりするcommitがあったとき、いちいちgit logでSHA-1を確認しなければならず面倒。

そこで、覚えておきたいcommitを指すポインタを作り、自由に名前をつけれるようにしたのがbranch。
ここで注意が必要なのは、SHA-1 != branchだということ。
SHA-1はあくまで特定のcommitと1対1になっており、生成されたSHA-1が指すcommitは不変。
branchはあるcommitを親とした子commitができた場合、子commitを指す。ポインタなので位置が変わる。(変えられる)

####1.コミット1が親のコミット2を作り、コミット2を起点とたdevブランチを作った場合
devブランチとHEAD(＝現在地)はコミット2を指す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1374119/97659fcf-c725-5aae-8798-cb98552a5b58.png)

####2.上記devブランチにコミット3を追加
コミット2に対するSHA-1は変わらず、devブランチとHEADの指す位置がコミット3に変わる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1374119/ee3632a5-16e8-c94c-0780-1284c6a1cd7f.png)

今までbranch操作はちょっとヒヤヒヤしていたが、正体はポインタなので操作ミスをしてもまた新しいブランチを作れば良い。
それよりもcommitの変更内容がわかるようなコメントをちゃんと書くことの方が大切だと思った。

##鬼門checkout
checkoutはbranchを切り替えるときも使うし、編集したワークツリーを変更前の状態に戻すときも使うし、単語の意味をひくと「勘定を済ませて出ていく」とあるし、で、イマイチよく分からないコマンドだった。
しかし上記を踏まえると、checkoutはcommitせずにHEAD(=現在地)の位置を移動させるコマンド、という認識で良いのかなと。

####branchの切り替え
devブランチからmainブランチへcheckoutすると、mainブランチが指すコミットbにHEADが移動する。
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1374119/1c0b4da7-a166-902f-2e93-b5df4e007b6c.png)

####ワークツリーの変更を戻す(インデックスの状態にする)
左　：ワークツリーとインデックスは同じコミットを指しているが、ワークツリーのほうは変更を入れてしまっている。
中央：そこでcheckoutし、インデックスのコミットを指し直す
右　：すると変更がリセットされ、変更前のコミット3の状態に戻る
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1374119/5193b661-5573-4ef4-c1df-7a918f27aedb.png)


##参考
特に@JugglerShu@github さんの以下の記事はとても参考になりました。より詳細の情報が解説されており、実践形式で手を動かしながらできるので、Git初心者の方と先にSVNに慣れている方には特におすすめです。

https://qiita.com/JugglerShu@github/items/f3a3d929b931e6fefb9d

https://github.blog/jp/2021-01-06-commits-are-snapshots-not-diffs/
https://ejje.weblio.jp/content/checkout
Linuxの起動画面に(initramfs)と表示されて起動しない場合の対処法

# 使用PC
メーカー：jumper
モデル：jumper-EZBOOK-X4
OS：Linux Mint 20 Ulyama
CPU:Intel celeron j3455
ストレージ：SSD(128GB)標準搭載です．
メモリ：４GB
度々登場する中国製3万円PCですね．
こいつ実はYoutube見てたら急に音が出なくなったり，無線のインターフェースが急にdownしたりと動作が危ない感じは前からありました．
起動しないのは困るので直して行いきましょう．


# エラー発生までの流れ
電源ボタン押下後，LinuxMintのロゴが表示される．
その後になぜか起動せず，以下のメッセージが出力．

```
# 表示メッセージの x は皆さんの環境によって数字が異なります
Busybox: v.x.xx.x (Linux Mint x:x.....) built-in shell (ash)
Enter 'help' for a list of built-in commands.

(initramfs)
```

あなた何者ですか？というかどの辺りの方？
自己紹介くらいしてよって感じです．

>電源を強制的に落としたりすることで、立ち上げに必要なファイルが破損してしまうというエラーらしい。
>アップグレードに伴って起こることもあるよう

ブチ切りには注意しましょう．

# さて直して行きますよっと
こんなのが表示されるたびにOSを入れ替えていたら，人類滅亡します(偏見)

確かに初期化も大切だが，windowsでも自動的に修復するプログラムが確かあったはずなので，それの手動バージョンのようなものだと認識しています．

## まずはやっぱり reboot っしょ
(initramfs)の後にはコマンドを打つことが可能です．

困ったら reboot に限ります．
ちなみに効果は無し．残念．無念．

ただ，収穫はありました．
起動画面で以下のような表示になったので，Linux Mintの起動ファイルがバグってる模様．

```
* LinuxMint 20.1 Ulyana xxxxxxxxxxxxx
  Advanced option for Linuxmint 20.1 xxxxxxxxxxxxxxx
```

悪さしているところを突き止めることに成功．

## fsckコマンドでディスクを修復
相変わらず，(initramfs)とかいう野郎が出現してきています．
~~そういうところだぞLinux~~
exitコマンドを入力することで，ディスクのチェックが始まります．

```
(initramfs) exit
/dev/sda2 contains a file system with error, check for .......
```

上記のような表示が出るはずだ。
/dev/以下の場所は各機器によって異なる。
<font color="Red">Run fsck MANUALLY</font>という表示もどこかにあるはずだ。
これでエラーを吐いている場所は特定完了。
以下のコマンドで修復する。

```
fsck /dev/sda2
# /dev以下は皆さんの環境によってことなるので、容易にペーストしないように。
```
上記のコマンドでsda2を修復する．
このあとはconnect ti …? (y)　とか
Fix … … ?(y) とか色々聞かれるが、全てyesで行こう。
そうすると、最後に<font color="DodgerBlue">file system has modified</font>という表示が出たら解決だ.

最後にrebootをかければ無事に起動することだろう。

## お・ま・け
本当？というかよくあるエラーだと，/dev/sda2/xxx/yyy/bootなんちゃか
というのがエラーを吐く人が多いが，私はSSDのパーティション丸々破損してます的なね．．．
これは中国製PCのだからなのか，SSDだからなのか，はたまた，ファンレス使用のアルミ筐体だからなのか．．．
熱がこもることは前から危険視していたが，シャットダウンすると，アルミの放熱性能も相まってすぐに冷えるのでスルーしてた．
冷めやすいということは熱しやすいと同義であるからして，対策は講じるべき．

# 参考にさせていただいたサイト

https://ultrabem-branch3.com/informatics/linux/error_initramfs
AWS Lambdaのログのタイムゾーンをいい感じにしたい

# 背景
Lambda Powertoolsを使ってログ出力していたが、タイムゾーンがUTCになっていたのでこれをどうにかしたい、という要望が出た。

どうにかしたい、という微妙な言い回しになっているのは、

- タイムゾーンをJSTに変更する
- UTCのままでタイムゾーン情報を追加で出力する

という２パターンが選択肢として存在したため。最終的には後者で終えた。理由は後段で述べる。


# Lambda Powertools の簡単な使い方

前提知識としてざっくり書いておく。以下の様にデコレータをつけると詳細なログを出してくれる。

```python
from aws_lambda_powertools import Logger

logger = Logger()

@logger.inject_lambda_context
def handler(event, context):
    logger.info("This is an information")
    ...
    logger.error("This is an error")
```

出力される情報は、関数名、メモリサイズ、コールドスタートかどうか、などなど。
詳しくはドキュメント参照。

https://awslabs.github.io/aws-lambda-powertools-python/latest/core/logger/


# タイムゾーン情報を付加して出力する方法

前述したようにタイムゾーンはUTCのまま、タイムゾーン情報を付加する形で落ち着いた。

実装方法は、`logger`の初期化部分で以下のようにするだけ。

```python
logger = Logger(datefmt="%Y-%m-%d %H:%M:%S %z")
```

こうすることで、出力の形式は以下のようになる。

```python
"2021-05-08 15:50:22 +0000"
```

一応タイムゾーンを変更する方法もあったのだが（後述）、内部変数をいじるような形になってしまったため今回は不採用。
とりあえず、プロジェクトに関わる人の中で合意が取れるところに落とし込んだ。


# Timezoneを変更する方法

以下のようにすれば変更できる。pytzはインストール済みの想定。

```python
from datetime import datetime

from aws_lambda_powertools import Logger
from pytz import timzeon


def custome_time(*arg):
    return datetime.now(timezone("Asia/Tokyo")).timetuple()

logger = Logger()
logger._handler.formatter.converter = custome_time
```

前述の通り内部変数をいじる形になってしまう。
（他にいい方法あるのだろうか...？）

https://github.com/awslabs/aws-lambda-powertools-python/blob/48a64ebfc741725acf0899972d232a8c9c9549ec/aws_lambda_powertools/logging/logger.py#L45



# Lambda Powertoolsを使わない場合

単純に`logging`ライブラリを使う場合は以下のようにできる。
今回はLambda Powertoolsの詳細なログを捨ててまでタイムゾーン変更をする強い要望はなかったのでこちらの方法は採用していない。

```python
from datetime import datetime
import logging
import pytz


def custome_time(*arg):
    return datetime.now(timzeone("Asia/Tokyo")).timetuple()
  
logger = logging.getLogger(__name__)

handler = logging.StreamHandler()

formatter = logging.Formatter()
formatter.converter = custome_time

handler.setFormatter(formatter)
logger.addhandler(handler)
```

前述したLambda Powertoolsでタイムゾーン変更はこの手順を無理矢理やっているだけ。


# ちなみに環境変数`TZ`をいじるという方法もあるが...

[この記事](https://blog.serverworks.co.jp/tech/2019/10/30/lambda-timezone-2/)によるとTZを変更するのは非推奨らしい。  


# 参考リンク

https://stackoverflow.com/questions/32402502/how-to-change-the-time-zone-in-python-logging/47104004

https://qiita.com/r1wtn/items/d615f19e338cbfbfd4d6

https://blog.serverworks.co.jp/tech/2019/10/30/lambda-timezone-2/

https://github.com/python/cpython/blob/1330338583d183250186a8123b99d2283e945b4f/Lib/logging/__init__.py#L522

https://github.com/awslabs/aws-lambda-powertools-python/blob/48a64ebfc741725acf0899972d232a8c9c9549ec/aws_lambda_powertools/logging/logger.py#L45
ENS + IPFS で web サイト公開(電子公告ホスティングなど)するメモ

## 背景

なるべくダウンせずに永続的に使える(非中央集権管理された)ドメインで情報公開したい. 
特に頻繁に変更はない + 静的サイト(単純 html, PDF とか画像のホスト)で十分
法人の電子公告のためのサイトとか.

手間暇かけず 3~5 年はダウンせずに動いてほしいようなもの.


Decentralizing your Website
https://towardsdatascience.com/decentralizing-your-website-f5bca765f9ed

IPFS + ENS Everywhere: Introducing EthDNS

ENS+IPFSでページ作ってみた
https://dev.to/dongri/ens-ipfs-3mb

Vue.jsのプロジェクトをIPFS上にホスティングし、ENS（Ethereum Name Service）でアクセス可能にする
https://qiita.com/toshiaki_takase/items/1075e534e8ce7ce00476

ありがとうございます.

ENS: Ethereum 上でのドメインネームサービス
IPFS: 最近のナウでヤングな分散ファイルシステムプロトコル. Brave ブラウザで対応. 今後主要ブラウザでも対応するやも?

## ENS ドメインの取得

文字数によって費用がかかります. 3~4 文字数ですと gas 代ふくめお高いです(1 年 3~5 万円くらい(2021/04/15 時点))
8 文字くらいだと 1 年 4~5 千円, 10 年で 1.5 万円くらいです.
ブロックチェーンで永続性高いとはいえ, 10 年後は Ethereum もどうなっているかわかりませんのでとりあえずは 10 年未満でドメイン購入がよいのではと思います.

## IPFS

分散ファイルシステム的なプロトコル

ブロックチェーンみたいに永久にファイルが残りそうな感があるが, そんなことはない(人気のないファイルは時間が経つと消える. 伝播させたくないファイルがあったらホスティングをやめる(そのままでは, 各ノードが永続的にファイルをホスティングするインセンティブが無い))

pin したとしても, 自身が頑張って IPFS ノードを建てて定期的に(だいたい一日おきくらい?)アップロードする必要がある.

https://filecoin.io/blog/posts/opensea-decentralizes-and-persists-nft-storage-with-ipfs-and-filecoin/

IPFS 自体はプロトコルなので, 実データは Filecoin のストレージに記録して, 適宜 `$FIL` を払うなり Filecoin のマイニングして永続化を頑張るのが 2021 年時点の推奨でしょうか.

Filecoin 自体は Chia のようにストレージ領域を提供することでマイニングできるっぽい
(日本語だと検索するとゴミな結果しか表示されず本当につらい :cry: )

https://filecoin.io/mine/

Unmineable で `$FIL` を　CPU/GPU マイニングできるとうれしいところであるが, 現状は対応していない.

### ファイルが公開されているかの確認

たとえば `https://ipfs.io` など


とりあえずは Brave で確認するのがよいでしょう.

```
ipfs://QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg
```

### Hosting サービス

ipfs のサービスは自前で建てるのもできますが, Fleek などのホスティングサービスを利用もできます(50 GB, 及び転送量? 50 GB/month まで無料). とりあえずはホスティングサービスを使うのが楽でしょうか. ENS との連携も楽にできます.

ちなみに ENS 連携は 2021/09/14 時点で 3,500 円くらい ETH の fee がかかりました

### 注意点

ipfs で publish してもすぐには反映されません. 
ネットワーク浸透？のため数十分から数時間は待つ必要があります.

自前 ipfs ノード建てた場合, ポートブロックとかでうまくいっていないのか単に反映が遅いだけなのかわかりずらいです.

ENS と連携した場合, DNS(?)の浸透に 24 時間くらいかかります.
すぐには反映されないので注意しましょう(12 時間ごとにリフレッシュしているっぽい)
(`could not resolve name` みたいなエラーが出る)


## 懸念点

現状なんでもアップロードできてしまう(ipfs 自体は protocol のため, content が何かは問わない).
ファイルを消したい場合, 各ノードが頑張って消す(伝播させない)必要があるので, 人気のあるファイルをアップロードしてしまうと消すのに時間かかるかも?

たまに `could not resolve ...` になります. あまりアクセスがないから IPFS ノードからキャッシュが消されたのか, ENS 周りの不調なのか...
(あとは IPFS ホスティングを利用している場合, ホスティング側の問題があるかもしれません)

1, 2 日程度のダウンはありえるのを考えておいたほうがよさそうです. 安定稼働にはもう少し技術の熟成をまつ必要がありそうです.
(電子公告だと基本ダウンしていないことが求められるが, とりあえず数日ならそのぶん公告を延長することで許容されそう...? URL が変わることはないので)


## TODO

* [ ] 電子公告関連のサービスを ENS + IPFS で対応し, 処理を自動化して電子公告ホスティング代行でがっぽがっぽする
  * [ ] IPFS + Filecoin でデータの永続化を考える
catコマンド

ファイル閲覧

````
$ cat fileA
`````

![スクリーンショット 2021-05-08 15.55.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/571973/eab12591-084d-bfd8-f2d2-024308e1b2e3.png)

ファイルへの出力（上書き）

`````
$ cat fileB
`````
![スクリーンショット 2021-05-08 15.56.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/571973/9c982231-b869-965b-8278-3089639e5169.png)

ファイルへの出力（追加）

````````
$ cat fileA >> fileB
`````````

まとめて出力できる

``````
$ cat fileA fileB
`````
![スクリーンショット 2021-05-08 15.58.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/571973/a72ed2e9-fe00-4864-ef57-4e490c6f4f1e.png)

番号つけて出力 numberのn

`````````
$ cat -n fileA
`````````

![スクリーンショット 2021-05-08 15.58.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/571973/49657042-1e63-85e3-33df-44dc5f02a253.png)

番号つけて出力（空白行はなし）blankのb

```````
$ cat -b fileA
```````
![スクリーンショット 2021-05-08 16.00.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/571973/ade3be96-0e07-e47d-f64d-5306137e9ce7.png)
Zip File Systemの提案

#Zip File Systemとは
ユーザーから見えるファイルは全てxmlをベースとしたrefwrapファイルとしそのファイルはAppleのエイリアスのような形で保持する。refwrapファイルの個数を調べるにあたりガベレージコレクタを利用し、全てのrefwrapファイルがなくなると、ハードリンクが削除される。

### この形式のメリット
1. シンボリックリンクと違い、ガベレージコレクタが管理しているため参照先の名前を変更してもリンクが切れることはまずない。
1. ユーザーにとってはファイルとフォルダしかないため非常に分かりやすい
1. 参照先を簡単に変更することが出来る。
1. 場合によってリンク先を変えることが出来る。
1. zfs対応のものだと移動コマンドで移動できるようになる。

### デメリット
1. JavaやC#といった言語で行うため自由度が低い
1. 場合により遅くなることもある

## このような形式
```
Files.zip{#`ファイル.refwrap`を押したためユーザーには`/ファイル`のように見えている
.winbld_refwrap#高速化のためのファイル
FileA.txt.refwrap
Youtube.refwrap
Dir.refwrap
}
```

```xml:FileA.txt.refwrap
<ref>
<link>
<where types="Posix">
~/zfs/FileA.txt
<else types="Windows">
C:\Users\ningyo_yurihime\Documents\FileA.txt
</where>
</link>
</ref>
```

```xml:Youtube.refwrap
<ref>
<browser>
org.mozilla.firefox
</browser>
<url>
https://www.youtube.com
</url>
</ref>
```
```xml:Dir.refwrap
<ref>
<zfslnk>
Dir.zip
</zfslnk>
</ref>
```
忘備録：macにインストールされているサービスを探す

# macにインストールされているサービスを探す


port 80 を使っているhttpdがbrewで入れたものなのかどうなのかがわからなくなってしまい、いくつかのコマンドを実行して確認しました。結局はMac標準のhttpdだったのですが、コマンドが便利だったので備忘録として残します。

## brew

`brew services list` の実行

```bash
$ brew services list
Name    Status  User Plist
httpd   stopped
mysql   stopped
```

## Mac のサービス管理ツール(launchctl)

lunchyというものをインストールすると簡単になる。

```bash
$ brew install lunchy
```

`lunchy list` の実行

```bash
% lunchy list
com.amazon.music
com.amazon.music.startup
com.dropbox.DropboxMacUpdate.agent
com.google.inputmethod.Japanese.Converter
com.google.inputmethod.Japanese.Renderer
.
.
.
org.virtualbox.vboxwebsrv
```

## Mac標準のApacheの停止

これもluncyでできたかもしれない。。。

```
$ sudo launchctl stop /System/Library/LaunchDaemons/org.apache.httpd.plist
$ sudo launchctl unload /System/Library/LaunchDaemons/org.apache.httpd.plist
```
ItemsControlによる外観のカスタマイズ

#はじめに
WPF学習中のため自分用に記載していきます。
間違い、不足などありましたらコメントをよろしくお願いします。

#●ItemsPanelプロパティ
コレクション項目をどのようにレイアウトするかを決定する。

```C#
~省略~
<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <!--(1) <StackPanel Orientation="Vertical" />-->
        <!--(2) <StackPanel Orientation="Horizontal" />-->
        <!--(3) <WrapPanel Orientation="Vertical" />-->
        <!--(4) <WrapPanel Orientation="Horizontal" />-->
        <!--(5) <Grid/>-->
        <!--(6) <UniformGrid/>-->
        <!--(7) <UniformGrid Rows="5"/>-->
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>
```
●(1)、(2)
　StackPanelで項目を並べる。
　Orientationプロパティで、縦方向、横方向を指定できる。
　ItemsPanelプロパティを指定しない場合は、(1)StackPanel Orientation="Vertical"の形式で表示される。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/1d2886ba-cdd7-42d4-60dc-648c705cda91.png">　<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/e585b135-16b4-e1c9-c320-db21e51b99d5.png">

●(3)、(4)
　WrapPanelで項目を並べる
　StackPanelと同様、Orientationプロパティで、縦方向、横方向を指定できる。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/e255e69c-dc2f-e4d1-2ee2-784efb99fb45.png">　<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/08a53e0d-f47c-b987-0f22-0c01e8ff1374.png">

●(5)
　Gridで項目を並べる。子要素を順番に並べる機能を持たないため、重なって表示される。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/44fc2d05-121c-a82b-f0ba-7e116583b9d0.png">

●(6)、(7)
　UniformGridで項目を並べる。等間隔にコレクションを配置する。
　Columnsプロパティ、Rowsプロパティを設定して、列数、行数を指定することができる。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/255a0edd-e730-6156-15ec-fdf5a8fd1de8.png"> <img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/5bdcb8c3-188e-54e9-9585-f829af6ec9d3.png">


***
#●ItemTemplateプロパティ
DataTemplateを指定してどのようにオブジェクトを表示するかを決定する。


```C#
// オブジェクトの定義
public class Person
{
   public string Name { get; set; }
   public int Age { get; set; }
}
```
```C#
<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
        <UniformGrid Columns="5"/>
    </ItemsPanelTemplate>
</ListBox.ItemsPanel>

<ListBox.ItemTemplate>
    <DataTemplate>
        <TextBlock Margin="10">
            <Run Text="Name:" Foreground="Red"/>
            <Run Text="{Binding Name}" Background="LightYellow"/>
            <LineBreak/>
            <Run Text="Age:" FontStyle="Oblique"/>
            <Run Text="{Binding Age}" FontSize="25"/>
        </TextBlock>
    </DataTemplate>
</ListBox.ItemTemplate>

```
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454547/865c5850-4d9d-3912-c946-8e2ff4a90111.png">

***

#参照
[ItemsControl 攻略 ～ 外観のカスタマイズ grabacr.nét](http://grabacr.net/archives/1240)
anacondaのproxy設定方法（Ubuntu16.04）

# はじめに
anacondaでのパッケージインストールの際にproxyを利用する場合の設定方法です。

# 動作確認済み環境
- Ubuntu16.04
- Anaconda 2020.11 (conda version=4.9.2)

# 設定方法
anacondaがインストールされているディレクトリ直下の.condarcを編集します
(通常はホームディレクトリ)

```
$ vi anaconda3/.condarc 
```


下記を追記します

```
proxy_servers: 
    http: 【proxyアドレス】:【ポート】
    https: 【proxyアドレス】:【ポート】
```

以上で完了です
【軌跡-DVA】今日からawsデベロッパーアソシエイト(DVA)の学習を始まります。

#【軌跡-DVA】今日からawsデベロッパーアソシエイト(DVA)の学習を始まります。
##前書き
‘今日’を書いたけど、実にウソ〜。
会社が推奨されているため、2020年12月にAPNのアカウントを作りました。
正直仕事ながらの勉強は辛いです、仕事内容もawsとまったく無関係で、アカウントを開通してもawsを触るのはec2くらいです。（会社に怒られますね）
2020年12月から通算で平均0.5時間/日*150日=約75時間くらい勉強したかと思います。主にクラウドプラクティショナー(CLF)の認証のため、5/7にCLFの認証を取得しました。DVAの学習はこのGWから始まります。

##きっかけ
DVAを始まりのきっかけは3度目の緊急事態宣言のGW、首都圏に住んでいるため、県をまたぐ移動自粛中、どこにも行かないことを決めました、連休が勿体無い、CLFもそろそろ取れるので、レベルアップに何か習おうと思うと、自分は開発経験１０数年あるので、DVAを目指すことにしました。

##勉強方法について
調べたらなんか１週間、２週間でDVA取得できた方もいるのようですね、自分は凡人しすぎるので、3ヶ月で合格できるように計画を立ててました。
####１．対策本を買いました。
アマゾンのURL：https://www.amazon.co.jp/gp/product/4798063401/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1
山下光洋先生著　ポケットスタディ AWS認定 デベロッパーアソシエイト (アソシエイト試験ポケットスタディ) 単行本

CLFの時はひとりでコツコツ資料を読んだり、awsトレーニング講習を学習したり、確実に進んでますが、少し遅い気がします。

Black Belt Online Seminarもconnpassも使っております。

今回はconnpassで山下先生の【GW10日間毎朝チャレンジ】を見つかりました。毎日３０分だが、すごく分かりやすく講習だったので、ついつい対策本を買えました。480ページあり、自分のベースだと毎日1-2時間(確保できるかな)、15ページ行けるか、1 ヶ月で本を読み終わって、2ヶ月ぐらいの実習（各サービスを触って見たり、問題集を解いたり）をして、トータルで3ヶ月ぐらいの学習で、8月の夏休みにDVAの認証を取得出来るか軽く目標を設定しました。

####２．実習
各サービスを触って見たい。
山下先生のYouTubeにも一部サービスの講習あります、共有します。

https://m.youtube.com/watch?v=ZbAwBFuFcNc
AWS Codeサービスのデモ(「ポケットスタディ AWS認定 デベロッパーアソシエイト」のデモ)

https://m.youtube.com/watch?v=GvLChaMudsY&t=42s
AWS CloudFormationのデモ(「ポケットスタディ AWS認定 デベロッパーアソシエイト」のデモ)

tocci3さんが書いたブロックにたくさんの学習資源があります。
https://techblog.nhn-techorus.com/archives/17830

####３．学習内容をまとめる
自分だけかもしれませんが、学習した内容はすぐ忘れてしまいます、いつもわかっていたつもりだが、説明するとき違うのを気が付きます。ここで学習のメモや資料を不定期に共有します、初めてのブロックです、一緒に勉強する方居たら声をかけて下さい。

山下先生のTwitter ハッシュタグ#yamamugi

※本記事の内容は2021年5月時点の情報です。
Nuxt.jsでasync/asyncData/awaitがわからなかったのでまとめた

# はじめに
Rails+Nuxt.jsをやっていてわからなかったので頭の整理も兼ねてまとめます。



<!-- 各チャプター -->
<a id="#Chapter1"></a>
# 今回わからなかったコード
```
 async asyncData ({ $axios }) {
    let users = []  //空配列
    await $axios.$get('/api/v1/users')   //nuxt axiosの記述でデータを取得する
    .then(res => (users = res))     //返ってきたデータをusers配列に格納
    return { users }        //usersの配列をtemplateで使用できるように返す
```

<a id="#Chapter2"></a>
# ```async　asyncData ({＄axios}) {```

**async**
Promiseを返してくれる。
Promiseとは外部からデータを取得した際に成功した処理と失敗した処理を返してくれるクラス

**asyncData()**
コンポーネントの作成前に実行させる。axiosは読み込みに時間がかかるため最初に読み込みたいのでここで記載する。Nuxt.js作成の際にaxiosを選択した為、今回の場合はNuxtのmoduleからaxiosを持ってきている。




<a id="#Chapter3"></a>
# ```await $axios.$get('/api/v1/users')```
**await**
Promiseを返すまでJavaScriptを待機させる。
axiosの通信は時間がかかるため、確実に処理を実行するために```await```をつける

**\$axios.$get(取得先)**
axiosで取得先を指定しています。$がつくのはNuxt.jsの書き方。

<a id="#Chapter４"></a>
# ```.then(res => (users = res)) ```

**.then(res => (users = res))**
先ほどPromiseは成功した時の処理と失敗した時の処理を返すと書いたが、**.then(処理)**では成功した時の処理を書くことができる。
```then```の中身を見ると```users```の空配列に```res```に格納されている処理結果を代入している。今回は```axios```を使用してRailsとやりとりしているため、```then```の中身にはRailsで作成したユーザーデータが入っている。
最後**return**をしてtemplate内で使えるようにしてあげる
<a id="#reference"></a>
TornadoFXを使う

# はじめに
TornadoFXを使う方法を説明します。

# 環境
動作実績のある組み合わせです。

|#|IntelliJ IDEA|JDK|Kotlin Plugin|Tornadofx Plugin|
|:-:|:-:|:-:|:-:|:-:|
|1|2018.2.8|8u291|1.2.51-release-IJ2018.2-1|1.7.17.2|
|2|2020.2.4|8u291|202-1.5.0-release-760-IJ8194.7|1.7.20|

バージョン不整合にともなうエラーに遭遇しました。
実績のある組み合わせを使うことをお勧めします。

以降の説明は#2の環境を使います。

# 手順

IntelliJ IDEAを起動します。
"New Project"を選択します。

"1.8.0_291"を選択します。
"TornadoFX" - "tornadofx-gradle-project-kts"を選択します。次の図を参照ください。
"Next"を押下します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/97969edd-15d1-28e9-3af3-1108f86e2929.png)

"Project name"を入力して"Finish"を押下します。

"Import Gradle Project"を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/ced3eb55-5534-0f15-2097-cb8ee6292450.png)

[Run]-[Edit Configurations...]を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/52bfeb1b-26d5-19c6-00a6-9b69b3ec0ccb.png)

"+" - "TornadoFX"を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/af74f86f-5e0a-d4c1-dddd-b45e95fb8cbf.png)

"Name"を入力します。
"App Class"を選択します。
"Use classpath of modules"を選択します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/fdbb81f0-627c-ca44-bbd3-ed352a5fb874.png)

"OK"を押下します。

"Run"ボタンを押下、または、Shift + F10を押下します。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84205/387424e3-9b27-b4d0-06bc-d44625ddbbbe.png)

"Hello TornadoFX"のWindowが表示されます。