OmniAICreator/Qiita-1.07M huggingface dataset:
LINE WORKS の掲示板の投稿の既読/未読リストを API で取得する

LINE WORKS の掲示板の投稿の既読/未読リストを API で取得できるようになりました。

この記事では、Google Colab で Python を使用してAPIから掲示板の既読メンバーリストを取得し、既読者と未読者を分けて表示してみます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">やってみました。<a href="https://twitter.com/hashtag/LINEWORKS?src=hash&amp;ref_src=twsrc%5Etfw">#LINEWORKS</a> <a href="https://t.co/vUVehLNLIy">https://t.co/vUVehLNLIy</a> <a href="https://t.co/iSmxYDL8dq">pic.twitter.com/iSmxYDL8dq</a></p>&mdash; iwaohig (@iwaohig) <a href="https://twitter.com/iwaohig/status/1803028555284336910?ref_src=twsrc%5Etfw">June 18, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 使用するライブラリのインストール
まず、Google Colabで必要なライブラリをインストールします。

~~~python
!pip install requests pandas
~~~
# スクリプトの実装
## セル1: パラメータ設定
以下のセルで必要なパラメータを設定します。自身の掲示板ID、投稿ID、およびアクセストークンを設定してください。

~~~python
# 必要なパラメータを設定
board_id = 100  # 掲示板ID
post_id = 1  # 投稿ID
token = "YOUR_ACCESS_TOKEN"  # 取得したアクセストークン
~~~
## セル2: データ取得および表示
次に、データを取得し、既読者と未読者を分けて表示するスクリプトを以下のセルに記述します。
~~~python
import requests
import pandas as pd

def get_all_readers(board_id, post_id, token):
    url = f"https://www.worksapis.com/v1.0/boards/{board_id}/posts/{post_id}/readers"
    headers = {
        'Authorization': f'Bearer {token}'
    }
    
    readers = []
    next_cursor = None
    
    while True:
        params = {}
        if next_cursor:
            params['cursor'] = next_cursor
        
        response = requests.get(url, headers=headers, params=params)
        
        if response.status_code == 200:
            data = response.json()
            readers.extend(data['readers'])
            next_cursor = data.get('responseMetaData', {}).get('nextCursor')
            
            if not next_cursor:
                break
        else:
            print(f"Error: {response.status_code}")
            print(response.json())
            response.raise_for_status()
    
    return readers

# 既読メンバーリストを取得
try:
    readers_data = get_all_readers(board_id, post_id, token)
    
    # データをPandasデータフレームに変換
    readers_df = pd.DataFrame(readers_data)
    
    # 既読者と未読者に分ける
    read_df = readers_df[readers_df['isRead'] == True]
    unread_df = readers_df[readers_df['isRead'] == False]
    
    # 既読人数と未読人数を表示
    read_count = len(read_df)
    unread_count = len(unread_df)
    
    print(f"既読人数: {read_count}")
    print(f"未読人数: {unread_count}")
    
    # データフレームを表示
    print("\n既読者:")
    print(read_df)
    
    print("\n未読者:")
    print(unread_df)
except requests.exceptions.HTTPError as err:
    print(f"HTTP error occurred: {err}")
except Exception as err:
    print(f"Other error occurred: {err}")
~~~

## 説明
### ライブラリのインポート:
- requestsとpandasライブラリをインポートします

### get_all_readers関数の定義:
- 指定された掲示板IDと投稿ID、およびアクセストークンを使ってAPIリクエストを送信し、既読メンバーリストを取得します
- カーソルを使用して全てのデータを取得するまでループを続けます
### データの取得と表示:
- 取得したデータをPandasデータフレームに変換します
- 既読者と未読者にデータを分け、それぞれの人数を表示します
- 既読者と未読者のデータフレームを表示します

# まとめ
この記事では、Google Colab を使用して LINE WORKS API で掲示板の既読メンバーリストを取得し、既読者と未読者を分けて表示する方法を紹介しました。
printlnとfmt.Printlnの違いが気になったので少し調べた

Goにはbuildinで定義されるprint, println関数がある。

一方でfmtパッケージにもPrint, Println関数がある。

Goを学ぶとサンプルコードではfmt.Printlnで標準出力に出力することが多いが、print, println関数も使える。

この差について気になったので本記事でまとめる。

## 結論

print, printlnは標準エラー出力に、fmt.Print, fmt.Printlnは標準出力に出力される。

## 標準出力と標準エラー出力

標準出力とは、プログラムの出力を表示するために使われるチャネルのことを指す。

コンピュータのシステムやプログラムの情報の"正常"な出力を表示するために使われ、一般的にはディスプレイに表示される。

例えば`ls`コマンドなどはファイルやディレクトリの一覧情報などを標準出力に出力する。

一方で標準エラー出力は、プログラムのエラー情報を表示するために使われるチャネルのことを指す。

例えば`ls`コマンドで存在しないディレクトリを指定するとエラー情報が標準エラー出力に出力される。

どちらも出力先はディスプレイであるが、標準出力は正常な情報、標準エラー出力はエラー情報を表示するために使われ、

標準エラー出力はログに残すなどしてエラー情報のみを残すことができる。

ちなみにLinuxでは標準出力は`1`、標準エラー出力は`2`で表される。

コマンドのリダイレクトを使って `>` で標準出力、 `2>` で標準エラー出力をファイルに出力することができる。


例えば、`ls`コマンドの標準出力を`out.txt`に、標準エラー出力を`err.txt`に出力する場合は以下のようにする。
```shell
$ ls > out.txt 2> err.txt
```
## 最後に

正常なプリントをする場合であればfmtパッケージを使えばよさそう。
Microsoft Fabric を活用して AWS S3 上のデータを Azure AI Search で検索できるようにする

※本記事の内容は2024年6月時点での情報を元にしています。

# はじめに
マルチクラウドが当たり前の現代において、様々な環境に保管されたデータの取り扱いには工夫が必要です。特に生成AIが流行し様々なアプリケーションとの統合が進んでいる昨今、貴重な情報を持ったデータを利活用することは自社製品に付加価値を与えるために欠かせません。

マルチクラウドにおけるデータの統合にはいくつかの手法があるかと思いますが、本記事では Microsoft Fabric という製品を活用し、AWS S3 にあるデータを Azure 上で構築する RAG アプリケーションに取り込むための基本的な設定手順を紹介します。Microsoft Fabric を活用することでマイクロソフトクラウド以外の環境にあるデータを簡単に統合し、アプリケーション開発に役立てることができます。

# 構成図
手順書通りに作成できると、以下の構成ができあがります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267543/8c3f191a-1c10-199e-c182-3f6d046498a3.png)

# 手順書
Step by Step で設定の手順をまとめて、Speaker Deck で公開しました。AWS が分からない方でも、Azure の経験がない方でも理解いただけるように心がけました。

https://speakerdeck.com/nori_min24/marutikuraudoshi-dai-niokeru-rag-akitekutiyagou-zhu-shou-shun-shu

# さいごに
データの移行は大規模なプロジェクトになりがちだと思いますが、Microsoft Fabric のショートカット機能と Azure AI Search の OneLake インデクサー機能を活用することで、接続情報さえあれば簡単にマルチクラウドのデータを統合し RAG アプリケーションの開発に役立てることができます。是非皆様もご自身の環境でお試しいただき、フィードバック等ございましたらコメントいただけましたら幸いです。 

