OmniAICreator/Qiita-1.07M huggingface dataset:
Fedora39でminikube - インストールと起動(1)

まずは、Fedora39 に minikube をダウンロードします。

```BASH
@fedora:~$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube && rm minikube-linux-amd64
```
VirtualBox を使って、minikube を立ち上げます。

```BASH
@fedora:~$ minikube start --driver=virtualbox
😄  Fedora 39 上の minikube v1.32.0
✨  ユーザーの設定に基づいて virtualbox ドライバーを使用します
💿  VM ブートイメージをダウンロードしています...
    > minikube-v1.32.1-amd64.iso....:  65 B / 65 B [---------] 100.00% ? p/s 0s
    > minikube-v1.32.1-amd64.iso:  292.96 MiB / 292.96 MiB  100.00% 11.89 MiB p
👍  minikube クラスター中のコントロールプレーンの minikube ノードを起動しています
💾  ロード済み Kubernetes v1.28.3 をダウンロードしています...
    > preloaded-images-k8s-v18-v1...:  403.35 MiB / 403.35 MiB  100.00% 12.41 M
🔥  virtualbox VM (CPUs=2, Memory=6000MB, Disk=20000MB) を作成しています...
🐳  Docker 24.0.7 で Kubernetes v1.28.3 を準備しています...
    ▪ 証明書と鍵を作成しています...
    ▪ コントロールプレーンを起動しています...
    ▪ RBAC のルールを設定中です...
🔗  bridge CNI (コンテナーネットワークインターフェース) を設定中です...
    ▪ gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています
🌟  有効なアドオン: storage-provisioner, default-storageclass
```
statusを確認します。
```BASH
@fedora:~$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
```
cluster-infoを確認します。
```BASH
@fedora:~$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.59.100:8443
CoreDNS is running at https://192.168.59.100:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```
minikubeのIPアドレスを確認します。

```
@fedora:~$ minikube ip
192.168.59.100
```

VirtualBox には、minikube というマシンが起動しています。
確認すると、この様に表示されています。

```BASH
Welcome to minikube
minikube login:
```

