OmniAICreator/Qiita-1.07M huggingface dataset:
２つの配列を比較するときの実装 JavaScript

たまに忘れるので備忘録として残します。

主には、配列メソッドの`include`を使います。

### includes
配列に特定の要素が含まれているかどうかを調べて、true/falseで結果を返す。

```javascript
const array1 = [1, 2, 3]
console.log(array1.includes(2)) // true
```

### サンプル
こんな配列データがあったとします。
```javascript
// 全リスト
const list = [
  {id: 1, name: 'A'},
  {id: 2, name: 'B'},
  {id: 3, name: 'C'},
  {id: 4, name: 'D'},
]
// 登録済みだけのリスト
const registeredList = [
  {id: 2, name: 'B'},
  {id: 4, name: 'D'},
]
```

#### ケース1
listを登録処理する際に、登録済みのregisteredListは登録不要なのでfilterして除去したい時

```javascript
const filteredList = list.filter(value => {
  // 判定用にidだけの配列を用意する
  const targetIds = registeredList.map(v => v.id)
  return !targetIds.includes(value.id)
})

// 結果はこうなります
[
  {id: 1, name: 'A'},
  {id: 3, name: 'C'}
]
```

#### ケース2
すべてのデータが登録済みなら登録ボタンを押せなくしたいので、listのデータと登録済みのregisteredListを比較して、すべて登録済みかを判定したい

```javascript
// 判定用にidだけの配列を用意する
const targetIds = registeredList.map(b => b.id)
// everyメソッドは全ての要素がコールバックに合格するかでtrue/falseを返す
const result = list.every((value) => targetIds.includes(value.id))

// 結果はfalseです。もしregisteredListのデータをlistと同じにすればtrueとなります
```

#### ケース3
ケース1, 2ともに判定用にidだけの配列を用意し、includesメソッドを使っていますが、別のやり方としてidをキーにしたRecordデータ `{ KEY1: {}, KEY2: {} }`を用意して、検索対象のidがキーの中に含まれるかどうかを検索すると言う方法もあります。得られる結果は同じなのですが、データ量が数千件規模と多くなるほどこの方法がの方が速く、存在チェックではよくある手法らしいので紹介します。

再度ケース1（listを登録処理する際に、登録済みのregisteredListは登録不要なのでfilterして除去したい時）をこのやり方でやってみます。

```javascript
// 判定用にidをキーにしたRecordデータを用意する
const registeredListMap = {}
registeredList.forEach(r => registeredListMap[r.id] = r)
// こういうオブジェクトができます
{
  2: {id: 2, name: 'B'},
  4: {id: 4, name: 'D'},
}

const filteredList = list.filter(value => {
  // registeredListMapに存在しないIDでアクセスすればundefinedが返る
  // 存在しない(= 登録してない)データだけを抽出する
  return !registeredListMap[value.id]
})

// 結果はこうなります
[
  {id: 1, name: 'A'},
  {id: 3, name: 'C'}
]
```

他にもやり方や、違う比較のケースなどあると思うので、追って追加して行きます。
Gmailのフィルターをうまく使ってメールを仕分けする

