複数のファイルを作成日時別にフォルダ分けする仕組みを作ってみた。

# 概要　

数年間乱雑にスマホの画像をPCに移行し続けたせいで、写真の整理が手動では手に負えない状況になってしまいました。そこで今回は、ファイルを作成日時別にフォルダに分けてくれるプログラムを作ってみたいと思います。使用した言語はpythonです。
pythonは詳しくないので専門用語等は間違えてしまうかもしれませんが悪しからず。

# プログラムの全容
まずディレクトリ構造を記載しておきます。

    folder/
        ├ image
        ├ newFolder/2023/7
        ├ fileSorter.py


imagの中に仕分けをしたいファイルたちをぶち込んでおき、fileSorter.pyを実行することでnewFolderの中に月別にフォルダ分けされたファイルを格納します。newFolderは自動で生成されるので手動で作らなくても良いです。
仕組みとしては、

1. imageの中にあるファイルの詳細情報を取得

1. 1の詳細情報をもとにnewFolderの中に年別と月別にフォルダを生成させる

1. image内のファイルを作成日時が適応するフォルダに適当に入れてあげる

という3段階になります。正確にはファイルの数だけ1から3までのステップをfor文で行います。


# 下準備

### 使用するライブラリをインポートする

今回使用するライブラリは以下の3つです。

```python
import os
import time
import shutil
```

### ファイル名を取得

まずは仕分けをしたいファイル名をすべて取得してlistにまとめます。とりあえず下のような感じです。
```python
readPath = 'image'
readFiles = os.listdir(readPath)
```

定数のreadPathはfileSorter.pyからimageフォルダへの相対パスを定義してあげています。os.listdir(readPath)は引数の相対パスに該当するディレクトリ内にあるファイル名をリスト化してくれる関数です。

### newFolderを作成

次に仕分けされたファイルを格納するためのnewFolderを生成します。正直もとから手動で作っても問題はありませんが、pathを間違えるとめんどくさいので自動生成することにしました。

```python
newDirPath = 'newFolder'
try:
    os.mkdir(newDirPath)
except FileExistError:
    pass
```

newDirPathでnewFolderへの相対パスを定義して、os.mkdir(newDirPath)で引数の相対パスに対応するようにnewFolderを生成します。コードを実行したときに既にnewFolderが生成されている場合エラーが発生するためtryを使っています。

これで下準備は出来ました。

# ファイルの詳細情報を取得

ここからはfor文でファイル一つひとつをnewFolderの中に移動させるプログラムを作成していきます。そのためにまずはfor文の中にファイルの詳細情報を取得するプログラムを書きます。

```python
for readFile in readFiles:
    readFilePath = readPath + '/' + readFile
    
    year = str(time.localtime(os.path.getmtime(readFilePath))[0])
    month = str(time.localtime(os.path.getmtime(readFilePath))[1])

```
このfor文はreadFilesに格納されているファイル名のリストを1つずつ参照していきます。まずは参照したいファイルのパスをreadFilePahに定義しておき、これを利用して参照ファイルの更新日時の年と月を取得しています。os.path.getmtime()は引数のパスに対応するファイルのタイムスタンプを取得しますが、調べた感じUNIX timestampという値が返されるようなので、time.localtime()でローカルタイムに変換しています。time.localtime()はたとえば

```
time.struct_time(tm_year=2023, tm_mon=6, tm_mday=17, tm_hour=4, tm_min=35, tm_sec=38, tm_wday=2, tm_yday=137, tm_isdst=0)
```
のようなtime.struct_timeというクラスとして返ってきます。この中でほしい値はtm_yearとtm_monなのでリスト番号を振って取得しています。定義したいyearとmonthという定数はあとでフォルダ名として使いたいため、データタイプをintからstringに変換しておきます。

ちなみに、今回は作成日時ではなく更新日時をもとにファイル分けをすることにしましたが、作成日時をもとにデータを取得したい場合はWindows環境ではgetmtimeではなくgetctimeを使えばいいようです。

# newFolderの中に年別と月別にフォルダを生成させる

続いてnewFolderの中に年別と月別にフォルダを生成させます。以下のコードはfor文の中に書きます。

```python
    yearDirPath = newDirPath + '/' + year 
    monDirPath = yearDirPath + '/' + month

    try:
        os.mkdir(yearDirPath)
    except FileExistsError:
        pass

    try:
        os.mkdir(monDirPath)
    except FileExistsError:
        pass
```

年と月のディレクトリを作るために相対パス名を定義しておき、os.mkdir()でフォルダを作成します。for文を回していると既にフォルダが存在する可能性があるのでエラー対策としてtry文を使います。

# image内のファイルを適当なフォルダに入れてあげる

あとは作成したフォルダに移してあげるだけなので、

```python
    shutil.move(readFilePath, monDirPath + '/' + readFile)
```

を追記します。引数のひとつ目が始動させたいファイルのパスで、二つ目が移し先のパスになっています。2つ目の引数にreadFileまで書き込むと、既に同じファイルが移動先にあった場合でもエラーが起きません。

# 全体像
```python
import shutil
import time
import os

readPath = 'image'
readFiles = os.listdir(readPath)

newDirPath = 'newFolder'
try:
    os.mkdir(newDirPath)
except FileExistsError:
    pass


for readFile in readFiles:
    readFilePath = readPath + '/' + readFile
    year = str(time.localtime(os.path.getmtime(readFilePath))[0])
    month = str(time.localtime(os.path.getmtime(readFilePath))[1])

    yearDirPath = newDirPath + '/' + year 
    monDirPath = yearDirPath + '/' + month

    try:
        os.mkdir(yearDirPath)
    except FileExistsError:
        pass

    try:
        os.mkdir(monDirPath)
    except FileExistsError:
        pass

    shutil.move(readFilePath, monDirPath + '/' + readFile)
```

一応これで完成なのですが、場合によってはもともとのファイルを消したくないこともあるかもしれないのでコマンド操作で元データを消去するかしないかを変更できるようにします。

```python
import shutil
import time
import os

readPath = 'image'
readFiles = os.listdir(readPath)

newDirPath = 'newFolder'
try:
    os.mkdir(newDirPath)
except FileExistsError:
    pass

print("Are you planning to delete original files?")
print("if Yes, please enter 'y', and if No, please enter any command other than'y'")
arg = input('>>')


for readFile in readFiles:
    readFilePath = readPath + '/' + readFile
    year = str(time.localtime(os.path.getmtime(readFilePath))[0])
    month = str(time.localtime(os.path.getmtime(readFilePath))[1])

    yearDirPath = newDirPath + '/' + year 
    monDirPath = yearDirPath + '/' + month

    try:
        os.mkdir(yearDirPath)
    except FileExistsError:
        pass

    try:
        os.mkdir(monDirPath)
    except FileExistsError:
        pass

    if arg == 'y':
        shutil.move(readFilePath, monDirPath + '/' + readFile)
    else:
        shutil.copy2(readFilePath, monDirPath)
```

途中で
```python
arg = input('>>')
```
を書いておき、最後のif文でarg='y'のときはカット&ペーストをしてもらい、それ以外ではコピペをしてもらうようにしました。コマンド操作はおそらくipynb形式ではできないので、py形式でコードを保存しています。

# 課題
今回はファイルを移動させると作成日時も更新されてしまうため、もともとの作成日時を継承させることができませんでした。暇なときにでも作成日時も継承できる方法を模索してみようと思います。