ほぼ質問だけでプログラミング 「Chat GPTが書いてくれたおカタヅケ君」 with Python

## はじめに
この散々溜まっているフォルダーのお掃除をChatGPTとPythonに任せた話です。
コードの作成は手を動かすよりChatGPTとのやり取りで書いてので、
「[プロンプトエンジニアリング](https://www.promptingguide.ai/jp )的」な考えも含まれています。

### 作製背景
* 色々ダウンロードして、消すのを忘れてしまう
* いつの間にか一杯で整理もどこかでやればいいのかよくわからない😢
* 好き勝手に遊んで散らかす娘を見ながら「僕と同じやん」これはごく自然なことだ！
* ぐるぐる回る掃除ロボットを見ながら「ええなワシのフォルダーも何とかしてくれんへんかな」
* どうせ、日にちでなんとなくわかるし、最悪検索かければ探せるで～
* 手でするのは気力も時間もない（ん？）
* 何か自作コード書きたいけどエラーでなかなか進まない

的な今まではどうしようもない、みんな同じだよと精神勝利してきたんですよ。
最近流行りのChat GPT先生に踏み込んでもらいました。

## やってみるに必要な事前知識はこれぐらいです。
| ツール | レベル | バージョン |
|:-----------|------------:|:------------:|
| ChatGPT       |    None     | 4.0         |
| Python     | 初心者      | 3.12.1       |
| VsCode       | 少し修正できるぐらい        |    見慣れたもの    |
| プロンプトエンジニアリング知識        | 味見程度          |   None         |
| 努力度       |     ???   |   非常に楽     |
| Window環境|  None    |   Window10     |

### 補足をすると
Pythonの初心者とはこれぐらいです。
 - インストールができて、文字を打って、返って来た文字が読める
 - 出力、変数、条件分岐が分かる
 - データの形を区分できる
     - 整数、文字列、真偽値、リスト、辞書系が分かって簡単な修正ができる
 - Magic（？）が使える ※　人が時間と努力、魂をすりおろして作ったライブラリーつまり、関数のパッケージのこと

VScodeだけではなく、見慣れてコードが見やすいのであれば（Sublime Text、サクラエディタなど）どれでもいいですが、
プログラミングの経験全然がなければ「Jupyter note」というエディターを使うのもおすすめです。
クラウド上、オフライン上で使えるし、1行ずつ実行し、結果が確認できる優れものです。（今度記事に書いてみます）

## プロンプトエンジニアリング知識
と言っても意識することはこれだけです。
質問 ⇒ 実行 ⇒ 確認 ⇒ エラー潰す ⇒ 保存 ⇒ 質問（機能追加） 
という工程をゴールまで繰り返します。例えば
```plantuml
人 ->GPT : 投げる 「～をしてくれるコードを書いて」
人 <-GPT : コードを書いてくれる
人 ->Python : コードの実行
人 <-Python : 結果返す
人 ->GPT : エラーが出る場合 「って出るけどどうすれば解消できる?と聞く」
人 ->GPT : 意図通りであれば 「保存：これ覚えて、ここから機能追加するよ」
```

:::note warn
警告
Chat GPTは人だと考えてください。毎回質問に対する答えは
同じくかまたは違います（笑） なので、実行していい答え（意図したコア機能を実現できそう）をもらったらきちんと「保存」させてください
:::

## 完成コード
文字列を検出して、各フォルダに移動。後どこにも当てはまらないファイルはｔｍｐというフォルダに移動し、30日後には消してくれるスクリプト。WindowのCronで毎日定期的に動かす。

まず、完成版はこれです。初心者で1.5時間～2時間かかりました。
終盤部のパスの設定、tmpファイルのどれぐらい日が経ったら削除するかお決める
パラメター（days_old=30 今だと30日）は各自変更が必要です。

試したいけどフォルダーにゴミのないきれい好きはこの後に出る「ゴミ作成スクリプト」を流してください。
``` py: おカタヅケ君

import os
import shutil
import time
from datetime import datetime, timedelta

def move_old_files_to_tmp(source_folder, tmp_folder, days_old=7):
    if not os.path.exists(tmp_folder):
        os.makedirs(tmp_folder)

    for file_name in os.listdir(source_folder):
        file_path = os.path.join(source_folder, file_name)
        file_mod_time = datetime.fromtimestamp(os.path.getmtime(file_path))
        if datetime.now() - file_mod_time > timedelta(days=days_old):
            shutil.move(file_path, os.path.join(tmp_folder, file_name))
            print(f"Moved to tmp: {file_name}")

def delete_old_files_from_tmp(tmp_folder, days_old=30):
    for file_name in os.listdir(tmp_folder):
        file_path = os.path.join(tmp_folder, file_name)
        file_creation_time = datetime.fromtimestamp(os.path.getctime(file_path))
        if datetime.now() - file_creation_time > timedelta(days=days_old):
            os.remove(file_path)
            print(f"Deleted: {file_name}")

def move_files_with_strings(source_folder, strings_and_folders, tmp_folder):
    move_old_files_to_tmp(source_folder, tmp_folder)

    for search_string, target_folder in strings_and_folders.items():
        if not os.path.exists(target_folder):
            os.makedirs(target_folder)

        for file_name in os.listdir(source_folder):
            if search_string in file_name:
                source_file_path = os.path.join(source_folder, file_name)
                target_file_path = os.path.join(target_folder, file_name)

                shutil.move(source_file_path, target_file_path)
                print(f"Moved: {file_name} to {target_folder}")

    delete_old_files_from_tmp(tmp_folder)

# パスと設定
source_folder = r'<お掃除したいフォルダのパス>'
tmp_folder = r'<どれにも当てはまらないファイルが収納されるパス>'
strings_and_folders = {
    'A社': r'<A社という文字列が入っているファイルの格納したいパス>',
    'B社': r'<B社という文字列が入っているファイルの格納したいパス>',
    'C社': r'<C社という文字列が入っているファイルの格納したいパス>',
    'D社': r'<D社という文字列が入っているファイルの格納したいパス>',
    'E社': r'<E社という文字列が入っているファイルの格納したいパス>'
}
move_files_with_strings(source_folder, strings_and_folders, tmp_folder)
```

## 作成スタート

 ゴミの山（検証用のファイル）をが必要なので、これもついでにGPTちゃんに
 
### 質問
``` 
指定したフォルダーの下にビジネスでよく使うダミーファイル作成するPythonスクリプトを書いて。
名前は会社名「A社」、「B社」、「C社」、「D社」、「E社」とし、数は100個、拡張子はexcel, pdf, word, zipのようなものがいいと思う。
```

### 回答のコード

``` py:ゴミ（検証用のファイル）作成スクリプト
import os
import random
import string

def create_dummy_files(folder_path, num_files=100):
    # フォルダが存在しない場合は作成
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

    # 拡張子のリスト
    extensions = ['xlsx', 'pdf', 'docx', 'zip']

    # 会社名のリスト
    companies = ['A社', 'B社', 'C社', 'D社', 'E社']

    for _ in range(num_files):
        # ランダムな会社名の選択
        company_name = random.choice(companies)
        # ランダムなファイル番号の生成（4桁の数字）
        file_number = ''.join(random.choices(string.digits, k=4))
        # ランダムな拡張子の選択
        ext = random.choice(extensions)
        # 完全なファイルパスの作成
        file_path = os.path.join(folder_path, f'{company_name}_{file_number}.{ext}')

        # ダミーファイルの作成（空ファイル）
        with open(file_path, 'w') as f:
            pass

# ここで指定したフォルダパスにダミーファイルを作成
folder_path = 'path/to/your/folder'  # ここに目的のフォルダパスを入力
create_dummy_files(folder_path)
```
### 実行
言われた通り、'path/to/your/folder' にローカルに事前に作成したフォルダのパスを入れて実行すると以下のようなエラーで叱られる。（例えば C:\Users\xxx\OneDrive\Desktop\python-chatgpt)

