Project: A File Organiser
About 35 minutes
Why this matters
Everyone’s Downloads folder is a mess. In this project you build a small command-line tool that sorts a folder’s files into Images, Documents, Archives and Other. It uses everything from this track: arguments, folders and files, and safe habits like a dry run and never overwriting.
The idea
The design is a few small functions, each easy to test:
category_for(filename): which category does a file belong to?unique_destination(path): ifphoto.jpgalready exists there, usephoto (1).jpg, thenphoto (2).jpg.plan_moves(folder): work out what would move where, without moving anything.organise(folder, dry_run): carry out the plan (or only report it).main(argv): the command line:organiser.py FOLDER [--dry-run].
Separating planning from doing is what makes a dry run easy and safe: the same plan is either executed or just printed.
from pathlib import Path
import shutil
source = Path("Downloads/cat.JPG")
target = Path("Downloads/Images/cat.JPG")
target.parent.mkdir(exist_ok=True)
shutil.move(source, target)
Try it
In a scratch folder, create a.jpg, b.pdf and c.xyz. Write just category_for first and print the category of each name.
Your turn
Write these five functions:
category_for(filename):"Images"(.jpg,.jpeg,.png,.gif),"Documents"(.pdf,.txt,.md,.docx),"Archives"(.zip,.tar,.gz) or"Other". Ignore upper or lower case in the extension.unique_destination(path):pathitself if nothing exists there, otherwise the first free name of the formname (1).ext,name (2).ext, …plan_moves(folder): a list of(source, destination)pairs ofPaths for the files directly in the folder, sorted by file name. The destination isfolder / category / name. Do not touch folders.organise(folder, dry_run=False): carry out the plan, creating category folders as needed and never overwriting (useunique_destination). Return a list of strings like"cat.jpg -> Images"in plan order. Withdry_run=True, return the same list but change nothing.main(argv=None): argumentsfolderand--dry-run. Print each line fromorganise, then a last lineN file(s) moved(orN file(s) would be movedfor a dry run). Return0.
Recap
- Plan first, then act: it makes dry runs, testing and undo-thinking easy.
pathlib+shutildo the file work;argparsemakes the script reusable.- Never overwrite silently; find a free name instead.
Going further
Add a --undo that reads a log of moves and reverses them, or organise by the file’s modified date (path.stat().st_mtime). Run your tool with a real cron job once you trust it.