Complete Implementation
#!/usr/bin/env python3
import shutil, json, argparse
from pathlib import Path
from datetime import datetime
CATEGORIES = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".webp"],
"Videos": [".mp4", ".mkv", ".avi", ".mov"],
"Audio": [".mp3", ".wav", ".flac", ".aac"],
"Documents": [".pdf", ".doc", ".docx", ".txt", ".md"],
"Code": [".py", ".js", ".ts", ".html", ".css"],
"Archives": [".zip", ".tar", ".gz", ".rar"],
}
def get_category(ext):
for cat, exts in CATEGORIES.items():
if ext.lower() in exts:
return cat
return "Other"
def organize(source, dest, dry_run=False):
moves = []
for file in source.iterdir():
if not file.is_file() or file.name.startswith("."):
continue
cat = get_category(file.suffix)
target_dir = dest / cat
target = target_dir / file.name
if target.exists():
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
target = target_dir / f"{file.stem}_{ts}{file.suffix}"
moves.append({"src": str(file), "dst": str(target), "category": cat})
if not dry_run:
target_dir.mkdir(parents=True, exist_ok=True)
shutil.move(str(file), target)
return moves
parser = argparse.ArgumentParser()
parser.add_argument("source")
parser.add_argument("-d", "--dest")
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
src = Path(args.source).resolve()
dst = Path(args.dest).resolve() if args.dest else src
moves = organize(src, dst, args.dry_run)
by_cat = {}
for m in moves:
by_cat.setdefault(m["category"], []).append(m)
for cat, items in sorted(by_cat.items()):
action = "Would move" if args.dry_run else "Moved"
print(f"\n{cat} ({len(items)} files)")
for item in items:
print(f" {action}: {Path(item['src']).name}")
if not args.dry_run and moves:
log = dst / ".organizer_log.json"
log.write_text(json.dumps(moves, indent=2))
print(f"\n{len(moves)} files moved. Log: {log}")python organizer.py ~/Downloads --dry-run
python organizer.py ~/Downloads -d ~/OrganizedποΈ Practical Exercise
Extend the file organizer:
- Add more category mappings (e.g. code files, audio, video).
- Handle name collisions by appending a number to duplicate filenames.
- Add a βdry runβ mode that prints what would move without moving anything.
- Log every move to a file with a timestamp.
π₯ Challenge Exercise
Make the organizer robust and reusable: accept the target folder as a command-line argument, support a configurable mapping of extensions to folders, skip files in use, and make repeated runs safe (idempotent). Add logging and a summary of how many files were moved per category. Bonus: optionally watch the folder and organize new files automatically as they appear.
π Summary
- This project sorts files into category folders based on their extension.
pathlibprovides a clean API for inspecting and moving files.- Folders are created on demand and name collisions are handled safely.
- A dry-run mode and logging make a file-moving script trustworthy.
- Idempotent design means re-running it does no harm.
- It can be parameterized with CLI arguments and even run automatically.
Interview Questions on Building a File Organizer
- How do you work with the filesystem in Python?
- Why is
pathlibpreferred over string-based paths? - How do you safely move files and handle name collisions?
- How do you make a script idempotent?
- How would you add command-line arguments?
- Why add a dry-run mode to a destructive script?
- How would you make the script run automatically?
Related Topics
FAQ
Because moving or renaming files is hard to undo. A dry run prints exactly what the script would do without touching anything, so you can verify the behavior before committing to real changes.
Before moving, check whether the destination exists; if it does, append a counter or timestamp to the filename (e.g. report (1).pdf). This prevents silent data loss from collisions.
Make it idempotent: skip files already in their target folder, handle the case where folders already exist, and never assume the directory is in a fresh state. Re-running should simply organize any new files.
Yes β run it on a schedule (cron/Task Scheduler), or use a filesystem-watching library like watchdog to react to new files in real time.