```
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
```

### エラーの解決
もちろん、GPTちゃんに聞いてみましょう。


``` 
folder_path にC:\Users\xxx\OneDrive\Desktop\python-chatgptを入力すると
次のエラーがでるけどどうすればコードがうまく動くか教えて
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
```


### 回答
```
このエラーは、Pythonでファイルパスを指定する際に、バックスラッシュ（\）をそのまま使用すると生じます。Pythonでは、バックスラッシュはエスケープ文字として扱われるため、ファイルパスに含まれるバックスラッシュを正しく扱うためにはいくつかの方法があります。

Raw String（生文字列）の使用: 文字列の前に r を付けることで、生文字列として扱い、バックスラッシュをエスケープ文字として解釈させないようにします。

folder_path = r'C:\Users\xxx\OneDrive\Desktop\python-chatgpt'
                        ・・・他の提案は省略・・・
```
:::note info
以外と親切に教えてくれます。 可能性が高いのをいくつか教えてくれるので試してみましょう。説目が不十分なときはもっと詳しく教えてとか、ステップごとに詳細に教えてと聞いてみてください。イメージでみたらわかりやすいものはGoogleなども活用しましょう。
:::

パス入力を以下に修正
```diff_python
# ここで指定したフォルダパスにダミーファイルを作成
- folder_path = 'C:\Users\xxx\OneDrive\Desktop\python-chatgpt'  # ここに目的のフォルダパスを入力
+ folder_path = r'C:\Users\xxx\OneDrive\Desktop\python-chatgpt' # ここに目的のフォルダパスを入力

```
### 結果
ほぉーちゃんと A社～E社までExcel、Pdf、Word、zip拡張子で100個を作ってくれてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/a56c179b-834e-e4c2-9fed-e51041234e8d.png)