## 参考文献
・[Fabric に接続するための AWS S3 の設定方法](https://robkerr.ai/creating-an-amazon-s3-shortcut-lakehouse/)
・[マイクロソフトの公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/search/search-how-to-index-onelake-files#supported-document-formats)
よく間違えるコマンドはalias設定で回避する

日々のコマンド操作で、コマンドを間違えて入力してしまうことは誰にでもあるかと思います。

コマンドの打ち間違いはalias設定を使って予防する事が出来ます。


## aliasとは？

そもそもaliasとは何かご存じでしょうか？

aliasとは特定のコマンドをカスタマイズした形で実行できるようにするシェルの機能となっております。

上記機能を使用する事であらかじめ予想されるコマンドミスを正しい形式に紐図ける事でミスした際にただし形式で実行する事が出来ます。

注意点として


## alias設定

それではalias設定方法についてご紹介させていただきます。

以下の様に実行する事でalias設定を行う事が出来ます。

```
alias カスタム名='実行したいコマンド'
```

今回は例としてlsコマンドをaliastest名で登録してみます。

下記をターミナルで実行します。

```
alias aliastest='ls'
```

上記実行後、登録したカスタム名をコマンドとして使用してみます。

```
#コマンド
aliastest

#結果
test.sh
```

オプション等も問題なく利用可能です。

```
#コマンド
aliastest -al

#結果
-rw-r--r--  1 test test  0 Jun 18 18:53 test.sh
```

## alias設定の永続化

先程のaliasコマンドでの設定は一時的な設定となっており、シェルを再起動すると消えてしまいます。

Bashでは.bashrcファイルに設定を追記する事で永続化を行う事が出来ます。

```
vi ~/.bashrc

## 追記内容
alias aliastest='ls'
```

上記の様に.bashrcファイルに設定を行う事でシェルを再起動した場合でもalias設定を読み込む事が出来ます。

※.bash_profileにて.bashrcを読み込む設定が有効化されている必要がります。

## 打ち間違いが多いコマンドを登録する

以上の手順を踏まえる事で打ち間違いが多いコマンドをalias登録する事が出来ます。

今回は例として「ls」コマンドでの打ち間違いが多い「sl」を登録します。

### aliasの登録

```
alias sl='ls'
```

### .bashrcの登録
```
vi ~/.bashrc

## 追記内容
alias sl='ls'
```

以上で「sl」を使用した際も「ls」コマンドを使用できるようになります。
折り紙で36度を作る

五角形を作図するために、36度か72度の作り方を調べた結果です。

「折る」のみは定規で線を引く、「長さを写し取る」はコンパスに相当します。

近似の折り方の角度を掲載しました。

# 37.374°
![アセット 4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/1c263aff-3e56-767b-68cd-d3efeb989e76.png)




















# 35.78°
折り紙の場合、角度を半分にするには、折るだけですが、イラレに機能がないので、円を書き半角を作っています。
イラレの誤差はなぜだろう。
![アセット 5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/1b9dac49-75fe-1c32-3a71-15bbb2772f0d.png)














# 72°
正確ですが、コンパス（円）使います。
![アセット 6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/39a4f150-19b9-ac82-0b65-612672151209.png)










# 参考サイト

https://www.youtube.com/watch?v=pdef-Zsgkm8

https://ja.wikipedia.org/wiki/%E4%BA%94%E8%A7%92%E5%BD%A2
SQLのWHERE句でNOTを使うときに注意すべきNULLの処理

## 概要

SQL の WHERE 句で NOT を利用してフィルタリングする際には NULL の考慮が必要となるのですが、その考慮をしなかったことでプログラムにバグを発生させてしまったため備忘録として残します。

下記のように 3 件のレコードもつデータを前提として 1 以外のデータを抽出する方法を検討してみます。

| col_1 |
| ----- |
| 1     |
| 2     |
| null  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/77f33e1a-1ed2-cf04-f7cc-6d3ffdbc55e3.png)

1 以外を抽出するために単純に NOT の条件（`NOT(col_1 = "1")`）でフィルタリングしてみます。1 以外のデータであるため 2 と NULL が出力されることを期待したのですが、残念ながら 2 のみ抽出されてしまいました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/231753b5-ce35-3ad2-93e2-852bec74a2e6.png)

NULL も抽出するためには下記のように明示する必要があります。否定の条件で記述することでいわゆる MECE になったと勘違いしてしまうのですが、 NULL があることも忘れないようにしようねという話でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4ab686eb-293b-54c1-f903-55f1d61f8b25.png)

ご興味がある方は、Databricks での実行コードを共有しますので、自分が利用しているデータベースで検証してみてください。

## 検証手順

### 1 以外の条件でフィルタリング

```sql
WITH test_data AS (
  SELECT '1' AS col_1
  UNION ALL
  SELECT '2' AS col_1
  UNION ALL
  SELECT NULL AS col_1
)
SELECT
  *
  FROM
    test_data
  WHERE 
    NOT(col_1 = "1")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/30481f8f-7d35-f395-1854-aae36d495c32.png)

### NULL も考慮した上で 1 以外の条件でフィルタリング

```sql
WITH test_data AS (
  SELECT '1' AS col_1
  UNION ALL
  SELECT '2' AS col_1
  UNION ALL
  SELECT NULL AS col_1
)
SELECT
  *
  FROM
    test_data
  WHERE 
    NOT(col_1 = "1") OR col_1 IS NULL
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/d228b978-a29d-06c8-4bc4-779d7ba2d761.png)

### 比較演算子により NULL も考慮した上で 1 以外の条件でフィルタリング

下記についてはデータベースによっては対応していない可能性があります。

```sql
%sql
WITH test_data AS (
  SELECT '1' AS col_1
  UNION ALL
  SELECT '2' AS col_1
  UNION ALL
  SELECT NULL AS col_1
)
SELECT
  *
  FROM
    test_data
  WHERE 
    NOT(col_1 <=> "1")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/60251e39-4b94-4c3f-e34d-9adf331ca2fc.png)
React Hook Formの実装

## はじめに
表題通り、ReactHookFormを実装していきます。

## 成果物

![ReactHookForm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/a007703e-603e-2663-941e-63677b1c0de8.gif)

https://github.com/kouji0705/react_hook_form_sample

## ソースコード
```tsx:MyForm.tsx
import React from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';
import { TextField, Button } from '@mui/material';

type Inputs = {
  name: string;
  email: string;
};

const MyForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm<Inputs>();

  const onSubmit: SubmitHandler<Inputs> = data => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <TextField
        {...register("name", { required: "名前は必須です。" })}
        label="名前"
        variant="outlined"
        margin="normal"
        fullWidth
        error={!!errors.name}
        helperText={errors.name ? errors.name.message : ""}
      />
      <TextField
        {...register("email", { 
          required: "メールアドレスは必須です。",
          pattern: {
            value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,
            message: "有効なメールアドレスを入力してください。"
          }
        })}
        label="メールアドレス"
        variant="outlined"
        margin="normal"
        fullWidth
        error={!!errors.email}
        helperText={errors.email ? errors.email.message : ""}
      />
      <Button type="submit" variant="contained" color="primary">
        送信
      </Button>
    </form>
  );
};

export default MyForm;

