Qiita-CLIで作ったmdファイルを整理するpart2


# はじめに

Qiita-CLI を使っていると md ファイルが多くなりすぎて管理ができなくなってしまう．
もちろん，後に編集はできるようにしたいのですべて public フォルダにおいておきたい．（md ファイルが public フォルダにないとプレビューがみれない）

そのため，ファイル名を正規化して，ツリー構造を作成することでどのような記事があるかを一目でわかるようにする．

# 前回からの変更点

https://qiita.com/magix/items/f8d87c3b4f48debb621d

- ファイル名の自動変更
  - 未投稿は初めに\_をつける
  - 限定公開は初めに+をつける
  - 投稿したら初めには何もつけない
- \_\_docs.md に出力する

# ファイル構造

ファイル構造

```
public
│  __docs.md
│  _aws_ec2.md
│  _aws_vpc.md
│  _eng_equLowRangeSystem.md
│  _eng_FMPM.md
│  _eng_whiteNoise.md
│  _github_pages_gas.md
│  _llm_Bard_Bard.md
│  _llm_GithubCopilot_auth.md
│  _llm_GithubCopilot_noAnswer.md
│  _llm_therory_multimodal.md
│  _math_Laplace.md
│  _qiita_error.md
│  _qiita_file2.md
│  _tex_cite.md
│  +eng_AM.md
│  +programming_mock.md
│  llm_chatGPT_IphoneShortcut.md
│  math_Fourie.md
│  qiita_file.md
│  qiita_HowTo.md
│
└─.remote
        11111111111111111111.md
        22222222222222222222.md
        33333333333333333333.md
        44444444444444444444.md
        55555555555555555555.md
        66666666666666666666.md
```

:::note info
.remote フォルダは公開している記事のものなので無視
:::

この場合だと，

- \_aws_ec2.md が未投稿
- +eng_AM.md が限定公開
- qiita_file.md が投稿済み

また，\_\_docs.md にはメモを書いているが，出力してほしい行（最後の行が好ましそう）に`public`と入れる．
`public`と書かれた行の次の行に出力結果を書くようにしている．

# 出力結果

```
public
├─
│   └─ *docs.md---title: memo
├─ aws
│   ├─ *ec2.md---title: About AWS EC2
│   └─ *vpc.md---title: About AWS VPC
├─ eng
│   ├─ *FMPM.md---title: 角度変調（FM・PM）
│   ├─ *equLowRangeSystem.md---title: 等価低域系
│   ├─ *whiteNoise.md---title: 白色雑音
│   └─ +AM.md---title: アナログ振幅変調（AM）
├─ github
│   └─ pages
│       └─ *gas.md---title: memo
├─ llm
│   ├─ Bard
│   │   └─ *Bard.md---title: Bard
│   ├─ GithubCopilot
│   │   ├─ *auth.md---title: github copilot の認証
│   │   └─ *noAnswer.md---title: github copilot answers no answer
│   ├─ chatGPT
│   │   └─ IphoneShortcut.md---title: Use ChatGPT with iPhone Shortcut App
│   └─ therory
│       └─ *multimodal.md---title: マルチモーダルAIについて
├─ math
│   ├─ *Laplace.md---title: ラプラス変換
│   └─ Fourie.md---title: フーリエ変換を信号処理の観点から理解する
├─ programming
│   └─ +mock.md---title: Test with Mock or Stub
├─ qiita
│   ├─ *error.md---title: qiita error
│   ├─ *file2.md---title: Qiita-CLIで作ったmdファイルを整理するpart2
│   ├─ HowTo.md---title: How To Write Blogs with Qiita-CLI
│   └─ file.md---title: Qiita-CLIで作ったmdファイルを整理するpart1
└─ tex
    └─ *cite.md---title: memo

```

### 出力のポイントは以下の通り

- ディレクトリはブログのジャンル

- タイトルも書いてあるとわかりやすいのでタイトルも---に続けて出力

- ファイル名の前に \* があるのは未投稿のファイル

# python コードを書く

結果はこちら

```
import glob
import os
import numpy as np

def read_file_info(file):
    original_file_name = os.path.basename(file)
    if original_file_name.startswith('_') or original_file_name.startswith('+'):
        original_file_name = original_file_name[1:]
    dir_file_name = original_file_name.rsplit('_', 1)[0]
    base_file_name = original_file_name.rsplit('_', 1)[1]

    title = read_file_line(file, 'title:')
    isTitle = title != 'title:\n'

    private = read_file_line(file, 'private:')
    isPrivate = private == 'private: true\n'

    id = read_file_line(file, 'id:')
    isId = id != 'id:\n'

    if not isId:    # 未投稿
        os.rename(file, 'public/' + '_' + original_file_name)
        base_file_name = '*' + base_file_name
    elif isPrivate: # 非公開
        os.rename(file, 'public/' + '+' + original_file_name)
        base_file_name = '+' + base_file_name
    else:           # 公開
        os.rename(file, 'public/' + original_file_name)

    if isTitle:
        base_file_name = base_file_name + '---' + title
    file_name = dir_file_name + '_' + base_file_name
    return file_name.rstrip('\n')

def read_file_line(file, search_string):
    with open(file, 'r') as f:
        lines = f.readlines()
    try:
        index = next(i for i, line in enumerate(lines) if search_string in line)
    except StopIteration:
        index = -1
    return lines[index]

def print_tree(folder_files):
    tree = {}
    for file in folder_files:
        parts = file.split('_')
        node = tree
        for part in parts:
            node = node.setdefault(part, {})
    return tree

def print_tree_structure(tree, indent='', is_last=False):
    nodes = sorted(tree)
    output = ''
    for i, node in enumerate(nodes):
        if i == len(nodes) - 1:
            output += indent + '└─ ' + node + '\n'
            if tree[node]:
                output += print_tree_structure(tree[node], indent + '    ', is_last=True)
        else:
            output += indent + '├─ ' + node + '\n'
            if tree[node]:
                output += print_tree_structure(tree[node], indent + '│   ')
    return output

def write_docs(tree):
    write_file_name = 'public/__docs.md'
    with open(write_file_name, 'r') as f:
        lines = f.readlines()
    index = lines.index('public\n')
    lines[index+1:] = [print_tree_structure(tree)]
    with open(write_file_name, 'w') as f:
        f.writelines(lines)

if __name__ == '__main__':
    md_files = glob.glob('public/*.md')
    folder_files = []
    for file in md_files:
        file_name = read_file_info(file)
        folder_files = np.append(folder_files, file_name)
    tree = print_tree(folder_files)
    write_docs(tree)
    print(print_tree_structure(tree))
```

## 関数の軽い説明

- `read_file_info`でファイル名を変更し，タイトルがあれば取得している
- `print_tree`と`print_tree_structure`でツリー構造を作成
- `write_docs`で\_\_docs.md に出力

自分用に変更したい部分があれば，ツリー構造の部分だけ使ってもらえるといいかもしれない．

## さいごに

ついでに python で公開コマンドを実行するようにもするといいかも
100 個単位でファイルがある人とかはどうしてるんだろう
もはや管理していないのかな？？