## じゃーカタヅケじゃ！

## 必要な機能は
要件1. 任意のフォルダーからある文字列が入っているファイルを指定したフォルダ（<格納するパス-Path1>）に移動する
要件2. ↑をどんどん追加したいA社、B社、C社・・・など
要件3. どこにも該当しないファイルは1週間後にtmpフォルダに移動し、30日後削除する。（要件2. 以外はそのまま掃除元フォルダに置いておく）
要件4. 処理を定期的に自動で回す

## コア機能を実現するための質問

### 質問(要件1.)
指定したフォルダーから特定の文字列を含むファイルを見つけて、
それらを別のフォルダー「Path1」に移動させるPythonスクリプトを書いてほしい。

### 回答
``` py:要件1 スクリプト
import os
import shutil

def move_files_with_string(source_folder, target_folder, search_string):
    # ターゲットフォルダーが存在しない場合は作成
    if not os.path.exists(target_folder):
        os.makedirs(target_folder)

    # ソースフォルダー内のファイルを確認
    for file_name in os.listdir(source_folder):
        if search_string in file_name:
            source_file_path = os.path.join(source_folder, file_name)
            target_file_path = os.path.join(target_folder, file_name)

            # ファイルを移動
            shutil.move(source_file_path, target_file_path)
            print(f"Moved: {file_name}")

# ソースフォルダー、ターゲットフォルダー、検索文字列を指定
source_folder = 'path/to/source/folder'  # ソースフォルダーのパス
target_folder = '<Path1>'               # ターゲットフォルダーのパス
search_string = 'specific_string'       # 検索する文字列

move_files_with_string(source_folder, target_folder, search_string)
```

:::note info
もし、答えが微妙なときは ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/4f995b3d-23c3-c7fa-b6a7-4370859ae256.png)
この鉛筆マークを推して質問事態を推して質問を修正しましょう。

また、上のコードをベースで機能を追加していくから覚えてね」というとちゃんと覚えてくれるんですよ。いわゆるこじらせる前に「保存」っていうものです。

::: 

回答を以下に修正し、実行してみました。
```diff_python
# ソースフォルダー、ターゲットフォルダー、検索文字列を指定

- source_folder = r'path/to/source/folder'  # ソースフォルダーのパス
- target_folder = r'<Path1>'               # ターゲットフォルダーのパス
- search_string = r'specific_string'       # 検索する文字列

# ソースフォルダー、ターゲットフォルダー、検索文字列を指定
+ source_folder = 'C:\Users\xxx\OneDrive\Desktop\sample'  # ソースフォルダーのパス
+ target_folder = 'C:\Users\xxx\OneDrive\Desktop\python-chatgpt'  # ターゲットフォルダーのパス
+ search_string = 'A社'       # 検索する文字列

```
### 結果は・・・ ちゃんとA社という文字列の名前のファイル名のみ移動される～😲(初回でうまく動くのは珍しいので💦気持ちいい)

ここからは見やすく、画面キャプチャーを利用します。
~~ここからはタイピングが面倒くさくなったので、画面キャプチャーを利用します。🙈🙉🙊~~

### 保存
コア機能がうまく動いたので保存します。GPTちゃん覚えておいて。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/a6c992af-dace-ebba-2c85-c9e5e70a29fb.png)