```
SPF, DMARCは何を設定すればいいのかという問題

## おさらい 〜 メールのポリシーについて 〜

SPFは，メールの送信元IPアドレスを検証する仕組みです。
しかし，SPFはEnvelopeFromのみを確認するため，本文のFromヘッダーがなりすましであっても検知できませんでした。
この問題を解決するため，DKIMが提案されました。DKIMは本文の改ざんを検知できますが，Fromヘッダーのなりすましは防げません。
そこで，SPFとDKIMの検証結果に基づいてポリシーを決められるDMARCが導入されました。

DMARCには以下のポリシーが設定できます。
```
p=none: 何もしない
p=quarantine: なりすましメールを隔離
p=reject: なりすましメールを拒否
```

さらに，DMARCではFromヘッダーとEnvelopeFromのアラインメントもチェックできます。
```
adkim=s: DKIMでFromとEnvelopeFromが完全に一致していること
adkim=r: DKIMでFromの一部とEnvelopeFromが一致していること
aspf=r: SPFでEnvelopeFromが有効であること
```

DMARCはあくまで送信ドメインの方針を示すものであり，受信側はその方針に従う義務はありませんので，メール受信時の判定が非常に厳格になっています。

<br><br>
ということを，様々なWebサイトで，もっとくやしく書いてあるのですが...

何を設定すればメールは届くのかって話はあんまり書かれていない...

## hoge.comを例に何をどう指定するのかを書く

### SPFレコードを設定する
まずは，どこから送信することを許可するかを書く。
```
_spf.hoge.com descriptive text "v=spf1 +ip4:xxx.xxx.xxx.xxx/32 +ip4:yyy.yyy.yyy.yyy/32 ~all"
```
Gmailから送信する場合
```
_spf.google.com descriptive text "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"
```
SPFレコードを設定する
```
hoge.com TXT "v=spf1 include:_spf.hoge.com include:_spf.google.com ~all"
```

-allでも良いのですが，SoftFailを採用するのは，レコードにリストが正しくない場合もあるでしょうから，まずは~allにしておくべきだという考えです。

SPFはメールの送信元の認証に役立ちますが，それ自体ではFromヘッダーのなりすましを防ぐことはできません。
DKIMは電子メールが配信経路で改ざんされていないことを確認するため，メールの完全性を提供します。

### DKIMを設定する
GmailやAmazonSESなどを利用している場合はメール送信サービスから作るので割愛。
自前のサーバーであればopendkim入れてopendkim.conf設定する。

```sh
pkg install opendkim # pkgが何かって？FreeBSD使え
opendkim-genkey -D /usr/local/etc/mail/dkim -d hoge.com -s selector1
chown opendkim:mail /usr/local/etc/mail/dkim/*
chmod u=rw,go=r /usr/local/etc/mail/dkim/*
```

/usr/local/etc/mail/opendkim.confと/usr/local/etc/postfix/main.cfにOpenDKIMとのマッピングを追加します。（postfix以外は知らない）

```sh
service opendkim start
service postfix restart
```

DKIMを設定する
```
selector1._domainkey.hoge.com TXT "v=DKIM1; k=rsa; p=[公開鍵]"
```

### DMARCを設定する
DMARCはこれら2つの結果を組み合わせたものとして，送信者ポリシーの遵守を要求します。
DMARCは送信側が自身のポリシーを公開するただの方法であり，受信側がそのポリシーに従わなければならないわけではありません。
多くの大手メールプロバイダーはDMARCを尊重しており，これはドメイン所有者にとって有益な追加の防御措置となります。

```
_dmarc.hoge.com TXT "v=DMARC1; p=none; rua=mailto:dmarc_rua@hoge.com; ruf=mailto:dmarc_ruf@hoge.com"
```

初期段階ではレポートモードにして，DMARC違反メールに対して具体的なアクションを行わず，代わりにDMARC違反レポートを収集を行い，DMARCのプロトコルにどの程度準拠しているのか，どんな種類のメールが違反しているのかを理解して，SPFやDKIMの設定を調整をする。
調整後に隔離か拒否の設定を行う。

<br><br>
おわり
【PHP】explode() と implode() どっちがどっちかわかりづらいよね。

どっちがどっちか曖昧でいつも使ってたので、今回整理しようと思いました。

結構、ごちゃごちゃになっている人多いはず...。

:::note warn
本記事は、`explode()` と `implode()`の解説記事ではありません。
:::

## 訳
### Explode
#### PHP公式
> 文字列を文字列により分割する。
文字列の配列を返します。
#### Google翻訳
> 爆発する

#### GPT4
> "explode" が「爆発する」という意味なので、文字列がいくつかの小さな部分に「分裂する」イメージ。文字列が爆発して小片に「バラバラになる」と覚えると良いでしょう。

#### 感想
結局整理してもしっくりこない。
どうにか落とし所を見つけたいですが、一旦`implode()`行ってみましょう。

### Implode
#### PHP公式
> 配列要素を文字列により連結する。
#### Google翻訳
> 爆裂する

#### GPT4
> "implode" が「内部崩壊」という意味なので、複数の小片が内部に引き寄せられて一つに「集まる」イメージです。配列の要素が一つに「まとまる」と覚えると分かりやすいです。

#### 感想
まじで理解できないですね。
まだ、内部崩壊って表現はわかりやすいけど...。

## 落とし所
### Implode
配列が文字列に変換する様子を「内部崩壊」って表現しているんでしょうか？
### Explode
文字列としてまとまってたものが、バラバラに配列内に飛んでいく様子を「爆発する」と表現しているんでしょうか？

## まとめ
調べたことで余計に理解に苦しみました。
もっといい関数名あったんじゃないんでしょうか？

なにはともあれ、調べたことでなんとなくはイメージできました。もう迷わない。

「爆発する」と「爆裂する」ってどう違うんですか？
whichコマンドで実行ファイルの場所を確認する方法 

LinuxのCLIではコマンドを使用し操作を行う事が出来ます。

普段から使用しているコマンドですが実行ファイルはどこに存在するか？と思ったことはありませんか。

今回はコマンド実行ファイルの場所を確認する方法についてご紹介致します。

##基本的な使い方

whichコマンドを使用するとシェルのパス($PATH)に基づいた実行ファイルの場所を確認する事が出来ます。

下記はwhichコマンドの基本的な使用方法となります。

```
which コマンド名
```

今回は例としてechoコマンドの実行ファイルの場所を確認してみます。

```
#コマンド
which echo

#結果
/bin/echo
```

上記では「/bin」配下にechoコマンドの実行ファイルがある事を示しています。


## 複数のコマンドを確認する

whichコマンドでは複数のコマンドを一括で確認する事が出来ます。

下記のように半角スペースにてコマンド名を区切り検索する事が出来ます。

``` 
which コマンド1 コマンド2 コマンド3
```

今回は例として「echo」「cd」「dig」「grep」の実行ファイルの場所を一括で確認してみます。
```
#コマンド
which echo cd dig grep 

#結果
/bin/echo
/bin/cd
/bin/dig
```

上記では「/bin」配下に検索したコマンドの実行ファイルがある事を示しています。

是非活用してみてください!
1人日で出来るWebパフォーマンス改善入門

## はじめに

こんにちは、株式会社HRBrainでデザインエンジニアをしている @Mozu1206 です。
Webパフォーマンス改善。なんとも面倒くさそうな響きですよね。

「やり方もよく分からないし、一旦置いておこう…」

と思ってるそこのあなた！

この記事では、そんなあなたに向けた**サクッと1人日で出来る、効果が期待できるパフォーマンス改善策**をいくつか紹介します！
具体的な方法をステップごとにご説明しますので、ぜひ参考にしてみてください！


## 1人日で出来るWebパフォーマンス改善

### 1. 画像を圧縮する

画像ファイルは、Webサイトの読み込み時間を大きく左右する要素の一つです。画像を圧縮することで、ファイルサイズを小さくし、読み込み時間を短縮できます。

- **ImageMagickなどのツールで圧縮する:**
[ImageMagick](https://imagemagick.org/script/download.php)などのツールを使って画像を圧縮することで、品質を保ちながらファイルサイズを削減できます。
ImageMagickでは、次のコマンドでJPEG画像を圧縮できます。

```sh
convert input.jpg -quality 70 output.jpg
```

### 2. 適切な画像フォーマットの画像を出し分ける

画像フォーマットの選択も重要です。新しいフォーマットである[AVIF](https://web.dev/learn/images/avif)や[WebP](https://web.dev/learn/images/webp)を使用することで、従来のJPEGやPNGに比べて大幅にファイルサイズを減らすことができます。HTMLの`<picture>`タグを使用して、ブラウザが最適なフォーマットを選択するように設定できます。

```html
<picture>
  <source srcset="image.avif" type="image/avif" />
  <source srcset="image.webp" type="image/webp" />
  <img src="image.jpg" alt="example image" />
</picture>
```
::: note
画像のAVIFとWebP変換も、先程のImageMagickで可能です。
```sh
// AVIFに変換
convert a.png output.avif 

// WebPに変換
convert a.png output.webp
```
:::


### 3. CLS（Cumulative Layout Shift）を改善する

[CLS](https://web.dev/articles/cls)は、ページのレイアウトが読み込み中に変化することで発生する、ユーザー体験を損なう問題です。CLSを改善することで、ユーザーがページをスムーズに閲覧できるようになります。

- **`<img>`タグに`width`と`height`属性を指定する:** 画像の表示領域を確保するために、`<img>`タグに適切な`width`と`height`属性を追加しましょう

```html
<img src="image.jpg" width="600" height="400" alt="example image" />
```

::: note
`<img>`タグに設定する`width`と`height`は、画像の実サイズを入れればOKです。ブラウザが必要なのは、表示画像の比率だからです。
つまり、上記例は下記に書き換えても成り立ちます。
CSSで適切な`width`と`height`を当てるのは忘れないでください。
```html
<img src="image.jpg" width="3" height="2" alt="example image" />
```
:::

### 4. 遅延読み込み

[遅延読み込み](https://developer.mozilla.org/ja/docs/Web/Performance/Lazy_loading#%E7%94%BB%E5%83%8F%E3%81%A8_iframe)は、ページの読み込み時間を短縮するために、必要なコンテンツのみを最初に読み込み、残りのコンテンツはユーザーがスクロールしたときに読み込む手法です。
ページ上部など、初期描画に必要ない要素は遅延読込させましょう。

- **`<img>`タグの`loading="lazy"`属性:** 画像を遅延読み込みします
- **`<iframe>`タグの`loading="lazy"`属性:** iframeを遅延読み込みします

```html
<img src="image.jpg" loading="lazy" alt="example image" />
```

```html
<iframe src="https://www.example.com" loading="lazy"></iframe>
```

## まとめ

この記事では、1人日で出来るWebパフォーマンス改善策をいくつか紹介しました。これらの改善策を実践することで、ユーザー体験を向上させ、SEOの改善、コンバージョン率の向上など、様々なメリットが期待できます。

ぜひ、この記事を参考に、あなたのWebサイトのパフォーマンス改善に取り組んでみてください！

## おまけ
この記事で紹介したパフォーマンス改善は、[Astro](https://astro.build/)という静的サイトに特化したフレームワークを使うことでより簡単に行うことができます。
試しにAstroでデモサイトを作ってみたら、パフォーマンス指標によく使われる[PageSpeed Insights](https://pagespeed.web.dev/)で100点を取れました :tada: 

興味のある方は、[デモサイト](https://mozu1206.github.io/web-performance-improvement-demo/)と[リポジトリ](https://github.com/Mozu1206/web-performance-improvement-demo)をご覧ください。


![スクリーンショット 2024-06-23 23.52.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1980374/3043d4d6-504c-84a8-a70f-9be83503b4e7.png)

## PR

株式会社HRBrainでは、一緒に働く仲間を募集しています！
Webパフォーマンス改善に興味がある方、ぜひご応募ください！

https://www.hrbrain.co.jp/recruit
社内でヘルプデスクするときにリモートでやる

## 社内でヘルプデスクするときにリモートでやる

PC操作関連でヘルプデスクに入るとき相手の場所の行くのも~~面倒くさい~~わざわざ行くより時間の節約になるので、リモートで出来たらいい
便利なことにWindowsには標準で「リモートアシスタンス」なる遠隔操作用の機能が備わっている。
だがしかし、ヘルプデスクに来るように人に「リモートアシスタンス」を起動させて招待してもらう操作ができるわけない。
ヘルプデスクのために「リモートアシスタンス」を起動させるためにヘルプデスクに行かなくてはならない。
堂々巡りじゃん。

## 出来るだけ簡単に出来ないか

色々と考えた結果、『「リモートアシスタンス」を起動して「招待」する』自体もリモート操作しちゃえばいいとなった。


### 事前準備
* 相手PCと操作元PCの双方からアクセスできる共有フォルダを用意する。
* 相手PCに対してPowerShellセッションが張れるようにしておく。
* セッションで使うユーザー権限で個々のユーザーフォルダーに書き込み権限を与える。
* もちろんリモートアシスタンスを出来る設定にしておく。


## 相手のデスクトップに接続用ショートカットを作る

起動して招待操作が出来なくても、さすがにデスクトップにあるアイコンで起動するくらいは出来るはず。
ということで、リモートアシスタンスを起動するためのショートカットをデスクトップに作ってしまう。

まずは、相手PCにリモートセッションを繋ぐ。


```powershell

$computername = ""  #接続コンピューター名
Enter-PSSession -Session (New-PSSession -ComputerName $computername)

```

この時に使うアカウントはリモートセッションが出来るのはもちろん「個々のユーザーフォルダーに書き込み権限」があるアカウントを使う。
そしてリモートアシスタンスの接続準備をする

```powershell

$domain = "localdomain" #ドメイン名
$userid = "butyou"      #接続先ユーザーID
$password = "hage"      #リモートアシスタンスの接続パスワード
$msrcIncidentPath = "\\share\remote\remote.msrcIncident" #リモートアシスタンス接続用ファイル(.msrcIncident)の保管場所


$userid = (New-Object System.Security.Principal.NTAccount($domain,$userid)).Translate([System.Security.Principal.SecurityIdentifier]).Value
$userprofile = Get-WmiObject -Class win32_userprofile | Where-Object({$_.SID -eq $userid})
$filePath = $userprofile.LocalPath + "\Desktop\リモートアシスタンス起動.cmd"
$msra = 'echo off'+"`r`n"+'start Msra.exe /saveasfile '+$msrcIncidentPath+' '+$password+"`r`n del "+$filePath
Set-Content -Path $filePath $msra


```

説明
```powershell
$domain = "localdomain"
$userid = "butyou"
```
接続先ユーザーIDとドメイン名、接続用のファイルをユーザーのデスクトップに置くのでユーザーを指定する
```powershell
$password = "hage"
```
リモートアシスタンスの接続パスワード ※相手に見えるのでこんなパスワードにしてはいけない
```powershell
$msrcIncidentPath = "\\share\remote\remote.msrcIncident"
```
リモートアシスタンス接続用ファイル(.msrcIncident)の保管場所、相手先と操作元双方でアクセスできる場所が必要

```powershell
$userid = (New-Object System.Security.Principal.NTAccount($domain,$userid)).Translate([System.Security.Principal.SecurityIdentifier]).Value
$userprofile = Get-WmiObject -Class win32_userprofile | Where-Object({$_.SID -eq $userid})
$filePath = $userprofile.LocalPath + "\Desktop\リモートアシスタンス起動.cmd"
```
ユーザーのデスクトップのパスを取得して「リモートアシスタンス起動.cmd」というファイルを作るためのパスを生成

```powershell
$msra = 'echo off'+"`r`n"+'start Msra.exe /saveasfile '+$msrcIncidentPath+' '+$password+"`r`n del "+\$filePath
```
「リモートアシスタンス」起動用のコマンド、最後に自身を削除する
Msra.exeのオプションで接続用ファイルの保存場所とパスワードを指定しておく
```powershell
Set-Content -Path $filePath $msra
```
リモートアシスタンス起動.cmdにコマンドを書き出す。

## リモートアシスタンスを起動させる

1. リモートアシスタンス起動.cmdを実行してもらう
『デスクトップに「リモートアシスタンス起動」のアイコンで出来ているのでダブルクリックして』とか伝えて起動してもらう。
相手側に「Windows リモートアシスタンス」の画面が開くのでそのまま待ってもらう。
※この時接続用パスワードが見えているので変なパスワードにしてはいけない。

2. リモートアシスタンス接続用ファイル(.msrcIncident)の保管場所にある.msrcIncidentを開く
パスワードは設定したパスワードを入れる

3. 相手側に接続確認が出るので許可してもらう

4. 接続完了
必要に応じで「制御の要求」もする


この方法で、相手側は
1. デスクトップの「リモートアシスタンス起動」を起動させる
2. リモートアシスタンスの許可をする
3. 場合によって制御の許可をする

だけでリモートアシスタンスが可能になる。


## 実際は？

何回か実際実行していますが
* 比較的スムーズにいく
* デスクトップにファイルを置きすぎて新たに出来たファイルが行方不明
* とにかく来てと言われて行くしかない

等、完璧ではないです。
また、リモートセッションの設定とかいろいろ面倒な部分もあったりで、
準備の手間のわりに効果があるのか不明な状態になってたりします。
空のPandas データフレームに対して apply を実行した際に発生するエラーについて

## 概要

Pandas が空の場合に、 apply 実行した際に下記のエラーに遭遇しました。私はデータフレームが空の場合にはスキップすれば要件を満たせたため、その対応方法を共有します。

> ValueError: Expected a 1D array, got an array with shape (0, 2)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/24e51ff5-6f45-13c0-b7c0-4342d757bc3c.png)

## エラーの再現方法と対応方法

### 1. エラーを再現

```python
import pandas as pd

pdf = pd.DataFrame(columns=['col_1'])

tmp_col_name = "temp"
tmp_col_delete_flg = "temp_delete"
encoding = "cp932"
col_names_and_bytes  = {
    "col_1": 10,
}
for col_n, col_b in col_names_and_bytes.items():
    pdf[tmp_col_name] = pdf[col_n].str.encode(encoding).str.len().fillna(0).astype(int)

    pdf[tmp_col_delete_flg] = pdf.apply(
        lambda row: 1 if row[tmp_col_name] > col_b else 0,
        axis=1,
    )
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/8fcc1b51-c1c0-669d-d3ed-0f5f577df7a5.png)

### 2. 対応方法

Pandas データフレームが空の場合にスキップするように修正。

```python
import pandas as pd

pdf = pd.DataFrame(columns=['col_1'])

tmp_col_name = "temp"
tmp_col_delete_flg = "temp_delete"
encoding = "cp932"
col_names_and_bytes  = {
    "col_1": 10,
}
if not pdf.empty:
    for col_n, col_b in col_names_and_bytes.items():
        pdf[tmp_col_name] = pdf[col_n].str.encode(encoding).str.len().fillna(0).astype(int)

        pdf[tmp_col_delete_flg] = pdf.apply(
            lambda row: 1 if row[tmp_col_name] > col_b else 0,
            axis=1,
        )
else:
    print("Pandas データフレームが空であるためスキップ")
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/92c002e1-e1bd-bf83-90fd-36b6a9ba8723.png)
==（等価演算子）と===（厳密等価演算子）の違い

業務でコードレビューを行っている時に、メンバーのソースで意図せず"=="と"==="が混在していたので。
改めて違いについて記事にしてみます。

# 等価演算子（==）と厳密等価演算子（===）の定義
JavaScriptでもTypeScriptでも、等価演算子は非推奨で、厳密等価演算子を用いることが推奨されています。

”=="と”===”は厳密には異なる挙動を示します。
MDNのドキュメントを確認してみましょう。

### 等価演算子
> 等価演算子 (==) は、二つのオペランドが等しいことを検査し、論理値で結果を返します。*厳密等価演算子とは異なり、オペランドの型が異なる場合には型の変換を試みてから比較を行います。*

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Equality

### 厳密等価演算子
> 厳密等価演算子 (===) は、二つのオペランドが等しいことを検査し、論理値で結果を返します。＊等価演算子とは異なり、厳密等価演算子はオペランドの型が異なる場合、常に異なるものと判断します。＊

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Strict_equality

## 実際の挙動を確認してみる
#### 数値型の比較
```
console.log(1 == 1);
// true
console.log(1 === 1);
// true
```
#### 文字列型の比較
```
console.log('hello' == 'hello');
// true
console.log('hello' === 'hello');
// true
```
#### 数値型と文字列型の比較
```
cconsole.log('1' ==  1);
// true
console.log('1' ===  1);
// false
```
#### 数値型と真偽型の比較
```
console.log(0 == false);
// true
console.log(0 === false);
// false
```

＃＃　まとめ
公式で推奨されていた通り、型まで等価であることが確認できる===を使うのが良さそうです。

実務だとコードを書いていく時、「公式に推奨されているから」や「現場の規約で決まっているから」などなぜ、*どういった理由で推奨されているのか*を考えながらコーディングをするということが技術力の底上げになっていくと思います。

少しでも参考になれば幸いです。
闘魂Elixir ーー AtCoder Beginner Contest 337(A)をElixirで楽しむ

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>

<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います}$</font></b>


# はじめに

[AtCoder Beginner Contest 337](https://atcoder.jp/contests/abc337)を[Elixir](https://elixir-lang.org/)で解いてみます。

[AtCoder](https://atcoder.jp/)を解くのが趣味で、休憩時間に解いているという若い人がいて、それってすごい意識の高い休憩時間の過ごし方だと思って、私も真似してみることにしました。


# [AtCoderをElixirでやってみる](https://zenn.dev/torifukukaiou/articles/ac84c87736ceebf4da01)

入力の読み取り方や解答の作り方は、別の記事にまとめています。


https://zenn.dev/torifukukaiou/articles/ac84c87736ceebf4da01

ご参照くださいませ。

[Elixir](https://elixir-lang.org/)で[AtCoder](https://atcoder.jp/)を楽しむためには、エントリポイントを`Main.main/0`にする必要があります。つまり`Main`モジュールを作って、その中に`main/0`関数を定義するわけです。

# [A - Scoreboard](https://atcoder.jp/contests/abc337/tasks/abc337_a)

問題はリンク先をご参照くださいませ。
私の解答を貼っておきます。


<details><summary>私の解答</summary>

_問題文を読んでいることを前提にひとこと解説をしておきます。_

入力値を縦に足します。とりあえず`list_of_lists`に入力値を格納しておいて、[Enum.reduce/3](https://hexdocs.pm/elixir/Enum.html#reduce/3)関数で畳み込みます。どちらのスコアが多いのかは、`if`は使わずに関数パターンマッチで算出することにしました。


```elixir
defmodule Main do
  def main do
    n = IO.read(:line) |> String.trim() |> String.to_integer()
    list_of_lists = for _ <- 1..n do
      IO.read(:line) |> String.trim() |> String.split(" ") |> Enum.map(&String.to_integer/1)
    end
    
    solve(list_of_lists)
    |> IO.puts()
  end
  
  def solve(list_of_lists) do
    %{t: t, a: a} = list_of_lists
    |> Enum.reduce(%{t: 0, a: 0}, fn [x, y], map ->
      map
      |> Map.update(:t, 0, & &1 + x)
      |> Map.update(:a, 0, & &1 + y)
    end)
    
    do_solve(t, a)
  end
  
  def do_solve(t, a) when t == a, do: "Draw"
  def do_solve(t, a) when t > a, do: "Takahashi"
  def do_solve(_t, _a), do: "Aoki"
end

```




</details>




---

# さいごに

[AtCoder Beginner Contest 337](https://atcoder.jp/contests/abc337)を[Elixir](https://elixir-lang.org/)で解くことを楽しみました。

あなたのお好きなプログラミング言語でお楽しみください。

---


**闘魂**とは、  **「己に打ち克つこと。そして闘いを通じて己の魂を磨いていくことである」** との猪木さんの言葉をそのまま胸に刻み込んでいます。
知っているだけで終わらせることなく、実行する、断行する、一歩を踏み出すことを自らの行動で示していきたいとおもいます。
**アントニオ猪木さんのメッセージから元氣をもらったものとして、それを次代に語り継ぎ、自分自身が「闘魂」を体現するものでありたいとおもいます。**

---

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>
<b><font color="red">$\huge{１、２、３ ぁっダァー！}$</font></b>
Excel VBA チェックを入れた項目のみ差し込み印刷

## チェックが入っている項目のみを選択して差し込み印刷するコード

自分メモ

チェックが入っている項目のみを別シートのフォーマットに1部ずつ差し込み印刷します。

家にプリンターがないのでDebug.Print でイミディエイトウインドウに出力して動作テストしました。

```vb
Sub CreateBill()
  
  Dim i As Long
  Dim Seikyu As Worksheet
  Dim List As Worksheet
  Set Seikyu = Sheets("請求書")
  Set List = Sheets("Sheet4") 'リストがあるシート
  
  For i = 2 To 11  'リストの項目は2行目から11行目
  
    If List.Cells(i, 5) = True Then 'チェックが入っていたら
      
      Seikyu.Range("B5") = List.Cells(i, 3) '名前を請求書の宛名欄に転記
      'Seikyu.PrintOut 印刷
      Debug.Print Seikyu.Range("B5") '動作テスト用
      
    End If
  
  Next i
  
End Sub
```
![請求書.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/dd80cdd2-be42-306d-5af7-4217a5f21b7b.png)
【第3回】せや！自宅サーバーと業務用ルーターを導入したろ！

この記事は下記記事の続きです。

## 過去記事はこちらから
### 第1回 業務用ルーター前編
https://qiita.com/ogatomo21/items/1cecb9a1a3595670d84e
### 第2回 業務用ルーター後編
https://qiita.com/ogatomo21/items/63504e1dbc6bd93e64cb

## 自宅サーバー買っちゃった
第1回と同様にノリでAmazonのN100搭載ミニPCを元旦にポチりました。
Rasberry Piでもよかったのですがあまり変わらない上電源ボタンあるしストレージも最初からあるしで楽そうだったので面倒くさがりな私は素直にN100のミニPCを買いました(汗)

https://amzn.asia/d/86yMmLM

## いざ開封
![1718707854090.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518724/12074eef-f61f-b2a9-597b-a9ec60b3d89f.jpeg)


18,000円ほどで購入しました。この緑色結構好みです。

## てかそもそも用途は？
- 実験用
- ファイルサーバー
- TailscaleでVPNぽく
- Linuxの勉強

## 中身
Windows11 Proが入っていましたがサーバーとして使うのでUbuntu Server 24.04をインストールしました。僕の部屋にはモニターがないので事前にSSHでつながるように設定もしておきました。

最終的に下のようなスタイルになりました。
![1718707854088.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518724/f639c617-46ed-1eb6-7e7a-90b86a9e15df.jpeg)

ヤバい人感がでていいですね。これぞプログラマー（？）

## 結局入れたアプリ
- Tailscale (SSHとかトンネリング用)
- samba (ファイル共有)
- minidlna (メディアサーバー)
- Docker (コンテナ)
- Cloudflared (外部公開用)
- Mackerel (サーバー監視用。おすすめ)

## 次回
「本当に必要だったか？」
乞うご期待！
CloudWatch LogsからS3にlogを手動エクスポートする

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

前回、WindowsのEventlogをCloudWatch Agentを使って自動でCloudWatch Logsにアップロードする手順を確認しました。

https://qiita.com/ohtsuka-shota/items/5dcf7118affc9c97f74f

今回はこのCloudWatch LogsにアップロードしているlogをS3に手動で退避する方法をメモって行きたいと思います。

# 環境イメージ
今回の環境は以下となります。
![島田様02-ページ3.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/38395ef9-dd62-172e-d5f7-3ef0ff1d7df2.png)

# 構築
## S3作成
今回はwineventlog-cloudwatchlogsという名前のS3バケットを作成し、取得するEventlogであるApplication,Security,Systemのフォルダを切りました。
S3作成時、名前を設定しただけで他はデフォルトのままで作成しております。
![Untitled (39).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/af503598-e179-b2c8-a7de-f62999d0a472.png)

作成したバケットのバケットポリシーを編集します。
今回は以下のように編集しました。
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws:s3:::wineventlog-cloudwatchlogs"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.ap-northeast-1.amazonaws.com"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::wineventlog-cloudwatchlogs/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

```
![Untitled (40).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/9fa3f405-6af3-f37e-28a2-25da1f13b935.png)


## 手動でS3にlogをエクスポートする
手動でCloudWatch LogsからS3にデータをエクスポートしたいと思います。
CloudWatch Logsの画面からS3にエクスポートしたいロググループを選択して、データをAmazon S3にエクスポートを選択します。
![Untitled (41).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/ea60f119-0438-f113-15c5-cf8e9b5f0935.png)
エクスポートします。
エクスポート先のバケット名と、プレフィクス（フォルダのこと）を入力します。
![Untitled (42).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/9ccb0455-6f6d-8542-0c01-8e50dfb82ef6.png)
エクスポートを開始するとタスクが表示されます。
ステータスがsuccesfullyとなることを確認します。
![Untitled (43).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/068464b7-fcf9-0628-75e1-fb4dd0f9fe41.png)
バケットを確認してみます。少し深いですが圧縮されたログがあります。
これをローカルにダウンロードし解凍することで、CloudWatch Logs上で見ていたlogをローカルで見ることが出来ます。
![Untitled (44).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/c3dd1970-03ff-3ced-2a69-1723fd1b0b2f.png)

近いうちにEventBridgeを使って自動化する。
⇒自動化しました。

https://qiita.com/ohtsuka-shota/items/cb573b69839a2d45e2ca
闘魂Elixir ーー AtCoder Beginner Contest 338(B)をElixirで楽しむ

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>

<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います}$</font></b>


# はじめに

[AtCoder Beginner Contest 338](https://atcoder.jp/contests/abc338)を[Elixir](https://elixir-lang.org/)で解いてみます。

[AtCoder](https://atcoder.jp/)を解くのが趣味で、休憩時間に解いているという若い人がいて、それってすごい意識の高い休憩時間の過ごし方だと思って、私も真似してみることにしました。


# [AtCoderをElixirでやってみる](https://zenn.dev/torifukukaiou/articles/ac84c87736ceebf4da01)

入力の読み取り方や解答の作り方は、別の記事にまとめています。


https://zenn.dev/torifukukaiou/articles/ac84c87736ceebf4da01

ご参照くださいませ。

[Elixir](https://elixir-lang.org/)で[AtCoder](https://atcoder.jp/)を楽しむためには、エントリポイントを`Main.main/0`にする必要があります。つまり`Main`モジュールを作って、その中に`main/0`関数を定義するわけです。

# [B - Frequency](https://atcoder.jp/contests/abc338/tasks/abc338_b)

問題はリンク先をご参照くださいませ。
私の解答を貼っておきます。


<details><summary>私の解答</summary>

_問題文を読んでいることを前提にひとこと解説をしておきます。_

出現頻度の多い順ですので、解法の骨格には[Enum.frequencies/1](https://hexdocs.pm/elixir/Enum.html#frequencies/1)をすえます。
並び替えは出現順が多い順かつアルファベットの並びの早いもの順なので`{出現頻度, 文字のASCIIコードに-1を乗じたもの}`を[Enum.sort_by/3](https://hexdocs.pm/elixir/Enum.html#sort_by/3)関数の引数である無名関数に指定するとうまく問題文にあうように並び替えができます。


```elixir
defmodule Main do
  def main do
    s = IO.read(:line) |> String.trim()
    
    solve(s)
    |> IO.puts()
  end
  
  def solve(s) do
    s
    |> String.codepoints()
    |> Enum.frequencies()
    |> Enum.sort_by(fn {c, cnt} -> 
      {cnt, c |> String.to_charlist() |> Enum.at(0) |> Kernel.*(-1)} 
    end, :desc)
    |> Enum.at(0)
    |> elem(0)
  end
end

```




</details>




---

# さいごに

[AtCoder Beginner Contest 338](https://atcoder.jp/contests/abc338)を[Elixir](https://elixir-lang.org/)で解くことを楽しみました。

あなたのお好きなプログラミング言語でお楽しみください。

---


**闘魂**とは、  **「己に打ち克つこと。そして闘いを通じて己の魂を磨いていくことである」** との猪木さんの言葉をそのまま胸に刻み込んでいます。
知っているだけで終わらせることなく、実行する、断行する、一歩を踏み出すことを自らの行動で示していきたいとおもいます。
**アントニオ猪木さんのメッセージから元氣をもらったものとして、それを次代に語り継ぎ、自分自身が「闘魂」を体現するものでありたいとおもいます。**

---

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>
<b><font color="red">$\huge{１、２、３ ぁっダァー！}$</font></b>
Openpyxl: 罫線を引く

罫線を引くプログラムです。

## プログラム

```py:set_borders.py
#! /usr/bin/python
# ---------------------------------------------------------------
#	set_borders.py
#
#						Jun/18/2024
#
# ---------------------------------------------------------------
import sys
import openpyxl as xl
from openpyxl.styles.borders import Border, Side

# ---------------------------------------------------------------
xlsx_in=sys.argv[1]
xlsx_out=sys.argv[2]

bk = xl.load_workbook(xlsx_in)
ws = bk.worksheets[0]
print("ws.max_row = %d" % ws.max_row)

no_borders = xl.styles.borders.Border(top = None,bottom = None,
	left = None, right = None,)

it = 1
side_thick = Side(style='thick', color='000000')
side_thin = Side(style='thin', color='000000')
border_left_thick = Border(left=side_thick)
border_both_thick = Border(left=side_thick,right=side_thick)
border_both_thin = Border(left=side_thin,right=side_thin)
border_bottom_thick = Border(bottom=side_thick)
border_bottom_left_thick = Border(bottom=side_thick,left=side_thick)
border_bottom_both_thin = Border(bottom=side_thick,left=side_thin,right=side_thin)
border_bottom_both_thick = Border(bottom=side_thick,left=side_thick,right=side_thick)
for row in ws.rows:
	if 5 < it:
		for cel in row:
			cel.border = border_both_thin
		ws.cell(it,4).border = border_both_thin
		ws.cell(it,6).border = border_left_thick
		ws.cell(it,9).border = border_left_thick
		ws.cell(it,14).border = border_both_thick
	if it == ws.max_row:
		for cel in row:
			cel.border = border_bottom_thick
		ws.cell(it,2).border = border_bottom_both_thin
		ws.cell(it,4).border = border_bottom_both_thin
		ws.cell(it,7).border = border_bottom_both_thin
		ws.cell(it,10).border = border_bottom_both_thin
		ws.cell(it,12).border = border_bottom_both_thin
		ws.cell(it,6).border = border_bottom_left_thick
		ws.cell(it,9).border = border_bottom_left_thick
		ws.cell(it,14).border = border_bottom_both_thick
	it += 1

bk.save(xlsx_out)
# ---------------------------------------------------------------
```

実行方法

```bash
./set_borders.py in01.xlsx out01.xlsx
```
mkdirコマンドで親ディレクトリを同時に作成する方法

LinuxでCLIを使用している場合、mkdirコマンドを使用してディレクトリを作成しているかと思います。

しかしmkdirコマンドのデフォルトでは指定した親ディレクトリが存在しない場合はエラーとなってしまいます。

そこで今回はmkdirコマンドで親ディレクトリを同時に作成する方法についてご紹介いたします。


## 親ディレクトリが無い場合

mkdirコマンドはディレクトリを作成するコマンドとなります。

下記を実行する事で指定したパスに新たにディレクトリを作成する事が出来ます。

今回は例として「/home/test」配下にappディレクトリを作成します。

下記コマンドの例です。

```
#コマンド
mkdir /home/test/app

#結果
mkdir: cannot create directory ‘/home/test/app’: No such file or directory
```

親ディレクトリが存在しない場合は上記の様に「No such file or directory」となります。

今回は「/home」配下に「test」ディレクトリが存在しない為、エラーとなりました。


では「/home」配下に「test」ディレクトリを作成し再度実施してみます。

```
#コマンド
mkdir /home/test
mkdir /home/test/app

#結果
成功
```

上記の様に親ディレクトリを作成し再度実行すると問題なくディレクトリを作成する事が出来ました。

## オプションを使用し一括で作成する

先程の親ディレクトリを作成し子ディレクトリを作成する方法はスマートではございません。

またディレクトリ階層数が多い場合、1階層ずつ作成するのは現実的ではございません。

mkdirコマンドでは親ディレクトリが存在しない場合、一括で作成してくれる便利なオプションがあります。

下記の様に「-p」オプションを使用する事で一括で作成する事が可能となっております。

```
#コマンド
mkdir -p /home/test/app
```

是非活用してみてください!!
ナビゲーション実装

```tsx 
// BottomNavigationComponent.js
import React, { useEffect, useState } from 'react';
import { BottomNavigation, BottomNavigationAction } from '@mui/material';
import { useLocation, useNavigate } from 'react-router-dom';
import HomeIcon from '@mui/icons-material/Home';
import SearchIcon from '@mui/icons-material/Search';
import AccountCircleIcon from '@mui/icons-material/AccountCircle';

const BottomNavigationComponent = () => {
  const location = useLocation();
  const navigate = useNavigate();
  const [value, setValue] = useState(0);

  // パスと対応するインデックスをマッピングする関数
  const pathToIndex = {
    '/': 0,
    '/search': 1,
    '/profile': 2,
  };

  useEffect(() => {
    // 現在のパスを基にBottom Navigationのインデックスを設定
    const currentPath = location.pathname;
    if (pathToIndex.hasOwnProperty(currentPath)) {
      setValue(pathToIndex[currentPath]);
    }
  }, [location.pathname]); // location.pathnameが変わる度に実行される

  const handleChange = (event, newValue) => {
    setValue(newValue);
    // インデックスに対応するパスにナビゲート
    const indexToPath = Object.keys(pathToIndex).find(
      (key) => pathToIndex[key] === newValue
    );
    if (indexToPath) {
      navigate(indexToPath);
    }
  };

  return (
    <BottomNavigation value={value} onChange={handleChange}>
      <BottomNavigationAction label="Home" icon={<HomeIcon />} />
      <BottomNavigationAction label="Search" icon={<SearchIcon />} />
      <BottomNavigationAction label="Profile" icon={<AccountCircleIcon />} />
    </BottomNavigation>
  );
};

export default BottomNavigationComponent;
```
[アルゴリズム] ランキングづけの問題

## モチベーション
ランキングづけのアルゴリズムの問題で少し悩んだので，少し考察してみました．
なお，単純化のため，個別のパターンの計算量最適化は考慮していません．
あくまで，ランキングづけ問題の複数のパターンを一般化して整理してみよう！というモチベーションです．

## 発想
すべての問題について，各参加者のエントリを定義することで，考えやすくなります．
エントリ内に必要な情報を，
+ 入力される情報
+ 参加者のランキング
+ 出力時に必要な情報

の３つに分けて考えると良いかなと思います．

## 問題例
以下の各パターンでは，参加者のテストの点数が与えられるので，それをランクづけした上で，所定の形式で出力することが求められます．

### 例１
入力:参加者の名前とスコアの組が，順番に与えられる
出力:参加者の名前とそのランキングを，入力の順に出力する

```
# 入力　
shinji 60
rei 100
asuka 95
mari 80

# 出力
shinji 4
rei 1
asuka 2
mari 3
```

### 例2
入力:参加者の名前とスコアの組が，順番に与えられる
出力:参加者の名前を，ランキング順に出力する


```
# 入力　
shinji 60
rei 100
asuka 95
mari 80

# 出力
rei
asuka
mari
shinji
```

### 例3
入力:参加者のスコアが順番に与えられる
出力:ランキングを，入力の順に出力する

```
# 入力
60
100
95
80

# 出力
4
1
2
3
```


## 解答例

### 例1
入力:参加者の名前とスコアの組が，順番に与えられる
出力:参加者の名前とそのランキングを，入力の順に出力する

従って，エントリ内に必要な情報は
+ 名前，テストの点数(=入力される情報)
+ 参加者のランキング
+ 入力時の順番(=出力時に必要な情報)

の３つです
したがって，各参加者のエントリは以下のように定義すると良いはずです．

```python
# 入力直後
children=[
    {'name':'shinji','score':60,'rank':None,'id':0,},
    {'name':'rei','score':100,'rank':None,'id':1,}
    {'name':'asuka','score':95,'rank':None,'id':2,}
    {'name':'mari','score':80,'rank':None,'id':3,}
]

# ランクづけの処理を行う

# ランキングづけ後
children=[
    {'name':'shinji','score':60,'rank':4,'id':0,},
    {'name':'rei','score':100,'rank':1,'id':1,}
    {'name':'asuka','score':95,'rank':2,'id':2,}
    {'name':'mari','score':80,'rank':3,'id':3,}
]
```

入力が与えられた時点ではrankはNoneになっていますが，入力が全て与えられた後，各参加者のrankを計算する処理を実行すれば良いでしょう．
例１で重要なのは，出力時に，入力時の順番を覚えておく必要がある点です．
そのために，上記の配列childrenでは，各エントリに入力順(id)を定義しています

### 例２
入力:参加者の名前とスコアの組が，順番に与えられる
出力:参加者の名前を，ランキング順に出力する

従って，エントリ内に必要な情報は
+ 名前，テストの点数(=入力される情報)
+ 参加者のランキング

例１と基本的には同じですが，例２の場合は，出力時はランキング順に出力するので，入力時の順番は不要です．したがって，各参加者のエントリは以下のようになります．


```python
# 入力直後
children=[
    {'name':'shinji','score':60,'rank':None},
    {'name':'rei','score':100,'rank':None}
    {'name':'asuka','score':95,'rank':None}
    {'name':'mari','score':80,'rank':None}
]

# ランクづけの処理を行う

# ランキングづけ後
children=[
    {'name':'shinji','score':60,'rank':4},
    {'name':'rei','score':100,'rank':1}
    {'name':'asuka','score':95,'rank':2}
    {'name':'mari','score':80,'rank':3}
]
```

### 例3
入力:参加者のスコアが順番に与えられる
出力:ランキングを，入力の順に出力する

従って，エントリ内に必要な情報は
+ テストの点数(=入力される情報)
+ 参加者のランキング
+ 入力時の順番(=出力時に必要な情報)

です．
例１，２と違うのは，参加者の名前が不要ということです，
（名前のような）参加者固有の情報が与えられないので，「点数に対してランクづけする」という風に解釈してしまうと，頭がこんがらがります．
ですから，入力時の順番を参加者固有のidとして各エントリ内に保持して，出力時はidでソートすれば良いでしょう．

```python
# 入力直後
children=[
    {'score':60,'rank':None,'id':0,},
    {'score':100,'rank':None,'id':1,}
    {'score':95,'rank':None,'id':2,}
    {'score':80,'rank':None,'id':3,}
]

# ランクづけの処理を行う

# ランキングづけ後
children=[
    {'score':60,'rank':4,'id':0,},
    {'score':100,'rank':1,'id':1,}
    {'score':95,'rank':2,'id':2,}
    {'score':80,'rank':3,'id':3,}
]
```

## まとめ
__入出力時に必要な情報をまとめて，エントリとして定義する__　ことが重要．
動画生成AI「Luma Dream Machine」で「役に立つ」を忘れて「原初の楽しさ」を思い出した

## はじめに

◆この記事は何？
動画生成AI「Luma Dream Machine」の紹介をして、試してみて感じたことを率直に共有します。

◆対象は？
エンジニア

## 先に結論
- 「Luma Dream Machine」は無料で利用できる動画生成サービス
- 触ってみると、「役に立つ」よりも先に「面白い！」という感情が湧いた
- 好奇心はすぐに役に立つとは限らないが、長期的には役に立つことがある

## Luma Dream Machine

Luma Dream Machineは、無料の動画生成サービスです。

テキストや画像からリアルな動画を生成できます。

https://lumalabs.ai/dream-machine

SNSでは、アイコン画像を動画にする人たちが現れました。

https://x.com/mkt_hanada/status/1803008632512286815

Luma Dream Machineでプロンプトをうまく扱うためのガイドラインはこちら。

https://lumaai.notion.site/FAQ-and-Prompt-Guide-Luma-Dream-Machine-f7bd5f77478c4994aa692aa58b063ef3


## 最初に思ったこと

「役に立つ」　よりも「面白い」という感情が先に来ました。

データサイエンティストとして働いているので、プロとして「どうやって価値を生み出すか？」を考えるべきですが、技術の面白さを先に実感しました。

YOLOに技術的な好奇心を抱いて、データサイエンスの世界に踏み込んだことを思い出しました。

技術的な面白さはキャリアのスタート地点になりえます。

## 好奇心は成長の指標になる

技術を面白がる好奇心は、すぐに役に立たないこともありますが、長期的には成長の指標になります。

例えば、Luma Dream Machineを使いながら、その技術を学ぶことで、徐々に生成AIについての理解が深まり、最終的には自分自身で開発できるようになります。

好奇心そのものはすぐに価値を生み出さないこともありますが、長期的には大きな価値を持つことがあります。技術的な好奇心を忘れずに、新しい技術を積極的に触れていこうと思います。

## おわりに

この記事では、Luma Dream Machineを紹介して、私がそれを使用して感じたことを共有しました。

Luma Dream Machineで感じたことを共有いただけると幸いです。

それでは。

## 参考

https://arxiv.org/abs/1506.02640
備忘録　next.js


## 諸注意　
これは自分用の備忘録です。
参考にはならないかもです。
## 環境
node v21.7.3
yarn 4.3.0
windows 11
## next.jsを導入
nodo.js・git・vscodeをダウンロード

その後、vs codeで、コマンドラインを開き、以下を入力

```
yarn create-next-app
```

その後、git hubを開き、新しくリモートリポジトリーを作成し、pushするだけ…

で終わりだと思っていました…

## 躓き **yarn が重すぎて**pushできない問題
リモートリポジトリーにプッシュしてみますが、以下のようなエラーが出現。
![スクリーンショット 2024-06-17 200037.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661945/dbad89c4-8006-a57f-664d-305228359eb0.png)

**?????**

このgitのリモートリポジトリーは空っぽですが？！

コマンド出力を確認してみると以下のようなエラーが…

```
~省略~
remote: error: File .yarn/unplugged/@next-swc-win32-x64-msvc-npm-14.2.4-619f8a900f/node_modules/@next/swc-win32-x64-msvc/next-swc.win32-x64-msvc.node is 129.50 MB; this exceeds GitHub's file size limit of 100.00 MB
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
~省略~
```
こんな感じのエラーが出現

chatgpt君に訪ねてみると、100M以上のファイルはpushできないよとのこと

解決方法を聞くと、git lsfとやらをインストールせよらしい。

しかしコミット後だったため、ファイルを削除してやり直す。

```
npx create-next-app
```
でyarnじゃない next.jsのappを作成。そのままpush!

lsfとやらをインストールし、重そうな原因のファイルをトレース
```
git lfs install
git lfs track "*.node"
git lfs track "*.py"
```
**ここで先に変更された**`.gitattributes`**をpush！**
じゃないと、トレースしてくれなくて、同じエラーが吐かれる(N敗)

```
yarn
```

でyanr環境にして、新しく追加された、yarn関係のファイルをpush!

これでyanr環境を実装できる。

### 感想
前まで普通に、pushできてたと思うんだけど何でこんな面倒な方法を踏まないといけないのか分からないといけなくなっちゃったの?

もっといい方法があったもしくはあれば、この記事のコメントとかで教えていただけると幸いです。
メモ

#4
@app.route('/EmployeeSystem/<int:id>', methods=['PUT'])
def EmployeeSystemPUT(id):
    json=request.get_json()
    name=json.get('name')
    data={'id':id, 'name':name}
    dbname='employee_info.db'
    conn=sqlite3.connect(dbname)
    cur=conn.cursor()
    if type(name) is int:
        return jsonify({'message': "データの形式が違います"}),400
    elif not name:
        return jsonify({'message': "データが入力されていません"}),400
    else:
            cur.execute('update info set name=? where id=?',[name,id])
            change=cur.rowcount;
            try:
                if change>0:
                    conn.commit()
                    conn.close()
                    return jsonify(data),200
                elif change==0:
                    conn.close()
                    raise IDerror("その社員IDは存在しません")
             except IDerror as e:
                  data={'message':e}
                  return jsonify(data),404
【ServiceNow】Washington DCリリース「Employee Center　Rich Content Editor」

Washington DCへのバージョンアップで新規追加された機能について紹介します。
Employee CenterのRich Content Editorに追加された機能をまとめました。

https://docs.servicenow.com/bundle/washingtondc-employee-service-management/page/product/employee-center/concept/ec-rich-content.html

# Rich Content Editor の概要
Rich Content Editor は Employee Center Pro トピックとポータルページ用のコンテンツ（ニュース記事、お知らせ、バナーなど）を作成できるツールです。
わかりやすい UI となっているため、技術的な知識がなくとも構築をすることができます。

# Washington DCでの新機能
・HTML と CSS のソースコードを編集する
・1回のクリックでキャンバス全体のコンテンツを消去します
・セルの境界線の色、幅、スタイルを設定する
・画像とビデオの水平方向の配置を設定する


# HTML と CSS のソースコードを編集する
コンテンツのデザインタブで「エディターを開く」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/db272451-c418-db35-d4a2-8a26b2998ec1.png)

ソースコードのマークをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/aef11265-8bb5-3a43-73f9-a7d1d8a9b1ba.png)

ソースコードを更新して「適用」をクリックすることで更新内容を適用できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/85bd0c33-34ea-4072-ad94-9e961977ca86.png)


# 1回のクリックでキャンバス全体のコンテンツを消去します
ゴミ箱アイコンをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/e5ddd240-60f9-e1d4-0e6c-771b7992cc46.png)

「キャンバスをクリア」をクリックするとコンテンツが消去される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/81c180a2-dbfd-2f19-fc6d-8537d3da2884.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/3b285ca5-e19e-8971-6572-62e68fe62f6e.png)


# セルの境界線の色、幅、スタイルを設定する
セルを選択した状態でAdvanced stylingの境界にて、線の色、幅、スタイルを設定することができる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/e2aacf3c-8a69-d0f1-49b8-1f9378e2b467.png)


# 画像とビデオの水平方向の配置を設定する
画像またはビデオを選択した状態でスタイリングのHorizontal alignにて、左寄せ、中央寄せ、右寄せを設定することができる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599975/25eb4dd4-3ed4-48b9-c734-fa8b4acb3f58.png)

# おわりに
小ネタになりましたが、Rich Content Editorに追加された新機能を紹介しました。
Employee Center Proで記事を作成する際にご活用ください。
ニコニコ動画で今公開されている動画一覧と震災に備えて避難所一覧を出せるサイトを作った

## 作ろうと思った経緯
去年の11月頃にもったいない市場という[サイト](https://mottainai.shop/)で防災グッズを買ったのですが
当時このサイトを紹介した人たちからは地震なんて滅多に怒らないのだから賞味期限が切れた防災グッズなんて買ったって使う機会がないから無駄だよと笑われました

しかし、今年の新年は能登半島地震や４月は台湾で大型地震が起きました。未曾有府増の大災害はいつ何時起きるか分からないと思う反面、自分は５日分の非常食と簡易トイレキットを賞味期限が切れかかっているとは言え、備えていたのは先見の明があったと思います

思えば、こういった事は多々あり、皆頭の片隅にこういった備えはするべきなのではないかと思います

なのでサイトを作りました
作り始めたのは１月からなので半年掛かりました

収益化するかで悩んでいる時にニコニコ動画がサイバー攻撃されてしまって見れなくなってしまったので、動画一覧表も作成しました。
鯖借りて公開するかどうかは評判とか見ながら考えます
## 構成
フロント側はnuxt
サーバーサイドはexpressで構成しました
純javascriptです

## どんな事が出来るのか
- 自分オリジナルの避難所マップを作成できます
　自分の住んでいる地域や良く行く場所の避難所について知っている人は中々居ないと思います
　このサイトはもし被災した際に徒歩で移動することが出来る範囲２０キロ以内の避難所を表示することが出来ます
　また、避難する場所も公園とかに避難したら土砂降りの中野宿。。。という事もあり得ます。
  避難したい避難所を３つまで選択し、pdfファイルでスマホに保存することが出来ます
　これならネットに繋がなくても避難所を見ることが出来ます
- 防災のニュースやもったいない市場の商品を見ることが出来ます
　スクレイピング機能を追加したので毎日一回、記事が更新されていたら記事タイトルとURLを紹介します
　また、ランキング機能も付けました。みんなが見る人気ニュースや商品をピックアップできます
- ニコニコ動画で公開されている動画一覧も見れる
　これは自分の為にあります。公開されている動画一覧を見ることが出来ます
　また、日間ランキングと週間ランキングも見れます
　これは、ニコニコ動画で一番好きな機能であるランキングを見れないのがつらいので付けました
　また、公開されている動画のランキングをサーバーが復旧した際に一覧で見ることが出来たら
　楽しいだろうなと考え付けました
## スクリーンショット
ホーム画面
![スクリーンショット 2024-06-18 183510.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/5c9d5100-3ca6-a78c-493b-aead9b5c2805.png)
ニュース記事一覧
![スクリーンショット 2024-06-18 183616.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/ea78db80-f775-551d-0e2e-057f1bc86d77.png)
避難所マップ
![スクリーンショット 2024-06-18 190915.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/3e0e4995-9eca-91b6-a878-2306b267d1e4.png)

PDFで出力する事も可能です
![スクリーンショット 2024-06-18 183745.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/0af1c643-be14-fc8d-d6de-7389d6ec7696.png)

ニコニコ動画一覧
![スクリーンショット 2024-06-18 183542.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/fe338a0d-9935-ff01-03b1-bf797f80a031.png)
日間週間ランキング
![スクリーンショット 2024-06-18 183559.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2879865/0351556f-30e5-2c41-8038-40e5cc9dcaec.png)


出来れば感想をください
よろしくお願いします

追記
ニコニコ動画一覧もう作った人いましたね。。。
行動が遅かったです