この記事は[PONOS Advent Calendar 2024](https://qiita.com/advent-calendar/2024/ponos) 10日目の記事です。
前回は @nissy_gpさんでした。

最近エンジニアっぽい仕事をしておらぬではないか！
書くことがない・・・どうする・・・！
そうだ！Gmailのフィルターの話をしよう！うまく使えば便利ですからね。
マークダウン記法を調べながらつたなく書いてまいります。

# はたしてどんな人に需要があるのか。
「やった！Gmailのフィルターの記事だ！」なんているのかという疑問を持ちつつ、こんな人かなとぼんやり。
- スレッド機能を使っていない
- フィルターをたくさん作りたくない方
- フィルターを細かく設定したい人
- ANDとかORとか見ても大丈夫な人

Gmailには便利なスレッド機能があるのですが、ちょくちょくメールの見落としがあり私との相性が悪いのでスレッド機能をオフにしています。スレッドの下の方に埋もれて添付ファイルを見逃したという過去があります。
ちゃんと計ったことはないのですが、毎日80件前後受信します。フィルターして受信ボックスとかに入らないようにしているのですがもしそれを手動でやるとなれば1日3時間くらい持っていかれるかもしれません。
あと、デフォルトのフィルター形式を使うと全てAND条件なので違うドメイン（メールアドレス）の人からのメールで同じラベルを付けたいときとかにドメインの数だけフィルターを作らないといけません。なのでこのドメインまたはこのドメインならこのラベルを付けるみたいなことができればフィルターの一覧がスッキリするという自己満足が得られます！

本題に入るにあたり、メールが処理されるまではこんな流れです。
1. 受信（または送信）する
1. フィルターの条件に一致するか判定する
1. 一致したものを決まった処理（アーカイブやラベル付け）を行う
今回は2の部分である条件について書いていきます。3については今回説明はしない感じで行きます。

# 実際に書いてみる
とは言うものの、どうやって紹介しようというところです。
使用場面に合わせた書き方を紹介していきますが、適宜自分用にカスタムしてもらってフィルターを「含む」の部分にいれるだけです。
フィルターですが部分一致のようなので、フルのメールアドレスを入れればそのメールアドレスのみが条件に一致し、ドメインだけを入れれば同じドメインのメールアドレスがすべて一致するようです。

例に使うドメインはexample.comとexamlpe.netです。

## 複数の宛先を対象にしたいとき
特定の人を複数指定したい場合に下記のような書き方をします。
```
from:((example.com) OR (example.net))
```

## 受信メールだけじゃなくて送信メールも条件一致の対象としたいとき
同じドメインを記載していますが、記載誤りではありません。
この場合はexample.comからメールを受信した場合または、example.comへのメール送信も対象となります。
検索窓のFromとToに入れてしまうとANDになってしまうので結構重宝しているフィルターです。
```
from:(example.com) OR to:(example.com)
```

## 返信先メールアドレスを対象とするとき
時々一致してほしいのにフィルターをすり抜けていくメールがいます。
差出人を見てもフィルターに一致してそうなのにな〜と思いながら確認すると、差出人アドレスはフィルターに一致しないが、返信先メールアドレスが指定されていてそっちが一致しているというパターンです。
replytoを使えば仕分けをすることができます。
```
from:(example.com) OR to:(example.com) OR replyto:(example.com)
```

## 特定の件名と特定の相手とのやりとりを対象としたいとき
同じ件名で特定の相手と取引をする場合に一致させることができます。このフィルターを使ってラベルをつけると案件ごとにラベルをつけることができたりします。
```
subject:(〇〇案件対応) AND (from:(example.com) OR to:(example.com))
```

## カレンダーの予定関連を対象にしたい場合
予定のメールにはinvite.icsというファイルが添付されています。このファイルが添付されているという条件を設定すれば一致するのは予定関連だけのメールです。ちなみにこれを知る前は「招待」とか「辞退」とかの単語で件名一致させたりしていました。
```
has:attachment invite.ics
```

# 終わりに
この記事を書いているときに、もっと簡単なやり方があるのにわざわざ難しいやり方をしているのではという気持ちがとても大きくなりました。
毎日何かを考え、エンジニア的なことをやっていないと何を書けばいいのだろうという良くない方向に迷ってしまう。来年はどれを書こうかなといういい方向の迷いを持ちたいと思いました。

フィルターを書くときはココを参考にしています。
https://support.google.com/mail/answer/7190?hl=ja

それでは次回は @gamebotさんです。
Laravel Job実行をより機能的に監視したい


# LaravelのJob Queueの問題点と解決策

Laravelの非同期処理のために利用するJob queueにはいくつか課題が存在します。
本記事では、Jobの実行状況をObserveする方法について述べます。

## 問題点

ここではJobをQueue Databaseで管理することを想定します。(redis等でも同様ですが、SQSはちょっと分かりません）
Laravelではこの場合Jobがdispatchされると、`jobs`テーブルに行が作成され、その中で実行状況（再実行回数やpending中かどうかなど）が管理されます。しかし、以下の問題があります。

- **JobをdispatchしてもJobのIDが返らないので、実行状況を知りたいJobをjobs tableの中から特定できない**
    - 単一のJobのdispatchでも、`Bus::batch`によるbatch dispatchでも同様です
    - そのため、Jobを作成したAPIリクエストのレスポンスから、どのようなJob行が作成され管理されているのかを知ることができません

私はこれを何気に不便に感じています。

### 例: Job Dispatch時の挙動

```php
MyJob::dispatch($data);

// この時点でJob IDは取得できない
```

この課題を解決しようと、以下のようなPull Requestを作成してみましたが却下されました。
PRも雑だったか、そもそもJobのdispatch時にIDを返す意義に管理者は興味がないのかなと感じました。

https://github.com/laravel/framework/pull/53800

## 解決策: JobQueuedイベントの利用

では他の方法がきっとあるのだろうと、調べてみたところ、`JobQueued`イベントを活用すれば、Jobの実行状況をObserveする仕組みを構築できるなと気付きました。JobがQueueに登録されるとEventが発火され、そこにリスナーを登録すれば該当Job IDとJob Instanceに同時にアクセスできるのです。

### 実装手順
（GPT製。手元でも動きました）

**リスナーの作成**

`php artisan make:listener JobQueuedListener`でリスナーを作成します。以下のように実装します。

```php
namespace App\Listeners;

use Illuminate\Queue\Events\JobQueued;
use Illuminate\Support\Facades\Log;

class JobQueuedListener
{
    public function handle(JobQueued $event)
    {
        $jobId = $event->id;
        $jobInstance = $event->job;
        Log::info('Job Queued', [
            'job_id' => $jobId,
            'job_class' => get_class($jobInstance),
            'job_arg' => $jobInstance->arg, //Jobのpublic プロパティにアクセスできる
        ]);

        //任意でデータベース更新
        //Jobs tableに列追加して、そこで管理してもよし、別テーブル作ってもよしです。
    }
}
```
リスナー上ではJobのpublic propertyとJobのテーブル上のIDを同時に受け取れるため、それらの紐付き情報をDBに保存する処理を書くことができます。

**JobQueuedイベントリスナーの登録**

`JobQueued`イベントを受け取るリスナーを登録します。
プロバイダーに以下を登録します。

```php
protected $listen = [
    'Illuminate\Queue\Events\JobQueued' => [
        'App\Listeners\JobQueuedListener',
    ],
];
```

## まとめ

Laravelのデフォルト機能では、JobのIDを取得して即座に返す仕組みは提供されておらず、Jobの監視ロジックが組みづらいです。`JobQueued`イベントを活用することで、Jobの状況を追跡する仕組みを構築できます。
annoyとknnで英文の難しさ推定

# 自己紹介＆やること
普段は教育AIや自然言語処理の研究をしており、IRコミュニティには基本的にいません。英単語等の学習支援システムが専門です。本当に久しぶりにQiitaを使います。10年ぶりぐらいでしょうか…

埋め込みベクトルを使う事があったので、annoyとk-nearest neighbor（K近傍）を使って英文の難しさ推定をやってみます。せっかくなので公開してみる事にしました。要するに、英文を入力すると難しさを数値で出します。
実験環境にはGoogle Colaboratoryを使います（課金しています）。

# 手法
テキストを入力として、意味的に近い文同士の距離が近くなるようなベクトルを出力するのが「埋め込みベクトル」の技術です。今回はOPenAIのtext-embedding-ada-002を使います。これはOpenAI APIを通じて文を投げると、1500次元でノルム（ベクトルの長さ）が1.0になるように正規化されたベクトルを返してくれます。

https://note.com/npaka/n/n8f410f178f75

英文の文単位の難しさを英語教師等の先生に読んでもらって、4段階で難しさを評価してもらったデータセットというのはここで手に入ります。難しさの段階の基準にはCEFRという基準があり、A1,A2,B1,B2があり、後の方が難しいです。ここでは、単純に番号にします。

https://github.com/yukiar/CEFR-SP

このようなデータセットを使う場合には、「引用してください」（citation）と書かれている論文を引用することが必要です。論文はここです。

https://aclanthology.org/2022.emnlp-main.416

https://www.jbe-platform.com/content/journals/10.1075/itl.22018.uch


ダウンロードしてきたデータセットを適切なフォルダにおきましょう。文の評価者は2名いますが、今回は2名の評価が一致しているデータだけを使います。
```python
def read_dscefr(dscefr):
  with open('/content/CEFR-SP-main/CEFR-SP/SCoRE/CEFR-SP_SCoRE_train.txt', 'r') as f:
    for line in f:
      ary = line.strip().split('\t')
      if ary[-1]!=ary[-2]:
        continue
      dscefr[ary[0]]=int(ary[1])
dscefr = {}
read_dscefr(dscefr)
```

文をキー、値をtorchのtext-embedding-ada-02とする辞書を用意します。最初の10文をテストに使いたいので10文以降で作ります。
```python
sdvecs = dict(zip(sents_orig[10:],sents_embeddings[10:]))
from annoy import AnnoyIndex
def get_annoyidx(sdvecs):
  lsdvecskeys = sorted(list(sdvecs.keys()))
  annoyidx = AnnoyIndex(sdvecs[lsdvecskeys[0]].shape[0], 'angular')
  ds2sid = {k:i for i,k in enumerate(lsdvecskeys)}
  rev_ds2sid = {v:k for k,v in ds2sid.items()}
  for k in sdvecs.keys():
    annoyidx.add_item(ds2sid[k], sdvecs[k])
  annoyidx.build(10)
  return annoyidx, ds2sid, rev_ds2sid
annoyidx, ds2sid_annoy, rev_ds2sid_annoy = get_annoyidx(sdvecs)
```

ベクトルを入力すると3-nearest neighborを検索してきて、その10例の平均値を難しさとして推定するコードを書きます。
```python
import numpy as np
def get_annoydiff(v):
  myids, vs =  annoyidx.get_nns_by_vector(v,3,include_distances=True)
  return (np.mean([dscefr[rev_ds2sid_annoy[myid]] for myid in myids]))
```

最後に10文で実験してみます。文例をここに直接載せると問題になるかもしれないので（ちゃんとした論文での引用ではないので）、載せません。正解ラベルと比較します。

```python
for i in range(10):
  print('estimated:', get_annoydiff(sdvecs[list(sdvecs.keys())[i]]),  'true label:' ,dscefr[sents_orig[i]])
```

結果はこんな感じです。難しさではなくて文の意味的類似度で見ているので、簡単な文が軒並み間違っています。難しさ2の時のみ大体当たっているように見えます。
```python
estimated: 2.3333333333333335 true label: 1
estimated: 2.0 true label: 1
estimated: 2.0 true label: 1
estimated: 2.0 true label: 1
estimated: 2.3333333333333335 true label: 1
estimated: 2.0 true label: 1
estimated: 2.0 true label: 2
estimated: 2.0 true label: 2
estimated: 2.3333333333333335 true label: 2
estimated: 2.3333333333333335 true label: 2
```

今回はやりませんでしたが、一般の好きな文例を入力してもその埋め込みベクトルを計算して同じようにすれば、annoyで英文の難しさ推定気が一応作れるはずです。
BIRD2でBGPを張る話

BIRD2と呼ばれるソフトの情報が少ないので、置いときます。

## BIRD2とは？
BGPなどを張ることができるソフトです。

## やりたいこと
JSNOGの方とGREv6接続して、BGPを張る、です。

## GREを張る
相手の終端IPv6は`2001:470::A`
自分の終端IPv6は`2001:470::B`とすると。
そして境界IPは自分のだと`10.1.0.1`で、相手は`10.1.0.0`です。
```yaml
network:
  version: 2
  tunnels:
    gre0:
      mode: ip6gre
      remote: 2001:470::A
      local: 2001:470::B
      addresses:
      - "10.1.0.1/31"
```

その後netplanを反映させます。
```sh
sudo netplan apply
```

その後GREトンネルが張れているかチェックしましょう。
```sh
ping 10.1.0.0 -c 4
```
これで問題なく通信できているのであれば設定終了です。

## BIRD2のコンフィグ
```conf
router id {ルーターのID};

log syslog { info, remote, warning, error, auth, fatal, bug };
log stderr all;
debug protocols {events, states, interfaces};
graceful restart wait 15;

protocol kernel {
    ipv4 {
        import none;
        export all;
    };
    learn;
    scan time 10;
}

protocol static {
    ipv4;
    route {自分が広報する予定のIP} blackhole;
}

protocol direct {
    ipv4;
    interface {広報予定のIPがあるイーサーネット};
}

protocol device {
    scan time 100;
}

protocol bgp bgp1 {
    local as {自分のASN};
    neighbor {相手の境界IP} as {相手のASN}; 
    ipv4 {
        next hop self;
        
        import all;
        export filter {
            if net ~ [{自分が広報する予定のIP}] then accept;
            if source = RTS_BGP then accept;
            reject;
        };
    };
}
```

## 確認
```
sudo birdc show protocol all
```
これでBGP接続がEstablishになっていれば正常に張れています。

## 最後に
BIRD2の情報が少なすぎる、、、
1本のUSBに複数のISOのインストーラーを入れる

最近は32GBのUSBも数百円程度と気軽に手に入れやすくなったが...通常1本のUSBメモリーにISOのインストーラーは1つしか入らないから小さい容量のものを買おうか迷うと思う。

しかしVentoyを活用すれば1本のUSBメモリーを複数のISOのインストーラーとして使えます!

[Ventoy](https://www.ventoy.net/en/index.html)

ファイルをダウンロードし、解凍、中にある「Ventoy2Disk.exe」を実行する。
複数のインストーラーを入れたいUSBを選択しインストールする。

そうするとVentoyという名前のドライブになっています。その中にISOをコピーするだけ

その後は普段通り起動するとVentoyの画面が出ます。
「Boot in normal mode」を選択し、起動したいISOに十字で合わせエンターを押すだけ。

簡単に複数のインストーラー入れられるツールが出ている便利な世の中だね
sattoを活用した一括処理スキルの実践解説



# **sattoを活用した一括処理スキルの実践解説**

今回は、私が実際に作成した「✅タスクをリストにしてNotionのGTDに転記してくれるスキル」を例にして、アプリノードの一括処理機能ををご紹介します。

---

## **1. sattoの基本情報**

**sattoとは？**  
sattoはソフトバンクが提供する生成AIエージェントで、直感的な操作で業務を効率化できるツールです。プロンプト入力が不要で、GoogleカレンダーやGmailなどの外部サービスともシームレスに連携可能なツールです。

### **主なメリット**
- **簡単操作**: プログラムやAI知識がなくても直感的に使える。
- **外部連携**: GoogleやSaaSツールと連携し、作業を一元化。
- **カスタマイズ性**: ユーザーが独自のスキルを作成・共有可能。

---

## **2. スキルの概要**

今回紹介するスキルの目的は、「タスクをリスト化し、NotionのGTDデータベースに転記すること」です。非常に地味なタスクですが、チャットや文章でタスクを投げられたり、自分が洗い出したタスクを抽出して、Notionデータベースに登録することができます。

---

## **3. 実装例**

**使用手順**
1. **文章選択**: 登録したいタスクを文章から選択し、`Ctrl + A`でコピー。
2. **タスク実行**: sattoで作成したスキルを実行。
3. **AIノードで処理**: AIノードが入力文章をタスクとして分解し、配列で出力。
4. **Notionに転記**: 配列内のタスクを一つずつNotionのデータベースに登録。  

2~3を自動で行ってくれます。

---

## **4. 各ノードの仕組み**

### **入力ノード**  
入力文章をそのまま次のノードへ送信します。特別な設定は不要です。

### **AIノード（タスク抽出）**  
ここがスキルの要となる部分です。

#### プロンプト例:
```jsx
あなたはタスク抽出システムです。次の手順に従ってユーザーの文章から複数のタスクを抽出し、配列で出力してください。
手順:
1. ユーザーの入力文章を読みます。
2. 文章の中から「タスク」と見なせる個々の行動や行為を特定します。この際、各行動は明確かつ具体的なものに限ります。
3. 抽出したタスクを個別の要素として配列に追加します。
4. 配列形式で抽出されたタスクを出力します。

入力文章: {{/入力文章をコンテキストとして選択}}

# 補足
- 指示の復唱はしないでください。
- 自己評価はしないでください。
- 結論やまとめは書かないでください。
- 最終成果物以外は出力しないでください。
- 出力結果をダブルクォーテーション("")で囲わないでください。

```

このプロンプトにより、AIノードが入力文章を解析し、タスクを個別の要素として配列で出力します。

### **アプリノード（Notion API処理）**
#### 事前準備
1. Notionとsattoを連携。
2. Notionに「フルページ」のデータベースを作成。   
※今回は、プロパティとして、名前（テキスト）とタグ（セレクト）を追加している
3. データベースIDを取得（`https://www.notion.so/XXX?v=YYY` の `XXX` 部分）。

#### 各パラメータの設定
- 一括処理モードをオンにする 
- **リクエストURL**: `https://api.notion.com/v1/pages`
- **Header**:
  ```json
  {"Notion-Version":"2022-02-22"}
  ```
- **Body**:
  ```json
  {
    "parent": { "database_id": "<database_id>" },
    "properties": {
      "名前": {
        "title": [ { "text": { "content": "{一括処理対象の一要素}" } } ]
      },
      "タグ": {
        "select": { "name": "次にやる" }
      }
    }
  }
  ```
※bodyの細かい設定は以下を参照
https://developers.notion.com/reference/property-object
- 上記ボディを入力すると、上の方にdatabaseIDの入力欄が出現するので先ほどメモしたデータベースIDを入力する

---

## **5. 一括処理対象の入力**
💡ここがポイントです。一括処理対象の入力欄に、AIノードで実行した結果をコンテキストにしたいのですが、この時点ではコンテキストを何も入力できない状態です。

AIノードの出力を直接一括処理に渡すことはできないため、以下のステップが必要です:
1. AIノードのタスクのみを実行
2. 処理結果が配列として出力されることを確認する
3. 一括処理対象の入力欄に「/」と入れるとが入力できる様になる
4. Bodyの`content`「{一括処理対象の一要素}」を削除して、「/」を入れると、一括処理対象の要素をコンテキストとして入力できるようになる
※2024年12月11日時点の情報なので今後のアップデートで変更する可能性あり。
---

## **6. 実際の運用例**

実際に動作している様子は以下の動画をご覧ください。
[動画リンク: Xの投稿URL]

---

## **7. まとめ**

このスキルは、複数タスクを追加するようなシンプルなスキルですが、この一括処理を応用することで様々な業務に活用できると思います。特に多忙なビジネスパーソンにとって、手作業から解放される効果は絶大です。もし、他の活用事例があればXで共有してください！！

---
Hubspot CMSと仲良くなるための小ネタ集

Metaps Advent Calendar 2024の12日目の記事です。

# はじめに
Hubspot CMSをカスタマイズする際に調べた色々工夫してみた小ネタ的なものを集めてみました。


# 前提: Hubspotブログのカスタマイズについて
ブログはアカウントにつき一つのみ利用できます(Marketing Hub Starterの場合)
そのため、ブログ機能を使ってトピックの違う記事をそれぞれ別の場所で見せたりするには少し手を加える必要があります。

## 任意のタグのブログ一覧を作りたい
Hubspotはブログにタグを設定して、その記事がどのようなトピックで書かれているかを分類することができます。

デフォルトの一覧だと、全ての記事が一緒になっており、URL`/tags`の下にそれぞれのタグごとの一覧が自動生成されます。

`/tags`以外の場所で任意のタグの一覧を作りたい場合はこのようにします。

```
// これでモジュールで選択したタグのブログ記事が3件一覧化される
{% set blog_tag = module.blog_tag.slug %}
{% set tag_posts = blog_recent_tag_posts("default", blog_tag, 3) %}


{% for tag_post in tag_posts %}
	<article>ブログカードのHTML</article>
{% endfor %}
```



## ブログ記事のデザインをタグごとで変えたい

ブログは一つしか作れないのですが、「レポート」タグのついたブログ記事と「ニュース」タグのついたブログ記事があった場合、さらに記事自体のデザインの見せ方を変えたいとなった時があります。

しかし、ブログのデザインのテンプレートは一覧用と記事用でそれぞれ1つしか当てることができません。

その場合、一つのブログ記事テンプレートの中で分岐を作ることでタグごとのブログ記事のデザインを変えることができます。

```
// ブログ記事に含まれているタグ一覧 この場合は1つしかタグがついてないことを想定する
{% set tags = content.tag_list %}

{% if tags[0] == "レポート" %}
	// レポートタグのブログ記事デザイン
{% else tags[0] == "ニュース" %}
    // ニュースタグのブログ記事デザイン
{% endif %}
```


## リッチテキスト内部のHTMLタグを無視したい
後から非エンジニアがテキスト変更可能なようにリッチテキストのフィールドを使った際、リッチテキストが自動で用意してくれる（おせっかいな）HTMLタグが元々用意していたモジュールのデザインを邪魔してしまうことがあります。

ある程度は予測してCSS側で対応することもできますが、デザインを意図通りにするため、リッチテキストで挿入されてしまったHTMLタグを無視したい時はこのようにします。

```
{% set text = module.text|sanitize_html("STRIP") %}

// リッチテキストに何かHTNLタグが付加されていてもテキストのみdivの中に挿入される
<div class="test-text">{{ text }}</div>
```

ちなみに`sanitize_html`はオプション値を変えることである一定のHTMLタグを通すようにも変更できます。
詳細は以下のドキュメントにあります。

https://developers.hubspot.jp/beta-docs/reference/cms/hubl/filters#sanitize_html

## ブログで使える変数値まとめ
Hubspotで用意されている変数の中でもブログ関連で使われているものをご紹介します。
`content`自体はページ情報のオブジェクトですが、ブログ記事ページであればこのような変数が含まれます。

| 変数名 | 変数の内容 |
|:-|:-|
| content.name  | ブログ記事のタイトル  |
| content.absolute_url | ブログ記事のURL  |
|  content.featured_image |  ブログのキーサムネイル |
|  content.featured_image_alt_text | ブログのキーサムネイルのaltテキスト  |
|  content.blog_post_author  | ブログの執筆者情報のオブジェクト  |
| content.tag_list|ブログに指定されているタグの一覧リスト|
| content.post_body  |ブログ本文   |



# 終わりに
Hubspot CMSは膨大な開発者ドキュメントを読めばある程度のことが大体できるようにはなっています。
その代わり、開発者ドキュメント以外の情報はなかなか見つからなかったので、情報を増やしていけたらと思っています。

# 参考情報

https://developers.hubspot.jp/beta-docs/reference/cms/overview

https://github.com/HubSpot/cms-theme-boilerplate/tree/main
uv run *.py で動かしたときの、python 環境を調べる

# 背景

uv 使いだして疑問になったこと

- 仮想環境指定してないのに、何で動くん？そもそも、どこの環境使ってるん？

ってこと、requirements.txt で簡単に pip install 出来るのは便利なんだけど、uv が効いてる環境ですぐに使えちゃうことで、想定していない python 環境使ってたりすることがあったので、ちょっと調べた記録

# 結論

以下、かな？

> If we discover a workspace, we use the current behavior.
If we can't discover a workspace, we use our standard Python interpreter discovery, and run in that interpreter. (So, e.g., run in the current venv, or a discovered venv, or whatever you pass in --python.)
We add an option to mark a pyproject.toml as "not managed by uv" / "not part of a workspace" (like managed = false or workspace = false) so that you can use this in codebases that have a pyproject.toml but want to use the pip API.

https://github.com/astral-sh/uv/issues/3836#issuecomment-2191624067


ざっくりでいくと

1. 現在の環境を使う < 環境設定かな？

1. 無い場合、標準のPythonインタープリタ検出を使用し、実行する。
   ex.) 現在の venv、検出された仮想環境、or <code>--python</code> の指定環境

1. toml や workspace を利かせないようなオプションもあるよ。
   ⇒そういう環境下で、使いたい場合用？



# 実際に使っている環境を調べるには？

こんな感じで調べて、インタラプターを設定すればOK

> uv run display_environment.py

```python: display_environment.py
import sys

# Pythonの検索パスを表示
print("\nPython module search paths:")
for path in sys.path:
    print(path)
    
print("Python executable:", sys.executable)
print("Python version:", sys.version)
print("Python prefix:", sys.prefix)
```

たぶん、こっちでやっても同じ筈

```powershell: uv
uv python find
```

# よくある困りごと

## error: No virtual environment found; run `uv venv` to create an environment, or pass `--system` to install into a non-virtual environment

仮想環境がないと、<code>uv pip install</code> が出来ないってだけなので
<code>uv venv</code> で仮想環境作ればOK
あとは、compile/install してやる

```powershell: uv pip install を継続
uv pip compile .\requirements.in --output-file requirements.txt
uv pip install -r requirements.txt
```

## vs code で、import が反応しない

環境がずれてるので、uv で使ってる環境に合わせれば ok

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/9db74a35-a887-4e75-9bf3-0b15322360e2.png)


## warning: `VIRTUAL_ENV=venv3125` does not match the project environment path `.venv` and will be ignored

環境不一致ってことなので、以下のようにして一致させれば解決はする

```powershell: set environment
$env:UV_PROJECT_ENVIRONMENT  = "{to path}\venv3125”
```

# あとがき

徐々に理解が深まっていくと楽しいですな :laughing:
ほぼvim初心者のエンジニアがvimtutorやったみて自分なりにまとめてみた



ほぼvimの初心者の自分が、同じく初心者の方、
苦手と思う方向けにvimの基本的な操作をまとめてみました。

さらっと内容を確認していただいて、あとは`vimtutor`でハンズオンすれば実務でも問題ないレベルになれると思います。

vimの世界はそんな甘くないと思いますが、1、2時間あれば最後まで出来ると思うのでまずは触ってみてください。

## この記事で分かること

- vimの基本的なコマンドの操作方法
- vimtutorというハンズオン形式で学べる無料で最高の教材

## 対象者

- vim初心者の方
- vim操作しかできない状況で`:wq`と`i(insert mode)`で乗り切ったことがある人（自分）
- vimの基礎を学びたい方

# vimの基本的なコマンドの操作方法

### 各モード

`esc`: ノーマルモード
vimを開いたデフォルトの状態

`v`: ビジュアルモード
複数行で削除、コピーするときに便利。

`i`: インサートモード
矢印キーでの操作や文字入力ができる状態。
とりあえず、編集時にこのモード使えればどうにかなる。


## ファイル操作

`vim ファイル名`: 新規ファイル名または既存ファイルで編集開始
`:w ファイル名`: ファイル名で保存した新規ファイルが作成される
`:r ファイル名 or 外部コマンド`: ファイル名の中身または外部コマンドの実行結果をペーストする

`:w`: 保存のみ
`:q`: 保存せずに編集終了
`:wq`: 保存して編集終了
`:q!`: 強制終了

`!`を付けることで、操作途中で編集終了できない時に強制的に終了できます。


## ファイル内の移動（ノーマルモードのみ）

`k`: 上に移動
`l`: 右に移動
`j`: 下に移動
`h`: 左に移動

`0`: 行頭へ移動
`$`: 行末へ移動
`w`: 各単語の先頭へ移動(T i t i v の順で移動するイメージ)
`e`: 各単語の末尾へ移動(s s t n m の順で移動するイメージ)

ex. This is text in vim.


## 操作の取り消し、削除

`x`: カーソル上の一文字を削除
`dd`: 行全体を削除
`2dd`: 先頭の数字分の行を削除する(2行削除する)

`u`: 直前の操作を取り消す
`ctl+R`: 取り消しコマンドを取り消す(`u`コマンド自体を取り消すイメージ)


## ペーストコマンド

`p`: 特定のコマンドで対象とした文字、行を貼り付けする


## 置換コマンド

`r`: カーソル上の文字を置き換える
`ce`: カーソル以降の単語を削除後、insertモードへ以降し編集できる
`c モーション`: モーションの部分に`e`や`w`を指定すると特定の動作が追加されます。
`R`: カーソル上から文字を入力することで、入力した文字だけ置換される

`:s/変換される文字/変換したい文字`: 最初の見つかった文字のみ置換される
`:s/変換される文字/変換したい文字/g`: その行の対象文字全てが置換される
`:開始行,終了行s/変換される文字/変換したい文字/g`: 開始と終了行の間の全ての対象文字が置換される
`:%s/変換される文字/変換したい文字/g`: ファイル内の全ての対象文字が置換される
`/gc`: `gc`にすることで毎回置換するか確認して置換できる

ex. `:s/appli/app/g`


## 行の特定と移動

`ctl+g`: ページ下部にファイル情報と現在の行数が表示される

ex. `ファイル名 [Modified] line 89 of 98`

`行番号 G`: `ctl+g`で特定した行数をタイプ後に`G(大文字)`をタイプすると、その行数へ移動する。


## 検索コマンド


`/検索文字`: 検索対象が複数の場合に上から下へ検索する
`?検索文字`: 同じく、下から上へ検索する 
`n`: 検索中に次の単語へ移動する（検索時と同じ方向へ移動）
`N`: `n`と逆方向へ移動する
`ctl+o`: 元の位置に戻る

`%`: `(,[,{`などの括弧にカーソルを当て実行すると、対となる括弧へ移動する。
括弧を忘れて構文エラーになる場合に確認できる。


## 外部コマンドの実行

`:!外部コマンド`: 外部コマンドを実行できる

ex. `:!ls`で`ls`コマンドの実行


## vモードの操作

`v`: ビジュアルモードへ移動

・方向キーで範囲指定後に`y`でコピー、`p`でペースト
・上記と同様に範囲指定後に`:`をタイプすると`:'<,'>`が表示される。
`'<,'>w ファイル名`の形式で範囲指定の内容で新規ファイルが作成される。

## insertモードへの移行方法

`i`: insertモードへ移行
`o or O`: `o`でカーソルの下に移動しinsertモードへ、`O`はカーソル上に移動する
`a`: カーソルが次の文字へ移動しinsertモードへ

## オプションの指定

検索や置換するタイミングで一緒に設定する。

`:set オプション名`: オプションが設定される

### オプション
`ic`: 検索時に大文字小文字関係なくする
`is`: 検索時に部分マッチした箇所を表示する
`hls`: 検索にマッチした文字を強調する


## 最後に

vimtutorという無料の教材があります。

自分は実務でvimを触る機会が少ないですが、vimtutorのみで基本はある程度理解できました。
是非、ご自身のターミナルで下記コマンドを実行してみてください。
```
$ vimtutor
```
ハンズオン形式で一度通してやってみると理解が深まると思います。
クネビンフレームワークとスクラム


この記事は[ZOZOアドベントカレンダー2024](https://qiita.com/advent-calendar/2024/zozo)シリーズ８の９日目の記事です。

# クネビンフレームワーク

デイヴ・スノーデンが提唱した、クネビンフレームワークは、組織やリーダーが直面する状況を5つの領域に分類し状況がどの領域に属しているかを考えることで、意思決定や問題解決の対処を行うことができるフレームワークです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150455/080524d1-3e60-773c-a2f4-61bd1d016e8a.png)


各領域の説明は以下のとおりです。

1. 明白系（Obvious）：原因と結果が明確で、ベストプラクティスが存在する状況。「判断→行動→評価」の順で対処し、標準的な手順やマニュアルに従います。ウォーターフォールが合う世界です

2. 煩雑系（Complicated）：原因と結果は理解可能だが、専門知識や分析が必要な状況。複数の正解があり、「分析→判断→行動」で対応します。専門家の意見が重要です。グッドプラクティスの世界です

3. 複雑系（Complex）：原因と結果の関係が不明瞭で、予測が困難な状況。試行錯誤や実験を通じてパターンを見つけ、「行動→評価→判断」の順で進めます。開発はほぼこの領域に属します。ほとんどの状況で分析・過去の経験では解決できず、検査と適応が都度必要になります。ずばりスクラムが合う世界です

4. 混沌系（Chaotic）：原因と結果の関係が不安定で即時の対応が必要な状況。まずは秩序を回復し、「行動→判断→評価」のサイクルで収拾を図ります。バグ対応などがこの領域の例になります

5. 無秩序（Disorder）：どの領域にも分類できない混乱した状況。まずは状況を分析し、他の4つの領域に振り分けます

# スクラムにおけるクネビンフレームワーク

複雑な状況にスクラムが合うというのは、検査と適応を繰り返していくからですが、この点は「複雑」な状況をスプリントを通し「煩雑」な状況に、そして「明白」な状況にしていくことができると言い換えることもできます。

## 明白系へ

明白系はベストプラクティスな世界です。つまり、スクラムのイベントやアジャイルのプラクティスとされているCI、スタンドアップミーティング、スプリント自体、レトロプラクティス、スクラムボードなどはこの単純な世界でのプラクティスです。
複雑状況のうち直近で明らかになったものをスプリントで実現していく。スプリントの中では単純な世界になっているということですね。

## 煩雑系へ

スクラムボードの作り方なんかは、チームの状況に応じて作っていくことが必要で、ここはグッドプラクティスの世界になります。

## スクラムは「複雑」な状況をスプリントを通して「煩雑」「明白」にしていく

スクラムでは「複雑」な状況の一部を取り出す、そして、次のスプリントのためにそこだけを分析し、計画可能にします。つまり「煩雑」な状況にしていきます。そして、スタンドアップミーティングやレトロプラクティスなど明白なベストプラクティスを活用しながらスプリントを進めていきます。

また、インクリメントをレビューし、振り返ることで、複雑なプロダクトバックログを更に更新していきます。
（状況によりここで混沌な状況になることもあります。）

スクラムはこの循環を通して、複雑な状況に対処していくのです。

こうしてクネビンフレームワークを通してスクラムがどういうものか説明することができるのはなかなか面白いですね。
ABC372Fを解いた【inline DP】


筆者はレート800前後の茶～緑コーダ

ABC372のF問題を解いていく
    
https://atcoder.jp/contests/abc372/tasks/abc372_f



# 実装コード

普通にDPをした場合はTLEしてしまうので計算量を減らす必要があるらしい。

必ず+1の頂点には辺が付いているので
値を使い回すようにDPを組めばいいみたい。

そうした場合イレギュラーのM個の辺と
N番目の値を1番目に持ってくるだけでいいみたい。

```python:main.py
import sys
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rLI1(): return list(map(lambda a:int(a)-1,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
def err(*args): print(*args, file=sys.stderr)

class ModInt:
    def __init__(self, x, mod = 998244353):
        self.mod = mod
        self.x = x.x if isinstance(x, ModInt) else x % self.mod
    __str__ = lambda self:str(self.x)
    __repr__ = __str__
    __int__ = lambda self: self.x
    __index__ = __int__
    __add__ = lambda self, other: ModInt(self.x + ModInt(other).x)
    __sub__ = lambda self, other: ModInt(self.x - ModInt(other).x)
    __mul__ = lambda self, other: ModInt(self.x * ModInt(other).x)
    __pow__ = lambda self, other: ModInt(pow(self.x, ModInt(other).x, self.mod))
    __truediv__ = lambda self, other: ModInt(self.x * pow(ModInt(other).x, self.mod - 2, self.mod))
    __floordiv__ = lambda self, other: ModInt(self.x // ModInt(other).x)
    __lt__ = lambda self, other: self.x < ModInt(other).x
    __gt__ = lambda self, other: self.x > ModInt(other).x
    __le__ = lambda self, other: self.x <= ModInt(other).x
    __ge__ = lambda self, other: self.x >= ModInt(other).x
    __eq__ = lambda self, other: self.x == ModInt(other).x
    __ne__ = lambda self, other: self.x!= ModInt(other).x

class mint(ModInt):
    pass

def main():
    N, M, K = rLI()
    G = []
    for _ in range(M):
        G.append(rLI1())

    dp = [mint(0) for _ in range(N+K)]
    
    dp[K] = mint(1)
    for k in range(K,0,-1):
        add = []
        for i,j in G:
            add.append((j,dp[i+k]))
        dp[k-1]=dp[k+N-1]
        for j,v in add:
            dp[j+k-1] += v
    ans = mint(0)
    for i in range(N):
        ans += dp[i]
    print(ans)
    # err(*(int(dp[i])for i in range(N)))
    
if __name__ == '__main__':
    main()

```

# まとめ

ABC372Fを解いた。inlineDPを実装した。

成約数やグラフの構造を鑑みて計算を減らす工夫が必要なのは流石F問題といったところ。

このような問題を時間内で解ける日はいつ来るのだろうか。
kaggle tutorial time series part3

季節性に関するtutorial

https://www.kaggle.com/code/ryanholbrook/seasonality

季節性のあるもの：気温、人の移動、〇〇月特有のもの・・・
ここで、季節性と言っていますが、一年間ごとの季節、春特有の・・という意だけではなく、一定期間ごとにデータの周期性があるものを取り扱うことができます。
多くの時系列データでは季節性を考慮する必要があります。このチュートリアルではいくつかの季節性を取り扱います。

まず、季節性のかくにん



tutorialでは複数の関数を定義しています。
```

# サンプリング周波数を設定（1年→365回確認するということ）
fs = pd.Timedelta("365D") / pd.Timedelta("1D")

# 時系列データ ts の周期性を計算
freqencies, spectrum = periodogram(
    ts,
    fs=fs,
    detrend=detrend,  # デトレンド方法を指定
    window="boxcar",  # ウィンドウ関数を指定
    scaling='spectrum',  # スペクトルのスケーリング方法を指定
)
```
periodogramが突然出て、定数を返しています。
```解説
detrend=データから傾向（トレンド）を除去する方法を指定
よく使われる選択肢:
  - `'constant'`: 平均値を差し引く
  - `'linear'`: 線形トレンドを除去
  - `False`: トレンド除去を行わない
```

それらの関数を使用してグラフを生成
```
plot_periodogram(tunnel.NumVehicles);
```
![__results___8_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627091/3d4762ff-3a53-93d4-b6bb-35e27a270967.png)

できたグラフから季節性を確認できます。ここでは週ごとに一定の変動が見られることを示していますね。


データをフーリエ変換して、データをいくつかのsin,cos波で近似します。
```
# 年次の季節性を表現するために、10組のサイン・コサインペアを生成
fourier = CalendarFourier(freq="A", order=10)  # "A"は年次の頻度を意味する

#データ作成の部分
dp = DeterministicProcess(
    index=tunnel.index,          # 時系列データのインデックスを指定
    constant=True,               # バイアス（y切片）のためのダミー特徴量を追加
    order=1,                     # トレンドを1次関数であるとみなす
    seasonal=True,               # 週次の季節性をインジケータで追加
    additional_terms=[fourier],  # 年次の季節性をFourier項で追加
    drop=True,                   # 共線性を避けるために不要な項を削除
)

# tunnel.indexの日付に対する特徴量を生成(サンプリング)
X = dp.in_sample()

model = LinearRegression(fit_intercept=False)
_ = model.fit(X, y)
```

ここでこの解説も含め、多くの日本語のtutorial解説では、必要な解説が不足していたり適当なことを書いていたりします。または単に英語の翻訳をしただけの意味不明な文章も見られます。残念ながら、このtutorialは多くの解説が原文でも不足しています。正しく理解するにはしっかりとライブラリ公式によるドキュメントを読む必要があります。（英語ですが非常にシンプルにまとまっています）。この部分のtutorial後にドキュメントを日本語に訳した補足を追加出来たらとおもいます。

https://www.statsmodels.org/dev/generated/statsmodels.tsa.deterministic.CalendarFourier.html

https://www.statsmodels.org/dev/generated/statsmodels.tsa.deterministic.DeterministicProcess.html

先に述べた通り、別に週・年以外にも周期性があると考えたらadditional_termsの部分に入れること必要です。入れなかったらモデルはその周期性を考慮しません。

lagとの関係性：
これについては、株価の例題がわかりやすいと思います。
ある日、株価が急に下がった。では次の日の株価はどうなるか？２つの考え方がありますね。

1:下がる前の株価に戻る
2:前日も下がったし、今日も下がる

データのlag_nとの相関を調べることで１と２のどちらになることが多いかを調べることができます。
Dictを使ってロジックを単純にする。 jsonの読み込み結果の検証

読み込んだjsonファイルのデータ形式が意図したものになっているのかを検証したいときがある。

画像認識屋といえども、jsonファイルを避けていくことができない。

## 主旨：
jsonファイルのデータ形式の確認にDictを使って、制御構文を簡単にし、見通しを改善しようという話

## おかれている状況
- 該当のjsonファイルの詳細な設計仕様書はなく、個別のファイルがあるだけ
- jsonファイルの中のデータ形式は、入れ子になったDict型, List型を含む。
- keyの種類によって値の型が異なる。
- keyの種類によって値の範囲の確認が必要。


## やりがちなこと
- keyの種類によって値の型が異なるので、次のようなコードを書いてしまう。
```python:
for k, v in d.items():
    if k == "name":
        assert isinstance(v, str)
    elif k == "age":
        assert isinstance(v, float)
    else:
       print(f"unexpected key")
```

こういうのが増えてしまうと、
元のDict型dとの対応関係が保たれているのかが、しだいにわかりにくくなってしまう。

とくに、データ形式が多重に入れ子になっていると、このようなコードが複雑過ぎて、元のデータ形式と対応がとれているのかがわからなくなってしまう。

## 対策案：
各キーによって、どのような確認動作をするのが変わる。
確認動作を関数にまとめる。

ACTIONS = {キー：　そのキーに対応する値での確認動作の関数}
となるようなDict型を用意する。

そうすると
```python:
for k, v in d.items():
    assert ACTIONS[k](v)
```
ような記述が簡単になる。

このように記述すれば、制御構文が不必要に複雑になりすぎるのを防げる。
そして、動作の記述が足りているのかどうかは
ACTIONS というデータ形式の定義の確認で判断できる。


## 付記：

実は、jsonファイルの読み込みのデータ形式の確認にはもっといい方法がある。

pythonのclassをjsonファイルとして出力したり、jsonファイルを読み込んでclassのインスタンスをすることだ。

### 選択肢１： dataclasses_json

dataclasses_json を使うことです。

pip instal dataclasses_json

pythonの標準ライブラリにある@dataclassのデコレータを使ったうえで
@dataclasses_json のデコレータも合わせて使います。

そのような使い方は、記事があるので参照してください。

[dataclass_jsonを利用したJSONとPythonオブジェクトの相互変換](https://gri.jp/media/entry/8261)

[Dataclassをjson形式でシリアライズ](https://horomary.hatenablog.com/entry/2019/11/24/033557)

### 選択肢２： pydantic
[PythonのPydanticで堅牢な開発](https://qiita.com/Tomato_otamoT/items/73a2ae7f03354a42c53a)

どちらにしても、自前のコードの行が少なくなるので、保守性がよくなるはずです。

pydantic の素晴らしいところ

```python:
from pydantic import BaseModel

class Something(BaseModel):
```

のようにpydanticを利用してclassを定義すると、
それを使うことで、インスタンスから辞書形式の生成、辞書形式からインスタンスの生成ができるという点です。
このため、それらの手法が標準化されます。
ユーザーが自分で書かなければならないコードが少なくなります。



## 付記２：
自作アプリケーションの設定ファイルのデータ形式を選択する際に
- jsonファイル
- toml ファイル
- yaml ファイル
などが候補になるだろう。
TiDBの周辺ツールの紹介

## はじめに

データベースの運用ではデータベースの中のデータを出し入れする必要が生じます。TiDBにはこのようなときに利用できる豊富な周辺ツールがあるのですが、あまり全体像について書いたものがありません。

本記事ではそのような周辺ツールの全体像を紹介します。また、TiDB Cloudで利用可能かどうかについても言及します。

## 全体像

TiDBの周辺ツールの全体像はこちらです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/5e8a526f-d633-a739-0777-6538038e14e0.png)

これらのツールの多くは `tiup` から利用可能になっています。
一つひとつ見ていきましょう。

### BR: バックアップ＆リカバリツール

:::note info
TiDB Cloudではバックアップ・リストアが標準で利用可能です。
:::

BRはTiDBのバックアップ・リストアツールで、TiDBのデータが保存されているTiKVのデータファイルのバックアップを作成します。s3へのエクスポートオプションもあります。

https://docs.pingcap.com/ja/tidb/v5.4/backup-and-restore-tool

その動作上TiKVへの直接アクセスが必要になるため、TiDB CloudではツールとしてのBRは利用できません。標準のバックアップとリストアを利用します。

### Dumpling: データエクスポートツール

:::note info
TiDB Cloudのクラスタに対しても利用可能です。
:::

DumplingはTiDBからデータをエクスポートするツールです。TiDBだけではなくMySQLに対しても利用可能です。CSV/SQL形式でクラウドストレージへの直接出力もサポートし、条件を指定した出力や、サイズや行数で出力ファイルを分割したりなど、高機能かつ高速な出力が可能です。

https://docs.pingcap.com/ja/tidb/stable/dumpling-overview

### Lightning: データインポートツール

:::note info
TiDB Cloudではimportが標準で利用可能です。
:::

Dumplingの逆で、データをimportするツールです。Dumplingの出力をインポートするように設計されています。TiKVに直接データを書き込むPhysicalインポートモードをサポートし、高速なインポートが可能です。（Insert文として実行するLogicalインポートモードもあります）

TiDB Cloudではインポートツールとして提供されています。Lightning同様にDumplingでクラウドストレージに出力したファイルをインポートすることができます。

PhysicalインポートモードはTiKVに直接書き込むためTiDB Cloudでは利用できませんが、LogicalインポートモードをTiDB Cloudに対して利用することは可能です。ただTiDB Cloudの場合は標準のインポート機能を利用することをおすすめします。

https://docs.pingcap.com/ja/tidb/stable/tidb-lightning-overview/

### DM: データ移行ツール

:::note info
TiDB Cloudではオプションで利用可能です。(Data Migrationメニュー/Dedicatedのみ)
:::

AWSのDMSのような、データ移行を行うツールです。移行元としてTiDBとMySQL、移行先としてTiDBをサポートしています。DMS同様に一括のデータ移行と、継続移行モードをサポートしています。一括データ移行は実質Dumpling+Lightningです。

TiDB Cloudではマネージドサービスとして、メニューから起動するだけで利用できます。
DMをセルフホストで起動してTiDB Cloudに対して利用することもできます。

https://docs.pingcap.com/ja/tidb/stable/dm-overview

### TiCDC: チェンジフィードツール

:::note info
TiDB Cloudではオプションで利用可能です。(Changefeedメニュー/Dedicatedのみ)
:::

TiCDCはTiDBの更新差分を外部システムに連携するツールです。いわゆるCDC(Change Data Capture)と呼ばれるツールです。主にTiDBクラスタ同士の同期に利用されるツールですが、連携先はTiDBだけではなくMySQLやS3, DebeziumやKafkaなどがあり、更新情報を使ったイベント駆動アプリケーションの実装も可能です。

TiDB Cloudではマネージドサービスとして、メニューから起動するだけで利用できます。
TiKVに直接アクセスする必要があるため、セルフホストのTiCDCをTiDB Cloudに対して利用することはできません。

https://docs.pingcap.com/ja/tidb/stable/ticdc-overview

### sync-diff-inspector: 差分確認ツール

:::note info
TiDB Cloudのクラスタに対しても利用可能です
:::

sync-diff-inspectorは、TiDB同士、もしくはMySQLとTiDBのデータ比較を行うツールです。
主に移行でデータが正しく移行できたかどうかの確認に利用されます。
TiDB Cloudに対しても利用可能です。

差分がある場合に差分を修正するためのSQLが生成されるのも面白い特徴です。

利用方法については、下記のブログに詳しく記載されています。

https://zenn.dev/tk0403/articles/81e3edf80ecfc9

## おわりに

いかがでしたでしょうか。また別の記事で個別のツールについては詳しく見ていきたいと思います！
今一度基礎からアウトプットしていこう。OS編

存在は知っていても、意外と説明することができないものって多い。
けど堅苦しい言葉だとすぐに忘れるから、何かに例える。

ということで、今日から地道に消化していこう。紹介していこう。
ここでは自分用に言い換えたりしているので認識がずれていたら申し訳ない。

# OS（オペレーティングシステム）

### 概要
パソコンやスマートフォンに入っている基本ソフトウェア。
様々な機能があるが、本業は「リソース管理」。

他にもユーザインターフェースの提供、ネットワーク等に使用される。
ゆるコンピュータラジオさん曰く、【伊藤忠】

### 種類

##### WindowsOS
世界最大のシェア率を誇り、主に企業用ソフトが豊富。
Microsoft作。

##### unix
現存する中で最も古いOS。
ハードウェアに依存せず、様々なコンピュータに移植しやすかった。

##### Linux
unixを参考に、ゼロから開発されたOS。
一番の特徴はOSS(オープンソースソフトウェア)であること。
派生形の話は今回割愛。

##### macOS
音楽、アートのようなクリエイティブ系のソフトが豊富。
デザインとGUIに優れたOS。Apple作。

# これらを踏まえて
自分の好きなOSを自覚しておくと良い。
よりコンピュータの世界への解像度が上がるのだ。
ちなみに私はUnixでした。ロマンを感じるのだ。
生物物理屋がSIGNATEの初心者向け課題に挑戦してみた話

## はじめに
この記事は「[ただただアウトプットを癖付けるための Advent Calendar 2024](https://qiita.com/advent-calendar/2024/newbie2024)」に投稿した記事です。

[最初の記事](https://qiita.com/yotabouya/items/e63c53c5e18b3cf6a454#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB)にも書いた通り、私は生物物理の実験を専門にしている研究者です。
最近はデータ解析のため機械学習のコード開発も行っており、幸いにもその成果が[NeurIPSに採択されました](https://neurips.cc/virtual/2024/poster/93465)。

競技プログラミングの存在は知っていました。
Kaggleくらいしか知らず、ややハードルが高いものという印象でした。
しかし、SIGNATEというサービスがあることを知り、今回は初心者向けの課題に挑戦してみました。

## 関連記事

#### 前の記事「[生物物理屋が日本語LLMを試してみた話](https://qiita.com/yotabouya/private/f15f62f658eabd0fcac2)」

#### 次の記事「[NeurIPS著者が今更JAXを使ってみた話](https://qiita.com/yotabouya/private/ceb9cc1489600dc76041)」

## SIGNATE

SIGNATEは、データサイエンスのコンペティションを提供しているサービスです。
[こちら](https://signate.jp/)からアクセスできます。

通常のコンペティションに加えて、[初心者向けの課題](https://signate.jp/competitions/practice)も提供されています。

目に付く課題として、[毒キノコの分類問題](https://signate.jp/competitions/105)をやってみることにしました。
選んだ基準は、毒キノコに触れる機会はなかなかないのではないかということです。あと、なんだか目を引きますよね。

## データセットと回答方法

コンペのページには、トレーニングデータ、テストデータがtsv形式で提供されています。
トレーニングデータには、キノコの各種特徴を表すアルファベットと、そのキノコが毒キノコか否かが記載されています。
テストデータには、キノコの各種特徴のみが記載されています。
これらを用いて、テストデータの各キノコが毒キノコか否かを予測するのが課題です。

ページには、ほかにも提出用のサンプルファイルがcsv形式で提供されています。
このファイルには、テストデータのキノコが毒キノコであるかどうかが記載されています。
これは正解というわけではありません。
単に、この形式にのっとったcsvファイルを提出することで、スコアが算出されるということです。

## モデルの選択

データセットを見ると、特徴量がアルファベットで表されていることがわかります。
これは、カテゴリカルデータとして扱う必要があります。
そこで、決定木を使ってみることにしました。

## プログラム

今回は練習問題ということで、だいぶ端折っています。
以下の手順で提出用ファイルを作成し、提出しました。

- pandasを使ってデータを読み込み (read_csv)
- トレーニングデータを学習用と検証用に分割 (train_test_split)
- カテゴリカルデータをバイナリのベクトルデータに変換 (OneHotEncoder)
- ランダムフォレストを定義 (DecisionTreeClassifier)
- 学習 (fit)
- テストデータを予測 (predict)
- 提出用ファイルを作成 (to_csv)

提出そのものは、練習問題のページを開いて参加をクリックしたのち、提出ボタンからファイル選択するだけです。
提出直後は評価値が表示されませんが、5分ほどするとスコアが表示されるようになりました。

## 結果

決定木の深さだけは変えてみました。
3, 5, 10を試したところ、深さ10では検証用データでの正答率が100%になりました。
そこで、深さ10で提出したところ、スコアは1でした。
とはいえ、これは練習問題ですので、自動的にスコアが1になるように設定されているのかもしれません。
追加で深さ3も提出しました。
深さ3と深さ10の予測値はほぼ同じですが、1.5%ほどの差がありましたので、正答率がスコアに反映されているならば、両者のスコアが1になることはないでしょう。
実際、深さ3のスコアは0.9852290でした。
実はこの値、トレーニングデータでの正答率とほぼ同じです。
間接的に、過学習しすぎていないことがわかるという結果になりました。
Cloud Integration Hub のトピックのレコードのメンテナンスについて (エラー編)

この記事は[インフォマティカ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/informatica) Day 13 の記事として書かれています。

# はじめに

前回 [Cloud Integration Hub のトピックのレコードのメンテナンスについて](https://qiita.com/syoshid/items/b8230d8bd81ea66af766) という記事の最後で「またいずれ記事を分けてご紹介したいと思います」と書いてからあっという間に一年が過ぎました。
前回はサブスクリプションイベントが全て成功しているケースについて説明しました。今回は、サブスクリプションイベントがエラーになった場合の動作をご紹介します。

Cloud Integration Hub (以下 CIH) のメンテナンスのジョブの概要については、前回の記事をご参照ください。

# 下準備
前回使用した topic_retention_test2 を使用します。
「コンシュームされるデータの保持期間」は「1」、「コンシュームされないデータの保持期間」は「3」に設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335778/73608684-4a20-05ee-88e4-939a79591ac9.png)

パブリケーションの実行間隔も前回と同じく日本時間 0:00 と 12:00 に実行するようスケジュールします。サブスクリプションのスケジューリングは「パブリッシュ済みデータの準備ができている場合」を選択し、パブリケーション実行後すぐにサブスクリプションが実行されるようにします。

そして、今回はエラーが発生した場合の動作について確認するため、意図的にサブスクリプションを一回失敗させました。サブスクリプションが失敗した日時は**2024/12/6 23:44:370 JST** です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335778/028726d7-1c27-cfd8-06df-ec59a6427467.png)

# 削除対象の確認
ここで、2024/12/10 のクリーンアップジョブのメンテナンスレポートを確認してみます。

(2024/12/10 8:00 JST のメンテナンスレポートより)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335778/65874176-08bc-762a-754c-7383b339f106.png)

前回、すべてのサブスクリプションが正常にコンシュームされた場合には 「__メンテナンスジョブが実行された前日以前でコンシュームされた最新のパブリケーションイベントを起点に「コンシュームされるデータの保持期間」分データが保持される__」ことをご紹介しました。

今回すべてのサブスクリプションが実行されています。「メンテナンスジョブが実行された前日以前でコンシュームされた最新のパブリケーションイベントを起点に「コンシュームされるデータの保持期間」分データが保持される」のであれば、2024/12/10 8:00 JST 時点では、12/9 から「コンシュームされるデータの保持期間」である 1 日を引いた 12/8 までが保持対象となり 12/7 以前のデータが削除されるように思えますが、__削除対象日は 12/6 になっています__。

# イベントがエラーの場合の削除対象の計算
サブスクリプションがエラーとなった場合のパブリケーションのコンシュームステータスは「エラー」であり、データがコンシュームされたとみなされません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2335778/8b7c2250-036a-2385-8a69-ab132dcea53c.png)

前回「サブスクリプションイベントが存在しない場合(topic_retention_test)」の項で、「コンシュームされないデータの保持期間」を計算する時には、メンテナンスジョブが実行される日にちが基点になると記載しました。
上述の通り、失敗しているイベントはコンシュームされていないとみなされるため、この状態では「コンシュームされないデータの保持期間」の設定が有効となります(*)。
そのため、メンテナンスジョブを実行する 12/10 から「コンシュームされないデータの保持期間」で指定している 3 日の保持期間の後の日にちである 12/6 が削除対象日になります。

(*) 正確には、存在しているコンシュームされていないイベントの中で最も古い日付に「コンシュームされるデータの保持期間」を引いた日にち(a)と、メンテナンスジョブ実行日から「コンシュームされるデータの保持期間」を引いた日にちのうち、新しい方の日にち(b)が削除対象日となります。今回は (a) は 12/4、(b) は 12/6 となるため、より新しい日にちである 12/6 が削除対象日となります。

尚、エラーとなったイベントがある場合、コンシュームされていないイベントの中で最も古いイベントが削除対象の日にちの計算で考慮され、それ以降で実行されたイベントはステータスに関わず削除されません。
例えば、上記画面では、ID:3332798 のパブリケーションの子イベント ID:3334263 のサブスクリプションは成功しておりコンシュームステータスは「最終」です。しかし、その前の ID:3334254 がエラーのため、12/7 (すべてのサブスクリプションイベントがコンシュームされている場合に削除対象日となる日にち)に実行されているにも関わらず削除対象とならずにそのまま保持されます。

# 終わりに
この記事では、シンプルな例を元にサブスクリプションでエラーが発生した場合のメンテナンスジョブの削除対象の計算方法をご案内しました。「サブスクリプションは成功しているのに「コンシュームされるデータの保持期間」を超えてレコードがトピックテーブルに残っている」という場合には、過去のサブスクリプションのステータスを確認すると、エラーになっているイベントがあるかもしれません。

尚、上記例のように「コンシュームされるデータの保持期間」と「コンシュームされないデータの保持期間」の設定が近い場合には、サブスクリプションイベントがエラーとなった場合のトピックテーブル内のレコード数への影響は小さくなりますが、「コンシュームされないデータの保持期間」が大幅に長い場合にはエラーのイベントが影響して多くのレコードがトピック内に残る可能性が考えられます。
前回の記事の最後にも記載した通り、トピックは永続的にデータを保持する用途のデータベースではありません。トピックのデータベースの肥大化やメンテナンスジョブによるレコードの削除処理の高負荷を防ぐため、データの保持期間は適切な日数に設定してご使用ください。
ABC372Eを解いた【UnionFind】


筆者はレート800前後の茶～緑コーダ

ABC372のE問題を解いていく
    
https://atcoder.jp/contests/abc372/tasks/abc372_e


# 実装コード

kの最大値が10なのを利用して
UnionFindの各ノードにに長さ10のリストを乗せて
マージするたびにソートすればOK

```python:main.py
import sys
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rI1(): return (int(sys.stdin.readline().rstrip())-1)
def rLI1(): return list(map(lambda a:int(a)-1,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
def err(*args): print(*args, file=sys.stderr)


class UnionFind:
    def __init__(self, n, k):
        self.parent = [-1] * -~n
        self.rank = [1] * -~n
        self.count = n
        self.k = k
        self.L = [[i]+[-1]*~-k for i in range(-~n)]
    def root(self, x):
        while (p:=self.parent[x]) != -1:
            x = p
        return x
    def union(self, x, y):
        xr = self.root(x)
        yr = self.root(y)
        # err(xr,yr)
        if xr == yr: return
        XL = self.L[xr]
        YL = self.L[yr]
        ML = sorted(XL+YL,reverse=True)[:self.k]
        # err(ML)
        if (rx:=self.rank[xr]) < (ry:=self.rank[yr]):
            self.parent[xr] = yr
            self.rank[yr] += rx
            self.L[yr] = ML
        else:
            self.parent[yr] = xr
            self.rank[xr] += ry
            self.L[xr] = ML
        self.count -= 1
    def same(self,x,y):
        # err(self.root(x), self.root(y))
        return self.root(x) == self.root(y)
    
def main():
    N, Q = rLI()
    UF=UnionFind(N,10)
    for _ in range(Q):
        q, *I = rLI()
        if q==1:
            UF.union(*I)
        if q==2:
            v,k=I
            rv = UF.root(v)
            ans = UF.L[rv][k-1]
            print(ans)
    
    
if __name__ == '__main__':
    main()

```

# まとめ

ABC372のE問題を解いた

当時はグラフの問題はできませんorzとか嘆いていた問題だけど、UnionFind一発で解けたので成長を感じた。
後からテストを書くとこういう発見がある

# はじめに
皆さんはテストを書いていますでしょうか？

この記事では私が経験した、「仕様書なしで運用が始まったシステムに後からテストを書く」について、発見したことをかんたんにご紹介します。


### コード通りのテストケースしか分からない

コードを読み解いてテストを書くわけですが、条件通りのケースしか書けません。
素直に全パターン書くのは可能ですが、テスト数が多いと全部通すのに時間がかかります。
可能であれば必要十分なケース数で済ませたいところですが、数を削る判断基準がありません...

例えば...

- 他の箇所でテストしているから不要

- 仕様上ありえないから不要

### 使っていないメソッドが発見される

テスト対象のメソッドの呼び出し元を辿っていき、処理の流れを把握するのですが、検索で引っかからないということもしばしば。

### エラーハンドリングされていない
処理の途中で空配列になった際を考慮できていなかったり。

### 同じ名前で似た処理のメソッドが存在する
8割くらい同じで、2割は違う内容のメソッドがあったり。
複数人で開発していて、それぞれ似たような処理を実装した、というところでしょうか。



# おわりに
色々な発見があって面白かったです。
他の人が書いたコードをもとにテストを書くので、「テストを書きやすいコード」の基準がだんだんと分かってきました。自分が書く際には気をつけようと思います。
NetSuiteの開発生産性向上chrome拡張機能：NetSuite: Show Field IDs

## はじめに

NetSuite開発及び利用において、私自身、他にもインストールしていますが、NetSuite開発の生産性に寄与するchrome拡張機能を今日は1つだけ、ご紹介します。

:::note info
これはほんの一部です。Chromeウェブストアで"NetSuite"と検索すると色々出てきますので、皆さんお試しください。
:::

:::note warn
　本投稿は、今回ご紹介した機能を完全に保証するものでは御座いません。実際の制約等は、拡張機能自身の説明にてご理解ください。
:::

# 1.Show Field IDs
  これは、わざわざヘルプリンクをクリックしなくともフォーカスするだけでフィールドIDが見えます。コピーもできます。

　[Show Field IDs](https://chromewebstore.google.com/detail/netsuite-show-field-ids/pnhdmfiakolonplgblabffnpfhfplmbh)

#### Before(NetSuite標準)
　NetSuite標準では項目にカーソルをあてるとHelpと表示されるのみ。フィールドIDはクリックして、ヘルプを表示させて確認する。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114483/99be49bd-c9e4-d561-2a9c-f717667283b5.png)


#### After(拡張機能インストール後)
 項目にカーソルをあてるとフィールドIDが表示される。Shiftを押しながらクリックすると、コピーされる。便利だと思います。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114483/2c4eaec9-df4e-6ec9-4b66-99608f7d1b81.png)

## さいごに
 冒頭でも述べましたが、NetSUiteを開発、利用する際に便利機能はまだまだ存在します。これを機に色々試してみてはいかがでしょうか？
Markdown Aiの使い方

この記事では、Markdown Aiを使ったウェブサイトやドキュメント作成方法を、初心者向けに分かりやすく解説します。

## 1. Markdown Aiとは？

`エンジニアが気軽に情報交換できるだけでなく、子どもたちが簡単にWebサイトを作成し、世界中の人々と交流できることをコンセプトとしています。`
(公式ドキュメント一部抜粋)

以下が主な特徴です：

- 直感的な操作：プログラミングスキル不要。
- AI統合機能：プロジェクト専用AIを容易に作成可能。
- 迅速な公開：ワンクリックでウェブページを公開。

短時間でブログ記事や技術ドキュメントを作成できます。


## 2. 導入手順

### 必要な環境
- 安定したインターネット接続
- Googleアカウント（必須）

### 登録方法
以下の手順で始められます：
1. [公式サイト](https://mdown.ai/)にアクセスし、アカウントを作成。
2. 新規プロジェクトを作成。
3. テンプレートを選択してMarkdown形式で記述。
4. プレビューで確認後、公開ボタンをクリック。

markdown-ai create "My New Project"

https://mdown.ai/

## 3. 機能と活用法

### 基本機能
- リアルタイムプレビュー：編集内容を即確認。
- 簡易公開機能：サーバー設定不要で即公開。
- Aiを使った文章生成
- Markdown機能：見出し、リスト、コードブロックを簡単に挿入。
- AIを使った画像生成

### 応用機能
- Aiを使った要約機能
- 

## 4. 注意点

- オフラインでは利用不可。
- プレビューで表示を必ず確認してから公開してください。
- AIが生成したコンテンツは最終確認が必要。

## 6. まとめ

Markdown Aiは初心者から上級者まで使える便利なツールです。この記事を参考に、新しいプロジェクトを始めてみましょう！
Chakra UI V3でスニペットを追加したあとビルドが通らない

## 状況
Chakra UI V3のインストール後、`npx @chakra-ui/cli snippet add`を実行しスニペットを追加した。

その後、`npm run build`を実行したところ、
```
error TS2322: Type '{ children: ArkTooltip.RootBaseProps; asChild: true; }' is not assignable to type 'IntrinsicAttributes & TooltipTriggerProps & RefAttributes<HTMLButtonElement>'.
  Property 'children' does not exist on type 'IntrinsicAttributes & TooltipTriggerProps & RefAttributes<HTMLButtonElement>'.

30         <ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
```
上記のような型に関するエラーが大量に発生する。

## 解決策
tsconfig.jsonに`"exclude": ["src/components/ui/*"]`
を追記しTypeScriptのコンパイル対象から除外することでビルドが通るようになった。

ちなみにtsconfig.jsonではなくtsconfig.app.jsonにexcludeを記述した場合、ビルドが通らなかった。

これは、tsconfig.jsonがTypeScriptがどのようにJavaScriptにコンパイルされるかを決めるグローバルなファイルに対し、tsconfig.app.jsonはアプリケーション固有の設定を含み、tsconfig.jsonを拡張する形で使用されるという違いがあるかららしい。
kintoneとAIでクレームを円満解決するアプリ作ってみた

今回はアドベントカレンダー・・・・ということで、
ご意見円満解決アプリを作ってみました。

プロンプトによるんだから何アプリでもいいでしょ！とか言わないでください👀

## 事前準備
OpenAIのAPIキーを取ってきてね。
※調べてみてね！

## アプリをつくる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/3bb1e63f-785c-e8e7-1480-f5df0abfd3ca.png)

|フィールド|フィールドタイプ|フィールドコード|備考|
|--|--|--|--|
|ご意見|文字列（複数行）|ご意見|来たご意見を書くところ|
|---|スペース|btn|ボタンを置くところ|
|ご意見の要点と回答例|文字列（複数行）|ご意見の要点と回答例|ボタン押したら回答例書いてくれるところ|

## サンプルコード

```js
(() => {
  'use strict';
  kintone.events.on(['app.record.create.show','app.record.edit.show'], (event) => {
    // 新規作成画面、編集画面開いたときにスペースフィールドにボタンを設置する。
    const spbtn = kintone.app.record.getSpaceElement('btn');
    const btn = document.createElement('button');
    btn.textContent = '日本語でokボタン';
    spbtn.appendChild(btn);
    
    btn.addEventListener('click', async ()=>{
      const apiKey = 'OpenAI API の APIキー'; // APIキーを入力
      const apiUrl = 'https://api.openai.com/v1/chat/completions';
      const headers = {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`
      };

      const record = kintone.app.record.get();
      const data = {
        model: 'gpt-4o-mini',
        messages: [
          { role: 'system', content :`あなたはクレーム処理のプロの、スーパーの店長です。`},
          { role: 'user', content: `次の文章はスーパーにきたクレームです。何に答えたらいいのか分かりやすいよう、要点をまとめて、何に答えたらいいのか明らかにしてください。また、回答例も記述してください。文章：${record.record['ご意見'].value}` }
        ]
      };

      const response = await fetch(apiUrl, {
        method: 'POST',
        headers: headers,
        body: JSON.stringify(data)
      });
      const responseData = await response.json();

      record.record['ご意見の要点と回答例'].value = responseData.choices[0].message.content;
      kintone.app.record.set(record);
    });
    
    return event;
  });
})();
```

## どんなかんじになるのか？
例えばこんなクレームが来たとする。

「レジの店員、感じが悪すぎる！無愛想で、こちらが質問してもぶっきらぼうに答える。お客は金を払って商品を買ってるんだぞ！もっと丁寧な対応をしてほしい。それに、いつもレジが混んでるのに、新しいレジを開けないのはなぜ？客をバカにしてるのか？」
※Geminiにクレームを考えてもらいました。

しまった、ボタンの名前が非常に良くない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/5726ae51-b752-2142-0bf7-d61788f4c323.png)

保存するとこんな感じ。
さすが店長！たよりになるなぁ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/536148/de4cd786-51b4-30da-7646-596d81947de2.png)

お・し・ま・い！
俺的Azure年報2024

# はじめに

　本年は家庭で配車要請等が多発し、新しいAzureの学習が難しい状況になってしまった。その中において、現状のリソースに大きな変更を行わず、サポート切れなどの対応にとどめながら、効率的な活用を意識する1年となった。
 　その中においても、新しいAzureコミュニティ「Japan Azure Travelers」が誕生し、自らの求める元々の自分像と合致したことで、新しい土地での経験を踏み出す1年となった。

# セッションコレクション

* 3月21日開催　Tokyo JAZUG Night #47
コスト削減 by オーナーシップ ～俺たちはQuick Winで満足しない～
<br>

* 6月22日開催　第3回 Azure Travelers 勉強会 山形の旅
さくらんぼと山形に関するレクチャー（YG-900）

<br>

* 6月27日開催　Tokyo JAZUG Night #48
俺的 Build 2024 (付近の) アップデートまとめ
<br>

* 10月19日開催 第4回 Azure Travelas勉強会　福岡の旅
Microsoft Entra External IDとAzure AD B2C
Azure AI servicesと歯のおはなし
<br>

* 11月11日開催　JAZUG Fukuoka #1
SASを使ったAzure Storageのセキュアなアクセス管理法

# 次年以降の目標
主に以下の内容を中心に取り組む予定。
* ストレージ集約の活用検討
 現在は、Onedrive＋シェアポイント＋オンプレミス2台でデータ管理運用を行っているが、これにより、データドライブの位置が不明確であることや未利用のハードドライブが存在していることも課題だ。また、大容量ドライブの市場価格が下降気味になっていることから、次年度については一部のドライブの置換並びに集約化を目指す。その際にクラウド上での一時保管ができないかを検討する。

* ユーザーのMicrosoft Entra Connectへの接続を完了させ、ハイブリッド接続を確立させる。

* ホームページ運用に関して、より効率的な運用を目指す計画を策定する
Azure Static Web Appsを利用して運用を効率化する
Azure Monitorでサイトパフォーマンスを追跡する<br>

* AZ-104の取得に向けて駒を進める

# おしまいに
　2024年11月以降、福岡リブートイベント以降、徐々に各地方でのイベントも再び動き始める感じはする。一部オンプレ回帰といわれる中において、今後は「なぜクラウドなのか？」を意識してキャッチアップと身近なシーンでのプラクティスをしていく必要がありそうだ。
NetSuiteの開発生産性向上chrome拡張機能：NetSuite Field Explorer

## はじめに

NetSuite開発及び利用において、私自身、他にもインストールしていますが、NetSuite開発の生産性に寄与するchrome拡張機能を今日は1つだけ、ご紹介します。

:::note info
これはほんの一部です。Chromeウェブストアで"NetSuite"と検索すると色々出てきますので、皆さんお試しください。
:::

:::note warn
　本投稿は、今回ご紹介した機能を完全に保証するものでは御座いません。実際の制約等は、拡張機能自身の説明にてご理解ください。
:::

# 1.NetSuite Field Explorer
  NetSuiteの画面に散らばっているフィールドデータをサクッと纏めて参照出来るものです。

　[NetSuite Field Explorer](https://chromewebstore.google.com/detail/netsuite-field-explorer/cekalaapeajnlhphgdpmngmollojdfnd)
 
#### After(拡張機能インストール後)
 レコードが表示されている画面でボタンを押すと、画面に存在するオブジェクト及び値が一覧表示されます。特定文字で検索も可能です。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114483/a55bb2f7-6125-93c0-25ac-d47372b3f4d7.png)

## さいごに
 冒頭でも述べましたが、NetSuiteを開発、利用する際に便利機能はまだまだ存在します。これを機に色々試してみてはいかがでしょうか？