### 拡張(要件2. ↑をどんどん追加したいA社、B社、C社・・・など )
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/9aac2f17-192b-59e3-a22d-af670daef217.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/d3822e30-41b6-8081-ba2d-0488b26271a4.png)

書いてもらったコードなんかっぽい
``` py:要件2.
import os
import shutil

def move_files_with_strings(source_folder, strings_and_folders):
    for search_string, target_folder in strings_and_folders.items():
        # ターゲットフォルダーが存在しない場合は作成
        if not os.path.exists(target_folder):
            os.makedirs(target_folder)

        # ソースフォルダー内のファイルを確認
        for file_name in os.listdir(source_folder):
            if search_string in file_name:
                source_file_path = os.path.join(source_folder, file_name)
                target_file_path = os.path.join(target_folder, file_name)

                # ファイルを移動
                shutil.move(source_file_path, target_file_path)
                print(f"Moved: {file_name} to {target_folder}")

# ソースフォルダーのパス
source_folder = 'path/to/source/folder'

# 検索文字列とターゲットフォルダーの辞書
strings_and_folders = {
    'string1': 'path/to/target/folder1',
    'string2': 'path/to/target/folder2',
    'string3': 'path/to/target/folder3',
    'string4': 'path/to/target/folder4',
    'string5': 'path/to/target/folder5'
}

move_files_with_strings(source_folder, strings_and_folders)
```


うまくいったので、これもセーブします。
ちなみにセーフはローカルで修正して丸ごとGPTに張り付ける。
その後、このコードから機能追加するから機能追加するから覚えてと言っておきます。
**私の場合「要件2.」のコードからパスを修正しました。**

``` diff_python:修正した個所 
# 検索文字列とターゲットフォルダーの辞書
strings_and_folders = {
-   'string1': 'path/to/target/folder1',
-   'string2': 'path/to/target/folder2',
-   'string3': 'path/to/target/folder3',
-   'string4': 'path/to/target/folder4',
-   'string5': 'path/to/target/folder5'
}

# 検索文字列とターゲットフォルダーの辞書
strings_and_folders = {
+    'A社': r'<A社という文字列が入っているファイルの格納したいパス>',
+    'B社': r'<B社という文字列が入っているファイルの格納したいパス>',
+    'C社': r'<C社という文字列が入っているファイルの格納したいパス>',
+    'D社': r'<D社という文字列が入っているファイルの格納したいパス>',
+    'E社': r'<E社という文字列が入っているファイルの格納したいパス>'
}
```

### 要件3
今までは細かく積み上げてみましたが、試しに二つをまとめて質問してみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/ecebea0e-b950-2227-236c-ee748d63a584.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/33bd34a2-3d39-8d88-39c8-25d55ab89789.png)

1は自分ではできないよとのこと。
できる範囲をスクリプトに追加してくれました。

``` py: 要件3. 最終版 
import os
import shutil
import time
from datetime import datetime, timedelta

def move_old_files_to_tmp(source_folder, tmp_folder, days_old=7):
    if not os.path.exists(tmp_folder):
        os.makedirs(tmp_folder)

    for file_name in os.listdir(source_folder):
        file_path = os.path.join(source_folder, file_name)
        file_mod_time = datetime.fromtimestamp(os.path.getmtime(file_path))
        if datetime.now() - file_mod_time > timedelta(days=days_old):
            shutil.move(file_path, os.path.join(tmp_folder, file_name))
            print(f"Moved to tmp: {file_name}")

def delete_old_files_from_tmp(tmp_folder, days_old=30):
    for file_name in os.listdir(tmp_folder):
        file_path = os.path.join(tmp_folder, file_name)
        file_creation_time = datetime.fromtimestamp(os.path.getctime(file_path))
        if datetime.now() - file_creation_time > timedelta(days=days_old):
            os.remove(file_path)
            print(f"Deleted: {file_name}")

def move_files_with_strings(source_folder, strings_and_folders, tmp_folder):
    move_old_files_to_tmp(source_folder, tmp_folder)

    for search_string, target_folder in strings_and_folders.items():
        if not os.path.exists(target_folder):
            os.makedirs(target_folder)

        for file_name in os.listdir(source_folder):
            if search_string in file_name:
                source_file_path = os.path.join(source_folder, file_name)
                target_file_path = os.path.join(target_folder, file_name)

                shutil.move(source_file_path, target_file_path)
                print(f"Moved: {file_name} to {target_folder}")

    delete_old_files_from_tmp(tmp_folder)

# パスと設定
source_folder = r'<お掃除したいフォルダのパス>'
tmp_folder = r'<どれにも当てはまらないファイルが収納されるパス>'
strings_and_folders = {
    'A社': r'<A社という文字列が入っているファイルの格納したいパス>',
    'B社': r'<B社という文字列が入っているファイルの格納したいパス>',
    'C社': r'<C社という文字列が入っているファイルの格納したいパス>',
    'D社': r'<D社という文字列が入っているファイルの格納したいパス>',
    'E社': r'<E社という文字列が入っているファイルの格納したいパス>'
}
move_files_with_strings(source_folder, strings_and_folders, tmp_folder)
```