コンソールの方は、ctl+C を押下しておきました。
(参考)[minikube start](https://minikube.sigs.k8s.io/docs/start/)
[Fedora39でminikube - インストールと起動(2)](https://qiita.com/intrajp/items/053509b11abcf97ce90d)に続く
Kusto Query Language（KQL）の join、union、サブクエリの例 (SQL の補足付き)

データ分析において、複数のテーブル間でデータを結合したり、複雑なクエリを実行する必要があります。  
この記事では、Kusto Query Language (KQL) と SQL での join、union、サブクエリの例を紹介します。

## join、union、サブクエリの例

### KQL での join の使い方

KQL では、`join`キーワードを使用し、`kind = xx`にて異なるテーブルの結合方法を指定します。  
以下は、2 つのテーブルを`join`して結果を取得する例です。

```kql
SecurityEvent
| where EventID == "4624"
| summarize LogOnCount=count() by Account
| project LogOnCount, Account
// JOIN でアカウントごとのログオフ回数出して、結合させる
| join kind = inner (
    SecurityEvent
    | where EventID == "4634"
    | summarize LogOffCount=count() by Account
    | project LogOffCount, Account
    ) on Account
```

- 結合種類の詳細について参考記事
  - [join 演算子](https://learn.microsoft.com/ja-jp/azure/data-explorer/kusto/query/join-operator?pivots=azuredataexplorer)
  - [Microsoft Sentinel / Log Analytics で結合クエリー (JOIN) を使いこなす](https://qiita.com/hisnakad/items/d2e2de72cfc47e75e901)

SQL でも`JOIN`句を使用して異なるテーブルを結合します。  
以下は、2 つのテーブルを`JOIN`して結果を取得する例です。

```sql
SELECT
    s1.Account,
    s1.LogOnCount,
    s2.LogOffCount
FROM
    (SELECT
        Account,
        COUNT(*) AS LogOnCount
    FROM
        SecurityEvent
    WHERE
        EventID = '4624'
    GROUP BY
        Account) AS s1
JOIN
    (SELECT
        Account,
        COUNT(*) AS LogOffCount
    FROM
        SecurityEvent
    WHERE
        EventID = '4634'
    GROUP BY
        Account) AS s2
ON
    s1.Account = s2.Account
```

### KQL での union の使い方

KQL の例

```kql
Table1
| project Column1, Column2;
| union
(
    Table2
    | project Column3, Column4;
)
```

```kql
// View1 を定義
let View1 =
    Table1
    | project Column1, Column2;

// View2 を定義
let View2 =
    Table2
    | project Column3, Column4;

// View1 と View2 を結合
View1 | union View2
```

SQL の例

```sql
-- クエリ1
SELECT Column1, Column2
FROM Table1

UNION

-- クエリ2
SELECT Column3, Column4
FROM Table2
```

### 入れ子になったクエリの例

KQL の例

```kql
SigninLogs
| where TimeGenerated > ago(1h) and ResultType == toscalar(
    SigninLogs
    | where TimeGenerated == '2024-04-05T07:32:54.567152Z'
    | project ResultType
)
```

SQL の例

```sql
SELECT *
FROM SigninLogs
WHERE TimeGenerated > DATEADD(HOUR, -1, GETUTCDATE())
AND ResultType = (
    SELECT ResultType
    FROM SigninLogs
    WHERE TimeGenerated = '2024-04-05 07:32:54.567152'
    AND CAST(TimeGenerated AS DATETIME) = '2024-04-05 07:32:54.567152'
)
```

## 参考記事

- [join 演算子](https://learn.microsoft.com/ja-jp/azure/data-explorer/kusto/query/join-operator?pivots=azuredataexplorer)
- [Microsoft Sentinel / Log Analytics で結合クエリー (JOIN) を使いこなす](https://qiita.com/hisnakad/items/d2e2de72cfc47e75e901)
[Google Calendar API(V3)] Javaでカレンダーの変更を監視する

Google Calendar API V3(Java)を使用して、
Googleカレンダーの変更を監視する方法
についてご紹介します。

:::note info
APIを利用する環境の準備から始める場合や、コードを実行する際は、
[⧉[Google Calendar API(V3)] JavaでCalendar APIを使う](https://qiita.com/doran/items/c735c7b05c0a2ed4bfdb)
を参照ください。
:::

|No||目次|
|---|---|---|
|1||[カレンダーの変更を監視](#1-カレンダーの変更を監視)|
||1|[スコープ](#11-スコープ)|
||2|[実行](#12-実行)|
||3|[レスポンスの内容](#13-レスポンスの内容)|
|2||[カレンダーの監視を停止](#2-カレンダーの監視を停止)|
|3||[変更通知を受信](#3-変更通知を受信)|

## 1. カレンダーの変更を監視
Googleカレンダーの変更を監視します。

### 1.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
CalendarScopes.CALENDAR
CalendarScopes.CALENDAR_READONLY
```
[⧉[Google Calendar API(V3)] JavaでCalendar APIを使う(2.2 Driveインスタンスを取得)](https://qiita.com/doran/items/c735c7b05c0a2ed4bfdb#22-calendarインスタンスを取得)
でスコープを指定してください。

### 1.2. 実行

```java
public static void main(String[] args) throws Exception{
    Calendar calendar = getCalendar();
    Calendar.CalendarList calendarList = calendar.calendarList();
    
    Channel content = new Channel();
    content.setId("チャンネルを識別するUUID");
    content.setType("web_hook");
    content.setAddress("変更通知が配信されるURL");
    Calendar.CalendarList.Watch watch = calendarList.watch(content);
    
    Channel res = watch.execute();
    System.out.println(res);
}
```
#### 1.2.1. HTTPリクエスト
POST https:\//www.googleapis.com/calendar/v3/users/me/calendarList/watch
が実行されます。

#### 1.2.2. クエリパラメータ
クエリパラメータはありません。

#### 1.2.3. リクエストボディ
Channelのsetメソッドにより、クエリパラメータを追加できます。

|メソッド|引数|説明|
|---|---|---|
|setId|String|【必須】チャンネルを識別するUUID|	
|setAddress|String|【必須】変更通知が配信されるURL|
|setType|String|【必須】このチャンネルで使用される配信方法の種類<br>"web_hook"（または"webhook"）のみ|
|setToken|String|このチャンネル経由で通知と一緒に対象のアドレスに配布される任意の文字列|	
|setParams|Map\<String,String\>|配信チャンネルの操作を制御する追加のパラメータ|	
|setExpiration|Long|通知チャンネルの有効期間（秒）|

### 1.3. レスポンスの内容

#### Channel

|メソッド|戻り値|説明|
|---|---|---|
|getKind|String|リソースの種類<br>固定文字列："api#channel"|
|getId|String|このチャンネルを識別するUUID|	
|getResourceId|String|このチャンネルで監視するリソースを識別するID|
|getResourceUri|String|監視対象のリソースのバージョン固有のID|	
|getToken|String|このチャンネル経由で通知と一緒に対象のアドレスに配布される任意の文字列|
|getExpiration|long|通知チャンネルの有効期限|

## 2. カレンダーの監視を停止
Googleカレンダーの監視を停止します。

変更通知の停止は、
[⧉[Google Calendar API(V3)] Javaで変更通知を停止する](https://qiita.com/doran/items/3c3ad1d6406d8fac634e)
の記事を参照ください。

## 3. 変更通知を受信
カレンダーの変更があった場合、登録したURLへPOSTされます。
HTTPヘッダーの「x-goog」から始まるヘッダーの内容で確認できます。

|ヘッダ|内容|例|
|---|---|---|
|x-goog-channel-expiration|通知チャネルの有効期限|Tue, 02 Apr 2024 03:29:03 GMT|
|x-goog-channel-id|チャンネルを識別するUUID|test|
|x-goog-message-number|通知チャネルのこのメッセージを識別する整数|61371|
|x-goog-resource-id|チャンネルで監視するリソースを識別するID|V0h2iEZpuFWTY1Y2BPmYPm3hnsg|
|x-goog-resource-state|通知をトリガーした新しいリソースの状態<br>sync,exists,not_exists|exists|
|x-goog-resource-uri|監視対象リソースの API バージョン固有の識別子|`https://www.googleapis.com/calendar/v3/users/me/calendarList?alt=json`|

<br>
<br>
おしまい。。
Kusto Query Language（KQL）の基本的なクエリの書き方 (SQL の補足付き)

Kusto Query Language（KQL）は、Azure Data Explorer やその他の Azure データサービスで使用されます。  
この記事では、KQL のクエリの基本形式について解説します。  
目指すのは下記で、今後の記事に繋がるように、と思っています。

- Grafana で KQL クエリを利用してグラフ化
- Grafana で Azure 上の疑わしい挙動が発生していないか監視

## 環境

個人で検証用のログを置くにも費用が必要となる可能性があります。  
マイクロソフトで提供している下記環境を利用することで Kusto Query Language（KQL）を実際に動かしながら確認を進められます。

- [Log Analytics のチュートリアル](https://learn.microsoft.com/ja-jp/azure/azure-monitor/logs/log-analytics-tutorial)
- [Log Analytics のデモ環境](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/DemoLogsBlade)

## KQL の基本概念

KQL はデータを検索、分析するための豊富な操作を提供しますが、基本的には以下の要素から構成されます。

- **データベース**: KQL クエリを実行する際のデータ格納先です。
- **テーブル**: データベース内でデータが格納される場所。行と列でデータが構成されます。
- **列**: テーブル内のデータの属性を表します。各列は特定のデータ型を持ちます。
- **データ型**: KQL では、数値、文字列、日付など、さまざまなデータ型がサポートされています。

## KQL クエリの基本形

### データの参照

基本的な構文は次の通りです。  
SQL 知っているとイメージしやすく、かゆい場所には手が届かない、歯がゆい感じです。

```kql
// KQLで特定の列を選択
TableName                   // 参照テーブル
| project Column1, Column2  // 参照列
```

```sql
-- SQLで特定の列を選択
SELECT Column1, Column2     -- 参照列
FROM TableName              -- 参照テーブル
```

### データのフィルタリング

KQL では、`where`句を使用して特定の条件を満たす行をフィルタリングします。

```kql
// KQLで条件に基づくデータのフィルタリング
TableName
| where Column1 == 'Value'
| project Column1, Column2
```

SQL でも`WHERE`句を使用して条件に基づいてデータをフィルタリングします。

```sql
-- SQLで条件に基づくデータのフィルタリング
SELECT Column1, Column2
FROM TableName
WHERE Column1 = 'Value'
```

### グループ化と集約関数

KQL では、`summarize`ステートメントを使用してデータをグループ化し、集約関数（`count`、`sum`、`avg`など）を適用します。

```kql
// KQLで特定の列に基づいてデータをグループ化し、集計
TableName
| summarize Count = count(), sumCount = sum(Column2), Average = avg(Column2) by Column1
```

SQL では、`GROUP BY`句を使用してデータをグループ化し、`SELECT`句で集約関数を適用します。

```sql
-- SQLで特定の列に基づいてデータをグループ化し、集計
SELECT Column1, COUNT(*) AS Count, SUM(Column2) AS Total, AVG(Column2) AS Average
FROM TableName
GROUP BY Column1
```

### 時間に基づくデータ集約

Grafana でグラフ化後、時間を指定した範囲のデータに絞り込みを行うことで把握情報を特定します。  
Grafana に情報を連携する場合、「時間に基づくデータ集約」が非常に重要な意味を持ちます。  
KQL では、`bin()`関数を使用して時系列データを時間間隔ごとにバケット分けします。これにより、データの集約や比較が容易になります。

```kql
// 1時間ごとにデータを分け、イベント数をカウント
TrendLogs
| summarize EventCount = count() by bin(TimeGenerated, 1h)
```

```kql
// 30分ごとにデータを分け、平均を計算
TemperatureSensors
| summarize AvgTemp = avg(Temperature) by bin(TimeGenerated, 30m)
```

SQL では、`DATEPART`や`FORMAT`などの関数を組み合わせて使用することが一般的です。

```sql
-- 1時間ごとにイベント数をカウント
SELECT DATEPART(hour, Timestamp) AS Hour, COUNT(*) AS EventCount
FROM TrendLogs
GROUP BY DATEPART(hour, Timestamp)
```

```sql
-- 一部のSQLデータベースではこのような直接的な方法が利用できない場合があります
SELECT FORMAT(Timestamp, 'yyyy-MM-dd HH:mm:00') AS TimeBucket, AVG(Temperature) AS AvgTemp
FROM TemperatureSensors
GROUP BY FORMAT(Timestamp, 'yyyy-MM-dd HH:mm:00')
```

## 参考記事

- [公式の KQL ドキュメント](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/)
- [Log Analytics のチュートリアル](https://learn.microsoft.com/ja-jp/azure/azure-monitor/logs/log-analytics-tutorial)
- [Log Analytics のデモ環境](https://portal.azure.com/#blade/Microsoft_Azure_Monitoring_Logs/DemoLogsBlade)
【備忘録】 Next.jsで関数の実行をdebounceする

# はじめに
この記事は **use-debounce** の使い方の **備忘録** です。

## debounceとは
**一定時間内** の **関数実行** の **回数** を **制限** することです。

**debounce** の **サイクル** は
**1**.**debounceされた関数** の **実行** が行われると **タイマーが開始** される。
**2**.**タイマーが終わる** までに **debouceされた関数** が **再度実行** されると **タイマー** が **リセット** される。
**3**.**タイマーが終わる** と **debouceされた関数** の **callback関数** が **実行** される。

という**サイクル**です。
## インストール
```
npm install use-debounce
```

## callback関数をdebounceする
```tsx:app/page.tsx
"use client";
import { useDebouncedCallback } from "use-debounce";

export default function Home() {
  const debounceCallback = useDebouncedCallback((e) => {
    console.log(e.target.value);
  }, 1000);
  
  return (
    <div>
      <input onChange={(e) => debounceCallback(e)} defaultValue={"Hello"}/>
    </div>
  );
}

```

## さいごに
学習させていただいた先・ライブラリの公式ドキュメントです。

https://www.npmjs.com/package/use-debounce?activeTab=readme

https://github.com/xnimorz/use-debounce
【ROS2 colcon test Tips】pythonパッケージのコードスタイルチェックを無視する方法

# はじめに
　ROS2でPythonパッケージを作成すると、デフォルトで以下のテストコードが作成されるが、colcol test実行時に実行されてしまいエラーとなることがある。そこで、この問題を解決するために、下記テストを無視する方法を紹介する。

 ```bash
 └── test
    ├── test_copyright.py
    ├── test_flake8.py
    └── test_pep257.py
 ```

 |テストファイル|概要|
 |:-:|:-:|
 |test_copyright.py|コピーライトチェック|
 |test_flake8.py|Pythonコードスタイルチェック(flake8)|
 |test_pep257.py|Python docstringsチェック([PEP257](https://peps.python.org/pep-0257/))

# 動作確認環境

* ROS2 Foxy

# 方法

 ```@pytest.mark.skip```をテスト関数の前に書くと無視される。任意の引数のreasonには、理由を入れることが可能、引数指定はなくても可。

例： test_copyright.py を無視したい場合
 ```python
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from ament_copyright.main import main
import pytest

@pytest.mark.skip(reason='Not implemented')
@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
    rc = main(argv=['.', 'test'])
    assert rc == 0, 'Found errors'
 ```

 # 参考

 https://docs.pytest.org/en/stable/how-to/skipping.html#skipping-test-functions

 # 関連情報
 
 C++プロジェクト(cmake)の場合

 https://qiita.com/ntrlmt/items/c0896ca591b031a97f53
サクッと API を作ろう｜FastAPI, Hugging Face,Docker,Python



## はじめに

API として提供することで、アプリケーションへの統合が容易になります。この記事では、FastAPI と Hugging Face を使用して、シンプルな API を素早く構築し、デプロイする方法を紹介します。
Google Croud Runを試行錯誤しましたが、うまくできませんでした。hugging faceならさくっとできました。

## 前提条件
- Hugging Face のアカウントを持っていること

## 手順

### ステップ 1: サンプルの Hugging Face Space を複製する

https://huggingface.co/spaces/tregu0458/FastapiSample

1. Hugging Face にログインします。
2. `https://huggingface.co/spaces/tregu0458/FastapiSample` にアクセスします。
3. ページ右上の "Duplicate this Space" ボタンをクリックします。
4. "Duplicate Space" ダイアログが表示されたら、新しい Space の名前を入力し、"Space visibility" を選択して、"Duplicate Space" ボタンをクリックします。
5. 複製した Space のページにリダイレクトされます。

### ステップ 2: 複製した Space を編集する

1. 複製した Space のページで、"Files" タブをクリックします。
2. `main.py` ファイルをクリックして、コードを編集します。
3. 必要に応じてコードを変更し、"Commit changes" ボタンをクリックして保存します。

### ステップ 3: API ドキュメントを見る

1. "enbed This space"から、URLをひらく
1. 例えば、`https://huggingface.co/spaces/tregu0458/FastapiSample`
1. 例えば、`https://huggingface.co/spaces/tregu0458/FastapiSample\docs`を開く
    1. APIドキュメントがみられる

### ステップ 4: 必要に応じて API を拡張する

1. `main.py` ファイルを編集して、新しいエンドポイントを追加したり、既存のエンドポイントを変更したりします。
2. 変更をコミットし、Space をアップデートします。
3. 更新された API をテストします。

## まとめ

この記事では、Hugging Face Spaces にあるサンプルの FastAPI アプリケーションを複製し、編集してデプロイする方法を紹介しました。この手順を応用して、独自の API として提供することができます。FastAPI と Hugging Face を組み合わせることで、シンプルかつ効率的に API を構築し、デプロイすることが可能です。

## 参考資料

- [Hugging Face Spaces のドキュメント](https://huggingface.co/blog/HemanthSai7/deploy-applications-on-huggingface-spaces)

```Dockerfile:Docker
FROM python:3.10.9

COPY . .

WORKDIR /

RUN pip install --no-cache-dir --upgrade -r /requirements.txt

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
```

```python:app.py
from fastapi import FastAPI

app = FastAPI(title="Deploying FastAPI Apps on Huggingface")

@app.get("/", tags=["Home"])
def api_home():
    return {'detail': 'Welcome to FastAPI Tutorial!'}

@app.post("/",tags=["hello"])
def hello(name: str):
    return {'message': f"hello {name}!"}
```
```plaintext:requirements.txt
fastapi==0.99.1
uvicorn
requests
pydantic==1.10.12
```

上記のテキストは[クリエイティブ・コモンズ 表示-継承ライセンス](https://creativecommons.org/licenses/by-sa/4.0/deed.ja)のもとで利用できます。
共通化できるパーツをテンプレート化してインクルードする方法

ページネーションや検索フォームなど、複数のテンプレートファイルで共通して使用されるパーツを作成し、それらを必要な場所にインクルードする方法について説明します。このアプローチにより、コードの再利用性が向上し、テーマの整理とメンテナンスが容易になります。

今回は、ページネーションをテンプレート化する想定。

## テンプレートパーツフォルダを作成

`template-parts` という名前のフォルダをテーマのルートディレクトリに作成します。このフォルダは、分割して再利用したいテンプレートファイルを確認する場所です。

## テンプレートファイルの作成

`template-parts` フォルダ内に、 `pagination.php` という名前のファイルを作成し、その中にページネーションを表示するためのコードを記述します。

## テンプレートパーツの呼び出し

```php
<?php get_template_part('template-parts/pagination'); ?>
```

テンプレートファイル（例えば、投稿一覧を表示する `index.php` や `archive.php` など）でページネーションを表示したい箇所に、上記のコードを挿入します。これにより、指定したパスにある `pagination.php` ファイルのコンテンツが読み込まれ、ページネーションが表示されます。
macOS備え付けのエディタ：nanoを使ってみた

# はじめに

CLIでファイルをちょっとだけ編集したい！って時に使えるエディタ **nano** の紹介です。

macOSだけでなく、UNIX系のOSならデフォルトで用意されているようです。
# 使い方

`nano 対象ファイル`（もしくは、`pico 対象ファイル`）

picoはnanoの前身となるエディタです。

コマンドにおいてはただのシンボリックリンクで、同じものを指すらしい。
# nanoとは？

公式サイトのREADME

https://www.nano-editor.org/dist/latest/README

公式サイト

https://www.nano-editor.org/

気になる方はここをお読みください。
# vi / vim / code コマンドとの比較

`vi 対象ファイル`：エディタ「Vi」で対象ファイルを開く
`vim 対象ファイル`：エディタ「Vim」で対象ファイルを開く
`code 対象ファイル・ディレクトリ`：エディタ「VSCode」で対象ファイル・ディレクトリを開く

さて、これらと比較して`nano`に優位性はあるでしょうか。

## `nano` vs `code`

VSCodeと違うのは、
- インストール不要
- 軽量
- 幅広いシステムで利用可能

であることです。

特に、リモートサーバーで作業する際などはVSCodeが使用不可です。
特定の状況では、nanoに優位性があると言えるでしょう。

## `nano` vs `vi` / `vim`

vi / vimと違うのは、
- 学習コストが低い
- 入力の際、`i`や`a`を押す必要がない
- コマンドのリストが表示される

![スクリーンショット 2024-04-06 14.24.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/ed4ac82c-c71e-a6cf-97d0-296a59cbf6f8.png)

vi / vim はCLI上の編集のど定番ですが、学習コストが高いです。
また、個人的には`a` / `i`を押し挿入モードに入らないといけないのが手間。

その点、`nano`は簡単です。普段から慣れた方法で入力ができます。

# おわりに

CLIだけで編集できるのはかっこいいですよね、憧れ。
`nano`はマイナーなのも通な感じで良き。

簡単に使えるのでぜひ使ってみてください。


さて、次は何を書こうかしら・・・

# 参考

https://www.nano-editor.org/dist/latest/README

https://www.nano-editor.org/

[エンジニアなら知っておきたいmacOS環境のキホン コマンド・Docker・サーバなどをイチから解説 | 大津 真 |本 | 通販 | Amazon](https://www.amazon.co.jp/%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AA%E3%82%89%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84macOS%E7%92%B0%E5%A2%83%E3%81%AE%E3%82%AD%E3%83%9B%E3%83%B3-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%BBDocker%E3%83%BB%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AA%E3%81%A9%E3%82%92%E3%82%A4%E3%83%81%E3%81%8B%E3%82%89%E8%A7%A3%E8%AA%AC-%E5%A4%A7%E6%B4%A5-%E7%9C%9F/dp/4295015032)
VDatePickerの初期値が当月（当日）になってしまう

v-date-pickerのv-modelに来月の日付をセットしても、初期表示が当月になってしまう。
来月に移動すると、ちゃんと来月の日付がハイライトされているので認識はされているもよう。

## v-date-pickerのドキュメントを見る
https://vuetifyjs.com/en/components/date-pickers/#guide

関係ありそうなプロパティをセットしてみたが、どれも効果なし。

## ググる
誰も言及していない。。。vuetifyの情報少なすぎませんか？

## ソースを見に行く

年月が表示されているHTMLは以下。controlsのソースを見ればなにかわかるかも？
```html
<div class="v-date-picker-controls__date">2024年5月</div>
```

node_modules\vuetify\lib\labs\VDatePicker\VDatePickerControls.mjs

```javascript
const month = props.range === 'end' ? adapter.addMonths(props.displayDate, 1) : props.displayDate;
```
rangeなので、カレンダーの表示範囲をセットしていると思われる。
**displayDate...** これか！？

## 解決
これで初期表示がセットできました！

```vue
<v-date-picker
  hide-actions
  min-height="470"
  v-model="selectEndDateValues"
  @update:modelValue="selectEndCalendarDate()"
  :displayDate="new Date(selectEndDateValues[0])"
  >
</v-date-picker>
```
symbol-bootstrap v1.1.11 ｲﾝｽﾄｰﾙ

symbol-bootstrap v1.1.11
今回は　今までの様に
`sudo npm install -g symbol-bootstrap`
では　ｲﾝｽﾄｰﾙは出来ません
以下の手順で symbol-bootstrapを更新して下さい

:::note warn

**更新作業を行う前に！**
作業ﾃﾞｨﾚｸﾄﾘに入って
```
symbol-bootstrap decrypt --source addresses.yml --destination d_addresses.yml
```
target/addresses.ymlと言う fileがありますが
ここに　暗号化された nodeAccount(main/transport/remote/vrf/(votingKey))の情報があります
今回　symbol-bootstrapの更新を行った後で　nodeﾊﾟｽﾜｰﾄﾞが通らなくなる恐れがあります
暗号化ﾓｼﾞｭｰﾙの大幅な変更があった為です
addresses.ymlが暗号化されたまま symbol-bootstrapを更新すると
二度と nodeﾊﾟｽﾜｰﾄﾞが通らなくなり
**nodeの更新が不可能になる**事もあります
**これに対処する為に　上記ｺﾏﾝﾄﾞを作業ﾃﾞｨﾚｸﾄﾘから実行**して下さい
作業ﾃﾞｨﾚｸﾄﾘに 復号（秘密鍵がそのまま読める）された d_addresses.yml fileが作成されます
symbol-bootstrapの更新後に　nodeﾊﾟｽﾜｰﾄﾞが通らなくなった時は
この d_addresses.ymlを使用して　nodeﾊﾟｽﾜｰﾄﾞを再設定出来ます
:::

:::note alert
**※使用が終わった d_addresses.ymlは　別の安全な場所に保管して　serverからは削除して下さい
万が一　serverに侵入された場合　秘密鍵を見られてしまいます**
:::

**symbol-bootstrapの更新作業**
homeﾃﾞｨﾚｸﾄﾘへ移動
```
cd
```
symbol-bootstrapｲﾝｽﾄｰﾙ用の ﾃﾞｨﾚｸﾄﾘを作成（ここでは`sb`とします　名前は任意で）
```
mkdir sb
cd sb
```
Githubからの ﾀﾞｳﾝﾛｰﾄﾞ
```
git clone https://github.com/symbol/symbol-bootstrap
```
ﾀﾞｳﾝﾛｰﾄﾞした symbol-bootstrapﾃﾞｨﾚｸﾄﾘに移動
```
cd symbol-bootstrap
```
ｲﾝｽﾄｰﾙ
```
npm install
npm run build
sudo npm install -g .
```
:::note info

もし　`sudo npm install -g .`で ｴﾗｰが出た場合は
```
sudo npm uninstall -g symbol-bootstrap
```
を実行後　上記の　ｲﾝｽﾄｰﾙの命令を `npm install`から　やり直してみて下さい
:::

ｲﾝｽﾄｰﾙした symbol-bootstrapの ver.を確認する
```
symbol-bootstrap -v
```
ver.が 1.1.11になっていたら成功です

後はいつもの symbol-bootstrapと使い方は同じです

**symbol nodeの update作業**
nodeの作業ﾃﾞｨﾚｸﾄﾘに入って
```
symbol-bootstrap stop
symbol-bootstrap start --upgrade -d
```

:::note info
**この時
「ﾊﾟｽﾜｰﾄﾞが違う」ﾒｯｾｰｼﾞが出て　ｺﾏﾝﾄﾞが正しく動作しない時は**
```
cp d_addresses.yml target/addresses.yml
symbol-bootstrap start --upgrade -d
```
これで nodeﾊﾟｽﾜｰﾄﾞを新たに設定し直す事が出来ます
:::

# nodeが起動しない！
今回の symbol-bootstrapの updateでは
mongoの ver.が上がった事により
dbが起動しないで　nodeが動かない報告が多数挙がっています
その場合に付いての対処方法を記して置きました

https://qiita.com/dusanjp/items/155c95cf36012f613cf4
windows 10 + WSL (Ubuntu 22)にOpenFOAMをインストールする

# はじめに
以前にDocker環境にOpenFOAMをインストールする記事を書きましたが，環境の都合やそもそもネイティブのUbuntuを使用しているユーザー向けに書き直すことにしました．
事前にWSLの環境は構築済みであるとします．以降のコマンドはすべてWSL上で実行されます．Ubuntuで実行している場合はその素のターミナルになります．

## OpanFOAMのインストール
ここでは公式サイト（ https://develop.openfoam.com/Development/openfoam/-/wikis/precompiled/debian ）に示される，aptを使用する方法を使用します．

まず，aptにOpenFOAMをダウンロード先を認識させる準備をします．以下のコマンドを実行します．
```
curl https://dl.openfoam.com/add-debian-repo.sh | sudo bash
```
実行すると数行のダウンロード状況が出ますが，実はこの状態でsudo bashに必要な管理者パスワードを待ち受けています．そのため実行したあと管理者パスワードを入力してください．そうすると次の状況に進みます．

次に
```
sudo apt-get update
```
を実行してaptにOpenFOAMのダウンロード先を認識させます．

最後に以下のコマンドでaotのパッケージとしてOpenFOAMをインストールします．末尾の数字はバージョンによって変わります．
```
sudo apt-get install openfoam2312-default
```

## OpenFOAMの実行
インストールしたOpenFOAMを実行するには以下のコマンドを使います．
```
openfoam2312
```
これを実行するとOpenFOAMを実行するための環境に移動します．
離脱するためには
```
exit
```
を実行します．

チュートリアルの実行はこちら（ https://develop.openfoam.com/Development/openfoam/-/blob/master/doc/Build.md ）の項目「コンパイル後の手順」を参考にして行います．

OpenFOAMを実行するターミナルは実行時のカレントディレクトリを引き継いでいます．そこに実行するためのディレクトリを作ります．
```
mkdir run
```
ここにチュートリアルをコピーします．
```
cp -r "$FOAM_TUTORIALS"/incompressible/simpleFoam/pitzDaily ./
```
ここでいう$FOAM_TUTORIALSはチュートリアルが置かれたディレクトリです．このディレクトリは管理者権限が必要であるため，そこに移動して実行すると失敗します．そのためコピーをしています．

チュートリアルの実行には以下のコマンドを使います．
```
cd pitzDaily && blockMesh && simpleFoam
```
まずコピーしたpitzDailyに移動し，blockMeshを使用して解析用のメッシュを作成します．そしてsimpleFoamで解析を実行します．
うまく実行できた場合，ディレクトリの中に0以外の数値のディレクトリが作られます．

## チュートリアル実行結果の可視化
実行結果の可視化にはparaviewが必要です．これは最初からインストールされていない場合があるため，その場合以下のコマンドでインストールします．
```
sudo apt install paraview
```
このあと以下のコマンドを実行するとparaviewが立ち上がります．最初の状態では何もないのですが，赤で示すApplyを押すと結果が表示されます．
```
paraFoam
```
![openfoam.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335923/d59028fb-3f84-651d-0bb2-2b63fb5d33e6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335923/34dafcee-63ac-e906-b9db-8bca7cd183b3.png)

## おわりに
実行環境の構築とチュートリアルの実行は以上の通りです．
これは個人的な話ですが，人力飛行機のフェアリングの解析に使いたいと思いつつ，未だに実行できていないのでそろそろどうにかしたいですね．
Pythonで「テキストファイルへ書き込む」の動作を確認してみた

# 概要

Pythonで「テキストファイルへ書き込む」の動作を確認してみました。
以下のページを参考にしました。

https://www.javadrive.jp/python/file/index3.html

# 実装

以下のファイルを作成しました。
```sample.py
f = open('myfile.txt', 'w', encoding='UTF-8')
f.write('こんにちは\n')
datalist = ['お元気ですか？\n', 'それではまた\n']
f.writelines(datalist)
f.close()

f = open('myfile.txt', 'a', encoding='UTF-8')
f.write('それではまた\n')
f.close()
```

以下のコマンドを実行しました。
```
$ python3 sample.py 
$ cat myfile.txt 
こんにちは
お元気ですか？
それではまた
それではまた
```

# まとめ

何かの役に立てばと。
【たちまちメンテされなくなりそうな便利系スクリプトライブラリ】アカデミック編 ORCID

# ORCID パブリックAPIを使って研究者の業績データを取得する、たちまちメンテされなくなりそうなライブラリ

## 概要
所属する研究室で ORCID の公開データから業績一覧を取得して自動でホームページを更新できるようにするといったプロジェクトが最近ありました。

よくありそうな自動化プロジェクトの一種かと思います。

ただ案外、ORCIDのパブリックAPIに関するドキュメントが見当たりません。公式のドキュメントも researcher_id を持っている研究者自身が使うAPIに関してのものがほとんどでした。

ということで、いわゆる直ぐにメンテナンスされなくなる系のPython便利系スクリプトライブラリを公開しました。

https://github.com/passive-radio/retrieve-orcid

## インストール
```bash
pip install git+https://github.com/passive-radio/retrieve-orcid.git
```

## 使い方
今のところコマンドラインから業績を取得する機能に対応しています。

```bash
python -m retrieve_orcid.get_works -r {researcher_id} -o {path/to/output_file}
```

より詳細な使い方については、**[README | passvie-radio/retrieve-orcid](https://github.com/passive-radio/retrieve-orcid/blob/main/README.md)** をご覧ください。

## あとがき
「たちまちメンテされなくなりそうな...」と前置きしている通り、精力的に更新していく感じではございません。
スクリプトも小さいですので、リポジトリをフォークしたりソースを参考に自分のニーズに合わせて一から作ってみたり。お好きにお使いください。

## リファレンス
1. [ORCID](https://orcid.org)
1. [研究者を識別する「ORCID(オーキッド)ID」とは？調べ方と登録方法 | SOUBUN.COM](https://www.soubun.com/journal/%E7%A0%94%E7%A9%B6%E8%80%85%E3%82%92%E8%AD%98%E5%88%A5%E3%81%99%E3%82%8B%E3%80%8Corcid%E3%82%AA%E3%83%BC%E3%82%AD%E3%83%83%E3%83%89id%E3%80%8D%E3%81%A8%E3%81%AF%EF%BC%9F%E8%AA%BF%E3%81%B9%E6%96%B9/)
VSCodeをエディタにしたTypeScript環境を構築する

## 前提
Windowsであることと、Node.jsとVSCodeはインストール済みとする。
コンパイルされたJavaScriptはNode.jsで動かす前提で記載している。

## プロジェクトフォルダを作る
tssampleというフォルダを作る。

## package.jsonを作る
tssampleフォルダに以下内容のpackage.jsonファイルを作る。
```json:package.json
{
    "name": "tssample",
    "version": "1.0.0",
    "dependencies": {
        "typescript": "5.4.4"
    }
}
```
このpackage.jsonにはTypeScriptをインストールするための設定が書かれている。

## TypeScriptをインストールする
コマンドプロンプトを立ち上げ、tssampleフォルダに移動し、`npm install`を実行する。
```
cd <tssampleフォルダのパス>
npm install
```
tssampleフォルダ直下にnode_modulesフォルダが作成され、TypeScriptがインストールされる。

## tsconfig.jsonを作る
TypeScriptのコンパイル設定ファイルtsconfig.jsonをtssampleフォルダ直下に作る。
```tsconfig.json
{
    "$schema": "https://json.schemastore.org/tsconfig",
    "compilerOptions": {
        "target": "ES2022",
        "module": "CommonJS",
        "lib": [
            "ES2023",
            "DOM"
        ],
        "outDir": "dst",
        "strict": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true
    },
    "include": [
        "src/**/*.ts"
    ]
}
```
srcフォルダ以下のtsファイルをソースとして、dstフォルダ以下にコンパイルしたJavaScriptを出力する設定となっている。

## tasks.jsonを作る
tssampleフォルダ直下に.vscodeフォルダを作る。
.vscodeフォルダにVSCodeでタスクを実行するための以下内容のtasks.jsonファイルを作る。
```json:.vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "typescript",
            "tsconfig": "tsconfig.json",
            "option": "watch",
            "problemMatcher": [
                "$tsc-watch"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "label": "tsc: build - tsconfig.json"
        }
    ]
}
```
tsファイルを保存すると、tsファイルがコンパイルされ、JavaScriptファイルが出力されるようになる。
また、コンパイルエラー時、VSCodeにエラーが通知されるようになる。

## サンプルのtsファイルを作る
tssampleフォルダ直下にsrcフォルダを作る。
srcフォルダ以下にサンプルのtsファイルを作る。
```typescript:src/Main.ts
import { greet } from './greet/Greet';

console.log(greet('World'));
```
```typescript:src/greet/Greet.ts
export function greet(name: string): string {
    return `Hello, ${name}!`;
}
```

## VSCodeでプロジェクトフォルダを開く
VSCodeを起動し、tssampleフォルダを開く（File > Open Folder > tssample > フォルダーの選択）

## VSCodeのタスクを起動する
キーボードCtrl + Shift + Bを押す。
TERMINALに以下のように表示され、タスクが起動する。
```
Starting compilation in watch mode...

Found 0 errors. Watching for file changes.
```

## 動作を確認する
VSCodeのタスクを起動すると、dstフォルダにコンパイルされたJavaScriptファイルが出力される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224267/aaf7b0b7-5c23-a9f1-2364-e8dca100a23b.png)

コンパイルされたJavaScriptファイルは以下のように実行する。
キーボードCtrl + Shift + @を押すと新しくTERMINALが開くので、`node .\dst\Main.js`を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224267/904f3ee5-cae3-8134-03cb-c0d3c6afe21c.png)

また、tsファイルが保存時コンパイルエラーになる場合、エラーが出力される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224267/61d1ded1-ded1-7b76-a209-00de527f041d.png)

## 上記のソース
ここに置いています。
https://github.com/hs0x01/tssample
グリッド問題でdfsのバックトラックは指数時間でO(4^HW)がかかってTLEします

# 言いたいこと
・グリッドの同じ座標を何度も見る場合、O(4^HW)がかかってTLEする(下の問題など)
・グリッドの各座標を最大で一回しか見ない場合、O(4HW)でACできる(水たまりの連結成分の個数を求める問題など)

# バックトラックの計算量
https://atcoder.jp/contests/abc348/tasks/abc348_d
この問題の解説放送でバックトラックが指数時間かかるということを知りました。グリッドのサイズがH*Wのとき、バックトラックをすると計算量は各座標で4方向見るので4*H*Wだと思っていましたが、実際は4^(H*W)でした。ただし例外として、水たまりの塊の数を求めるなどの連結成分を求める問題などは4*H*Wになります。下の説明を見ていただければわかると思います。

わかりやすいイメージとしては、4*H*Wの場合は「グリッドの各座標を一回ずつ訪れて4方向見る」ということになります。しかし4^(H*W)の場合は、「dfsをしていて、何度も同じ座標を訪れているかもしれないが毎回4方向見る」という感じになります。

つまり何度も同じ座標を訪れていて毎回4方向見るというのが問題になっています。
ちなみに4^(H*W)でなぜH*Wなのかという部分は、「ある地点からdfsをスタートして、そこから四方向の一つに進んで、さらに進んでと繰り返した結果、綺麗にグリッドを全て見ることができた。そしてdfsをreturnして戻ってきて、その戻ってきた毎回の場所からまだ見ていない4方向を選んで進んで、その進み方でも毎回綺麗にグリッドを全て見ることができた」みたいなのをイメージすればなんとなくわかると思います。実際にH*Wというわけではなく目安としてのサイズとして覚えてもらえればいいと思います(O(2^30)でTLEなのでそれを超えているとわかっているだけでもいいと思います)

![バックトラックはO(4^HW).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3701202/489e820e-ffab-7fd1-fb36-ba04fccaf183.png)


図にしてみるとこんな感じです。言いたいのは「無数とまではいかないが、とてつもない計算量がかかるほどの通りがある」ということです、数え上げお姉さんと似た香りがします。ただこれはあくまで上に貼った問題のリンクのような一度通った座標を何度も通る可能性がある問題の場合で、最初に書いた通り水たまりの塊の数を求めるような連結成分を求める場合は4*H*Wになります。

# おわりに
今まで当然のようにバックトラックを4*H*Wだと思っていて、上の問題を本番で解いてTLEして痛い目に遭いました。解説放送でバックトラックについてコメントしてくれた方とsnukeさんに感謝します。
Azure 東日本リージョンでGPT-4を新規で使えるようにする裏技

# はじめに
2024年4月6日時点の情報の記事ですが､掲題のように東日本リージョンでGPT-4を新規で使える方法があることがわかりました｡この情報は､当面の間､おそらく汎用的に使える情報だと思いますので､共有したいと思います｡

# 前提
最低限､AzureのEntra IDテナントはあるものとします｡
所有するEntra IDテナント上で､新しくサブスクリプションを作り､そのサブスクリプション内で新たにAzure OpenAI Service(以下AOAI)のリソースを､タイトルにあるように東日本リージョンにて作り､GPT-4を使用したいと考えたとします｡

# そもそも何が問題なのか?
上記の前提で新たに東日本リージョンでGPT-4系のモデルをAzure OpenAI Studioからデプロイしようとすると以下のように使用可能なクォータがないと言われデプロイできない状態になります･･･

![](https://storage.googleapis.com/zenn-user-upload/2cb452852243-20240406.png)

とまあ､上記のような状態なので､まずは現状､どうなっているかを確認するために[公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/models)(2024年4月6日時点)を読みますと､全体としては以下の状況となっている模様です｡
![](https://storage.googleapis.com/zenn-user-upload/a52e1cfe6a62-20240406.png)

中段にjapaneastの行があるので､そこに着目しますと､どうやらgpt-4の0613バージョンとgpt-4v-previewとgpt-4-32kの0613バージョンの3つに☑があり､現時点ではその3つのGPT-4が使える(デプロイできる)ように読めます｡
しかし､現状の自分のAOAI上では十分なクォータがないことでこれらのGPT-4モデルが使えないらしいと推測されます｡

# じゃあどうやったら東日本リージョンでGPT-4が使えるようになるの?
当たり前ではありますが､こういう疑問が湧いてきちゃいます｡
[公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/ai-services/openai/)をくまなく探しても､結論よく分からなかったです｡(ちゃんと読み解けば求めている情報は書いてありそうですが､国語力と根気が正直けっこう必要なドキュメントですよね･･･)

ということで､**クォータらしきものを増やせばどうにかなる**と考えて､
Azure OpenAI　Studioを適当に探索してみますと､管理メニューのクォータという箇所をクリックしますと､
以下のようなページに遷移します｡
![](https://storage.googleapis.com/zenn-user-upload/2829b15da832-20240406.png)

このページの､
**Tokens Per Minute (thousands) - GPT-4-Turbo-V**
という行にある､クォータの要求というリンク箇所を押しますと､[Azure OpenAI Service: Request for Quota Increase](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR4xPXO648sJKt4GoXAed-0pURVJWRU4yRTMxRkszU0NXRFFTTEhaT1g1NyQlQCN0PWcu)
というフォームに飛ばされます｡

そして､英語なので適宜翻訳にかけていただきながら､この謎のフォームに必要な項目を適宜埋めて､申請を出しますと､うまくいけば数日以内に返信があります｡
Microsoft側で申請が承認されますと､クォータ増加されたという承認連絡が返ってくるはずです｡
(個人情報などがふいに写って事故りそうなので､その様子の画像やフォーム記入例などは割愛させていただきます｡)

ということで､またAzure OpenAI Studioに戻り､再度GPT-4のデプロイに再挑戦してみてください｡
以下のように､先程とは打って変わって､上の表で☑がついていたGPT-4のバージョンがデプロイできるようになっているはずです｡
![](https://storage.googleapis.com/zenn-user-upload/a208f3e5065a-20240406.png)


いつまでこの方法が使えるかは不明ですが､当面､モデルバージョンが移り変わったとしても応用を効かせれば長く使えそうな裏技となります｡


最後まで読んでいただきありがとうございます!もし役に立ちましたら**♡**をお願いします!
【oracle】リスナーを知りたい初心者はまず見て！！！

oracleの勉強やoracle運用していると
- リスナーを介したDB接続
- リスナーへのサービス登録を確認する
- リスナーの状態ってどうなってる？

など、リスナーに関する様々なことが、たまに聞こえてくる。
でも、自分自身、リスナーとoracleって図で示すとどんな関係になるのか、何となくしか把握できていなかった。

そんなDBAの自分が、リスナーとDBの関係を図を使ってイメージするために本記事を投稿する。

また、以下のサイトを参考にするとリスナーに関する理解が深まったので紹介する。
[Oracleへの接続について](https://qiita.com/mi-kana/items/bf0bebf900d2a2408ffe)
[動的リスナー登録について](https://qiita.com/mi-kana/items/862305b7f5fdee8d080d)
[静的リスナー登録について](https://qiita.com/mi-kana/items/6b1a39736df30ab21e35)


# 目次
1. リスナーの役割
1. リスナーとDBの関係図
1. 初期化パラメータ LOCAL_LISTENER
1. 初期化パラメータ SERVICE_NAMES

# 1. リスナーの役割
DBとリスナーの関係を説明する前に、リスナーの役割について
通常、oracleへのアクセスするには、以下の2種類の方法があります。
1. ローカル接続
1. ネットワークを経由した接続

①ローカル接続の場合
インスタンスが起動しているサーバーへ、既にログインできていれば、可能です。下図は、リスナーを介さないoracleへのアクセス方法です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/0b926d3d-8e85-71e6-5e74-e57c3c3fa556.png)

②ネットワークを経由した接続の場合
インスタンス起動サーバーとクライアントがアクセスしているサーバーとが、別の場合、下図のようにリスナーを介します。
ネットワークを経由した接続は、リモート接続ともいわれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/56a14c08-f0e6-ac75-3a8a-2c431049875d.png)

リスナーの役割とは、

クライアントからリスナーめがけて、
「このサービスに接続したいんだけど？」
と要求が来る。そこでリスナーは。
「このサービスのDBは、あっちですね！」
と案内するのが、リスナーの大きな役割です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/f8e72b80-7079-f324-4f67-e59df2e7b5bd.png)


# 2. リスナーとDBの関係図
結論から言うと、
リスナーとDBは、まったくの別物と考えてください。
どういうことか。
DB停止中でもリスナーは起動できるし、
リスナーを起動したからと言って、DBが起動するわけでもない。
→リスナーはDBから独立した存在なのです。


通常、リスナーはインスタンスが起動するサーバー内で、起動するのが一般的です。
しかし、ぶっちゃけ、リスナーはどこで起動しても構いません。

①一般的なリスナーとインスタンスの関係
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/afc5bd1e-75c8-bd52-5c6b-f6b6ce489a5a.png)

②リスナーとDBは独立してるから、こんなこともできるよ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/159ab0ad-276b-32f8-e37d-1104976e6571.png)

ただ、ここで問題になってくるのは、

**インスタンスからしたら、どこにリスナーがあるかわからない。**

それを解消してくれるのが、初期化パラメータである ```LOCAL_LISTENER``` です。

# 3. 初期化パラメータ LOCAL_LISTENER
大前提として、
- 初期化パラメータ ```LOCAL_LISTENER```
- 初期化パラメータ ```REMOTE_LISTENER```

上記二つの違いは、別途記事で説明します。
簡単に説明すると、二つともどこで起動しているリスナーに接続情報を登録するかを定めているパラメータです。
要するに、リスナーの場所を示しています。

 
```LOCAL_LISTENER```の場合、デフォルトでは、インスタンス起動サーバーにリスナーが存在すると設定されています。
``` LOCAL_LISTENER=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/e72e9b9a-3560-77a1-c53f-359fb54c3d6d.png)


使用用途は、動的リスナー登録を行うときに使用します。
インスタンス→リスナーへ60秒に一回 ```SERVICE_NAMES```の情報を提供しています。
その提供先を``` LOCAL_LISTENER```で毎回確認しながら、情報提供をしているという仕組みです。

# 4. 初期化パラメータ SERVICE_NAMES
 ```SERVICE_NAMES```パラメータとは、
 **DB名とサービス名を紐づけている設計書**
 です。
 基本的に、クライアントは、oracleにアクセスするとき、サービス名のみ意識しています。リスナーにもサービス名でアクセスしてきます。
そのため、リスナーにサービス名とDB名を紐づけておく必要があるのです。
そこで登場するのが、 ```SERVICE_NAMES```パラメータです。
```SERVICE_NAMES```は、60秒に一度インスタンス→リスナーに登録を行っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477632/e5390a37-0419-bd4d-b450-01ecc6eaf739.png)
【aws-cdk-examples】API GatewayからLambda経由でDynamoDBを参照/更新する

# これは何をしたくて書いたもの？
私個人として、現在業務でアプリ開発に利用しているReact NativeをAWSと連携させたいと考えているため、そのための学習の一環としてReact Nativeの通信先のREST APIを提供できるAPI Gatewayを構築して、その挙動を確認してみることにしました。
CDKには[aws-cdk-examples](https://github.com/aws-samples/aws-cdk-examples/)というサンプルプロジェクトをまとめたリポジトリがあります。今回はその中の[api-cors-lambda-crud-dynamodb](https://github.com/aws-samples/aws-cdk-examples/tree/main/typescript/api-cors-lambda-crud-dynamodb/)というサンプルを実際にデプロイして動かしてみます。

# 構成
`cdk deploy`で構築できるリソースは、大まかには以下の画像の通りです。構築されるリソースの詳細は、[index.ts](https://github.com/aws-samples/aws-cdk-examples/blob/main/typescript/api-cors-lambda-crud-dynamodb/index.ts)に記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518221/c00c5b54-316c-ba7f-e311-d95578858265.png)
- `OPTIONS`メソッドはCORS設定をするために作られています
- LambdaはNode.jsランタイムの関数が複数構築されています
- API GatewayとLambdaの間は「Lambda統合」というリソースで接続されているようです

# 実行コマンド例
本来はReact Nativeアプリからのリクエスト送信の実装サンプルを書きたいのですが、いったんはcurlコマンドの例を記載します。

~~~md:新規作成
curl -X POST '<API GatewayのURL>/items/' -H 'Content-Type:application/json' -d '{"id": "002","name": "Takeru Minami"}'
~~~

~~~md:全件取得
curl -X GET '<API GatewayのURL>/prod/items/' -H 'Content-Type:application/json'
~~~

~~~md:一件削除
curl -X DELETE '<API GatewayのURL>/prod/items/<itemId>' -H 'Content-Type:application/json'
~~~

~~~md:一件取得
curl -X GET '<API GatewayのURL>/prod/items/<itemId>' -H 'Content-Type:application/json'
~~~

~~~md:一件更新
curl -X PATCH '<API GatewayのURL>/prod/items/<itemId>' -H 'Content-Type:application/json' -d '{"id": "Minato"}'
~~~

# まとめ
このサンプルプロジェクトでは任意のサイトからのCORS通信を許可することが可能になっていて便利です。
また、CRUD処理を一通り網羅しているため、業務などでCRUD処理を作る時にたたき台として使えるところも使い勝手が良いと感じました。

今後も、他サンプルを実際にデプロイして動作を確認してみることで環境構築力を鍛えていきたいと思います。
AtCoder初心者振り返りメモ ABC348

# ABC348の回
https://atcoder.jp/contests/abc348
# A-問題
https://atcoder.jp/contests/abc348/tasks/abc348_a

[問題文]
髙橋君はサッカーの試合で N 回ペナルティキックを蹴ります。
髙橋君はi 回目のペナルティキックでは、i が 3 の倍数の場合は失敗しそれ以外の場合は成功します。髙橋君がペナルティキックを蹴ったときの結果を出力してください。

[考察や感想]
蹴る回数を % 3 で判定し、3の倍数のときは、失敗 x を出力
それ以外は、成功 o を出力する。問題通りにロジックを記述して正答できました。

````Python:A.py
N = int(input()) 

ans = ""

for i in range(1,N+1):
    if i % 3 == 0:
        ans += "x"
    else:
        ans += "o"

print(ans)
````
# B-問題
https://atcoder.jp/contests/abc348/tasks/abc348_b

![348b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547298/75554126-ea2c-dcb7-6684-0b5e06c7c5c8.png)

[考察や感想]
i番目の座標からその他の座標の距離を計算し、その中で最大値とそのインデックスを変数に記憶して選出しました。
小さい方から計算するので同じ最大値のときも答えは、条件通りで正答できました。


````Python:B.py
N = int(input()) 
X = [0]
Y = [0]

# それぞれの座標を別々の配列に格納する
for _ in range(N):
    x,y = map(int,input().split())
    X.append(x)
    Y.append(y)

for i in range(1,N+1):
    # 最大値とそのインデックスを格納する
    MX = 0
    IDX = 0
    for j in range(1,N+1):
        if i == j:
            continue    # 同じ座標は計算しない
        else:
            # 距離の最大な点（IDX)を選出
            P = (Y[j]-Y[i])**2 + (X[j]-X[i])**2
            if MX < P:
                MX = P
                IDX = j
    print(IDX)
````

# C-問題
https://atcoder.jp/contests/abc348/tasks/abc348_c

[問題文]
N 種類のビーンズが 1 粒ずつあります。i 種類目のビーンズはおいしさが Aiで色がCiです。ビーンズは混ぜられており、色でしか区別することができません。
あなたはビーンズの色を 1 つ選び、その色のビーンズをどれか 1 粒食べます。ビーンズの色をうまく選ぶことで、食べる可能性のあるビーンズのおいしさの最小値を最大化してください。

[考察や感想]
同じ色の値は、辞書で管理する。辞書にその色の値があれば、低い方の値に更新することで最小値を管理できる。
最後に、最大値を持つ色の値を答えに出力して正答できました。

````Python:C.py
# 入力
N = int(input()) 
dic = defaultdict(int)

# 辞書でその色の最小値を管理する
for _ in range(N):
    A,C = map(int,input().split())
    if C not in dic:
        dic[C] = A
    else:
        x = dic[C]
        if A < x:
            dic[C] = A

Mnum = 0
MV = 0

# 辞書の中から最大値を見つける
for num, value in dic.items():
    if MV < value:
        MV = value
        Mnum = num
    else:
        continue

print(MV)
````
command

# ls

## files only

https://askubuntu.com/questions/811210/how-can-i-make-ls-only-display-files

```sh
tokunori@tokunori-desktop:~$ ls /sys/class/nvme/nvme0/ -p | grep -v /
address
cntlid
cntrltype
dctype
dev
device
firmware_rev
kato
model
numa_node
passthru_err_log_enabled
queue_count
rescan_controller
reset_controller
serial
sqsize
state
subsysnqn
subsystem
transport
uevent
```
[GitLab]とあるプロジェクトのすべてMR(マージリクエスト)の詳細情報をJSONで引き抜く方法

# これなに
GitLabのとあるプロジェクトにおける過去のMR(マージリクエスト)をJSONで手に入れておくためのシェル。自分のメモも兼ねてqittaにも残しておく。

# 背景
現在のプロジェクトはGitLabでコード管理をしているが、とあるインシデントの水平展開のため、過去のMRについて調査が必要なった際に全量が取りたくなり作成した。（当時はMRのタイトルでフィルタして利用した）

# 利用環境
* Windows
```
エディション	Windows 10 Pro
バージョン	22H2
```

* glab ([GitLabのCLI用のツール](https://docs.gitlab.co.jp/ee/integration/glab/))
```bash
$ glab -v
glab version 1.37.0 (2024-03-07)
```
* git(git bashを利用)
```bash
$ git -v
git version 2.37.0.windows.1
```

# 実際のシェル
https://github.com/kamiryo/tool_gitlab_allmrlistup
【Flutter】HTTPを用いてAPIリクエスト実装した

## はじめに
APIを利用することで、アプリ開発の可能性は大きく広がります。Flutterでは、`http`パッケージを利用することでHTTP通信を簡単に実装できます。今回は[OpenWeatherMap API](https://openweathermap.org/api)を使って、特定地域の天気情報を取得するアプリを作りました。とても簡単に実装できたので共有したいと思います。

## 開発環境
- Flutter: 3.14.0
- Dart: 3.2.0

## パッケージのインストール
[http](https://pub.dev/packages/http)というパッケージを利用します。
```yaml
  http: ^1.1.0
```

## APIへのリクエスト
`http.get()`メソッドを使用して[OpenWeatherMap API](https://openweathermap.org/api)のAPIエンドポイントにGETリクエストを送信します。この例では、石川県（緯度: 26, 経度: 128）の天気情報をリクエストしています。`YourAPIKey`にはOpenWeatherMapで取得したAPIKeyを各自記入してください。

```dart
const apiKey = 'YourAPIKey'; // 各自APIKeyを記述
  var url = Uri.https(
      // ホスト
      'api.openweathermap.org',
      // パス
      '/data/2.5/weather',
      {
        // クエリパラメータ（石川のLatLongを使用）
        'lat': '26',
        'lon': '128',
        'appid': apiKey,
      });

  var response = await http.get(url);
```

## レスポンスの処理
リクエストが成功した場合(statusCode:200)、レスポンスのbodyがJSON形式で返されます。これをデコードし、欲しいデータを抽出することができます。

以下のコードでは成功したリクエストの天気情報をコンソールに出力しています。リクエストが失敗した場合は、ステータスコードと共にエラーメッセージが表示されます。

```dart
var response = await http.get(url);
if (response.statusCode == 200) {
  var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>;
  print("✅ 天気: ${jsonResponse['weather'][0]['main']}");
} else {
  print('❌ Request failed with status: ${response.statusCode}.');
}
```

出力例(成功した場合)
```
✅ 天気: Clouds
```

## コード全文
```dart
import 'package:flutter/material.dart';
import 'dart:convert' as convert;

import 'package:http/http.dart' as http;

void main(List<String> arguments) async {
  const apiKey = 'YourAPIKey';
  var url = Uri.https(
      // ホスト
      'api.openweathermap.org',
      // パス
      '/data/2.5/weather',
      {
        // クエリパラメータ（石川のLatLongを使用）
        'lat': '26',
        'lon': '128',
        'appid': apiKey,
      });

  var response = await http.get(url);
  if (response.statusCode == 200) {
    var jsonResponse =
        convert.jsonDecode(response.body) as Map<String, dynamic>;
    print("✅ 天気: ${jsonResponse['weather'][0]['main']}");
  } else {
    print('❌ Request failed with status: ${response.statusCode}.');
  }
}

```

## 最後に
本記事では、Flutterで外部APIへのHTTPリクエストを送信し、レスポンスデータを処理する基本的な方法を紹介しました。HTTPリクエストの送信方法をマスターすることで、バックエンドとの連携など、より高度な開発が可能になります。これからも、この技術を活用して面白い開発を進めていきたいと思います。
[AtCoder備忘録記事]AtCoder Beginner Contest 348 C問題

[AtCoder Beginner Contest 348 C問題](https://atcoder.jp/contests/abc348/tasks/abc348_c "Atcoder")
こちらの問題です。

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/2c584f35-de5e-0b68-db5b-52e02ae71d58.png)

# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/84f5dbc6-eeac-007f-e196-d887b7b74000.png)

# 解法
mapでそれぞれの色でグループ化し、それぞれのグループの最小値の中での最大の値を出力します。
```
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <cmath> 
#include <map>
#include <algorithm>
#include <limits>
using namespace std;


int main() {
    int N;
    cin >> N;
    map<int, vector<int>> groups;

    for (int i = 0; i < N; i++) {
        int x, y;
        cin >> x >> y;
        groups[y].push_back(x);
    }

    int maxOfMins = numeric_limits<int>::min(); 

    for (const auto& group : groups) {
        int minInGroup = *min_element(group.second.begin(), group.second.end());
        maxOfMins = max(maxOfMins, minInGroup);
    }

    cout << maxOfMins << endl;

    return 0;
}
```
ABC348をPythonで（A～D）+Eの感想

トヨタ自動車プログラミングコンテスト2024#4（AtCoder Beginner Contest 348）の解放等のまとめ

https://atcoder.jp/contests/abc348

# A問題
ooxを$N$個以上まで増やして、そこからスライス
```python:A
s = "oox" * 50
print(s[:int(input())])
```
# B問題
距離を計算して一番遠いところを答える
ただ、ルートをとると小数点の計算になって精度誤差が怖いのでルートをとる前で比較する
```python:B
n = int(input())
p = [list(map(int, input().split())) for _ in range(n)]

for x, y in p:
    ans = 0
    dist = 0
    for i, (x_i, y_i) in enumerate(p, 1):
        if dist < (x_i - x) ** 2 + (y_i - y) ** 2:
            dist = (x_i - x) ** 2 + (y_i - y) ** 2
            ans = i
    print(ans)
```
# C問題
同じ色の中で最小値は何かを調べて、その色別の値の中で最大値を答える問題
色ごとに、同じ色は$A_i$が昇順になるようにソートして順に見ると、各色ごとに最初に現れる値が最小値になるからそれらを大小比較すればよい
```python:C
n = int(input())

data = [list(map(int, input().split())) for _ in range(n)]
data.sort(key=lambda x: (x[1], x[0]))

ans = 0
for i in range(n):
    if i > 0 and data[i - 1][1] == data[i][1]:
        continue
    ans = max(ans, data[i][0])

print(ans)
```
# D問題
ダイクストラ
現状エネルギー最大でその場所へ来た時に、薬がありかつエネルギーより大きい値になるときその薬を使う
あとはエネルギーを正負反転させてダイクストラをすればいい
```python:D
from heapq import heappop, heappush


def max_pop(q):
    a = heappop(q)
    a[0] *= -1
    return a


def max_push(q, x):
    x[0] *= -1
    heappush(q, x)


h, w = map(int, input().split())
field = [input() for _ in range(h)]

sx, sy, gx, gy = 0, 0, 0, 0
for i, a in enumerate(field):
    for j, a_j in enumerate(a):
        if a_j == "S":
            sx, sy = i, j
        if a_j == "T":
            gx, gy = i, j

n = int(input())
d = [[0] * w for _ in range(h)]
for _ in range(n):
    r_i, c_i, e_i = map(int, input().split())
    d[r_i - 1][c_i - 1] = e_i

q = list()
q.append([0, sx, sy])

E = [[-1] * w for _ in range(h)]

while q:
    e_i, x, y = max_pop(q)
    if E[x][y] > e_i:
        continue
    if e_i < d[x][y]:
        e_i = d[x][y]
        E[x][y] = e_i

    if e_i == 0:
        continue

    for x_i, y_i in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
        if 0 <= x_i < h and 0 <= y_i < w and field[x_i][y_i] != "#" and E[x_i][y_i] < e_i - 1:
            E[x_i][y_i] = e_i - 1
            max_push(q, [e_i - 1, x_i, y_i])

print("Yes" if E[gx][gy] >= 0 else "No")
```
# E問題
全方位木DPにしか見えなくて色々わちゃわちゃやってたができなかった。
木の重心の解説がなぜ正解できるかわからない
なんでみんなできてるの？

- やってたこと
    - 最初のスコアをDFSで計算
    - しながらその枝へ移動したらいくつ変わるかの差分を求める
    - 減る方向にのみ移動しながら計算
    - この方法でMLEしてダメだった
[AtCoder備忘録記事]AtCoder Beginner Contest 348 B問題

[AtCoder Beginner Contest 348 B問題](https://atcoder.jp/contests/abc348/tasks/abc348_b "Atcoder")
こちらの問題です。

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/9e6598a2-a1d9-11b5-98d8-de8e352a9531.png)

# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/1049232a-0a44-b7f7-e6de-d92bf458908c.png)


# 解法
二点間の距離のうち最大のもののindexを返す関数を作成し、それぞれの座標に対して関数を実行します。
```
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

int FarthestPointIndex(const vector<vector<int>>& arr, int x1, int y1) {
    int farthestIndex = -1;
    double maxDistance = -1;

    for (int i = 0; i < arr.size(); i++) {
        if (x1 == arr[i][0] && y1 == arr[i][1]) continue;
        //最大距離の計算
        double distance = sqrt(pow(arr[i][0] - x1, 2) + pow(arr[i][1] - y1, 2));
        if (distance > maxDistance) {
            maxDistance = distance;
            farthestIndex = i;
        }
    }

    return farthestIndex + 1;
}

int main() {
    int N;
    cin >> N;
    vector<vector<int>> coords(N, vector<int>(2));
  
    for (int i = 0; i < N; i++) {
        cin >> coords[i][0] >> coords[i][1];
    }
    for (int i = 0; i < N; i++) {
        cout << FarthestPointIndex(coords, coords[i][0], coords[i][1]) << endl;
    }
    
    return 0;
}

```
[AtCoder備忘録記事]AtCoder Beginner Contest 348 A問題

[AtCoder Beginner Contest 348 A問題](https://atcoder.jp/contests/abc348/tasks/abc348_a "Atcoder")
こちらの問題です。

# 問題
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/8924399c-774a-06ad-8cde-a5aa5673b4be.png)


# 入出力例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2374978/ad9b5917-6246-89d8-f39c-1c44412a1304.png)

# 解法
簡単ですね。
forでまわして3の倍数の時に×を出力します。
```
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
#include <cmath> 
#include <algorithm>
using namespace std;

int main() {
    int N;
    cin >> N;
    for(int i = 1; i <= N; i++){
        if(i % 3 == 0){
            cout << "x";
        }else{
            cout << "o";
        }
    }
}

```
【Git】間違えたpushを取り消したい

Gitでpushを取り消したい時の手法になります。
※個人開発では問題ないと思いますが、チームで開発しているときは注意が必要です。

## 直前のpushを取り消す

間違えたpushをしたブランチにて実行

```bash
$ git reset --hard HEAD^
```

強制pushをして、リセットをリモートリのブランチに反映させます。

```bash
$ git push -f origin 間違えてpushしたブランチ名
```

## 過去のpushを取り消す

過去の取り消しにはコミットのハッシュ値が必要になります。
`git log`にてハッシュ値を取得します。

```bash
$ git log

// git logだと情報量が多いと思うのでオプションをつけると見やすくなると思います。
$ git log --oneline -5
```

取得したコミットのハッシュ値を指定してリセット、強制pushをして反映します。

```bash
$ git reset --hard コミットのハッシュ値
$ git push -f origin 間違えてpushしたブランチ名
```

## 参考

https://zenn.dev/take12/articles/2b4f52290d6bdf

https://qiita.com/forest1/items/82ac22596fbe65187a8b
20240406 powershellのlsを色付きの`ls --color=auto -F`にする、starshipでposh-gitを置き換えたい

### powershellのlsを色付けにした話

Powershellの各種コマンドをcoreutilsで置き換えてかなり満足だったが、lsの出力だけが気に食わない

この貧相っぷりである
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/9b4347ff-8109-b585-b952-10739bbd7b84.png)
普通`ls -F --color=auto`のaliasを作るのだが、Powershellではオプション付けてaliasにするのがめんどい
こんな風にしてうまくいった
```powershell
function _ls() {
  if ($args.Length -eq 0) {
    uutils ls -F --color=auto
  } else {
    uutils ls -F --color=auto $args
  }
}
Set-Alias ls _ls -Option AllScope
```

出力結果、なんとも見慣れた雰囲気になって落ち着く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/6ff11a19-fdac-9f10-fc37-2fc0bb8749fa.png)

### starshipでposh-gitを置き換えたい
starshipはたぶんすごく早いはずなので、今のposh-gitを置き換えたいと考えている
posh-gitのプロンプト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/f612a48f-d990-433c-fa56-128ba7dd81a5.png)

starshipのプロンプト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3740499/36b61a58-1c89-8464-f83f-fa8d73e35864.png)

gitのステータスをもうちょっと細かく表示してくれるとうれしい