### ついでに 
らったこの説目も不十分なので追加で聞きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/b3e66c45-f070-9f0d-8811-e075e5577f55.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163986/8d8aa095-a741-b7a7-9f5b-137d80da9272.png)


### Windowのスケジュールに追加（いわゆるcron)
```
Windows 10でタスクスケジューラーを使用して、Pythonスクリプトを毎日18時に実行するように設定する方法は以下の通りです


1. **タスクスケジューラーの開始**:
   - スタートメニューを開き、「タスクスケジューラー」と検索して起動します。

2. **基本タスクの作成**:
   - 「アクション」メニューから「基本タスクの作成...」を選択します。

3. **タスクの名前と説明の設定**:
   - タスクに名前を付け、必要に応じて説明を追加します（例: 名前 - `PythonScriptScheduler`）。
   - 「次へ」をクリックします。

4. **トリガーの設定**:
   - 「毎日」を選択して「次へ」をクリックします。
   - 開始日を設定し、「繰り返す」の項目で「18:00」を指定します。
   - 「次へ」をクリックします。

5. **アクションの設定**:
   - 「プログラムの開始」を選択して「次へ」をクリックします。
   - 「プログラム/スクリプト」の欄にPythonの実行ファイルのパスを入力します。通常は `C:\Python39\python.exe` のような形式です（Pythonのインストールパスに依存します）。
   - 「引数の追加」の欄にスクリプトのファイル名を入力します。フルパスが必要です（例: `C:\path\to\your\script.py`）。
   - 「開始位置」の欄にスクリプトがあるディレクトリのパスを入力します。
   - 「次へ」をクリックします。

6. **オプションの設定とタスクの完成**:
   - オプションを確認し、必要に応じて変更します。
   - 「完了」をクリックしてタスクを保存します。

7. **タスクのテスト**:
   - タスクスケジューラーのメインウィンドウで、作成したタスクを見つけ、右クリックして「実行」を選択してテストします。

注意点
- Pythonが正しくインストールされていることを確認し、正しいパスを使用してください。
- スクリプトのパスが正しいことを確認してください。
- タスクスケジューラーでは、Pythonスクリプトが依存する外部ファイルやライブラリへの正しいアクセスパスが必要です。依存関係がある場合、それらも適切に設定してください。
- スクリプトにユーザーの入力が必要な場合、スケジュールされたタスクではうまく動作しない可能性があります。このような場合、スクリプトを修正して自動実行できるようにする必要があります。

```
どれどれ、やってみよう。
```
1. **タスクスケジューラーの開始**:
   - スタートメニューを開き、「タスクスケジューラー」と検索して起動します。
```
検索しても出てこないし・・・草 Googleで検索イメージ見ながら登録

:::note warn
こういう系（Window設定関連、画面で見やいもの）はGoogleで「タスクスケジューラで定期的な作業を自動化する」などで検索して画面見ながらやった方が早い。
:::

## まとめ

個人的に実際に手を動いて作成するより楽しかったです。

おそらく、こんな利用ではないかと思います。
- 細かいところ（ミスタイピング）に気にしなくていいので楽
- 機能を実現するために、何かを１からのインプットが要らない（必要な部分だけ勉強）
- 質問するうちにフローやゴールのイメージが明確に浮かぶ
- 自分では想像できないところを見せてくれたりする
- GPTちゃんとワイがお互いコードメンター的な気もするので暇じゃない

また、個人のフォルダだけではなく、
複数人が使う共有フォルダなどの管理にも使えそうだと思ったり。

## 結論
このお店リピーター「あり」です。
もっと多くのアイディアや機能をGPTとPythonなどで実現してみたいです。