Advertisement
πŸ“ Projects

Python File Organizer – Automation Project

Sort files into categorized folders automatically. Features dry-run preview, conflict handling, and a move log for undo.

⏱️ 20 min read🎯 ProjectsπŸ“… Updated 2026

Complete Implementation

Python
#!/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}")
Bash
python organizer.py ~/Downloads --dry-run
python organizer.py ~/Downloads -d ~/Organized
Tip: The --dry-run flag is essential for any file manipulation tool. Always let users preview before committing.

πŸ‹οΈ Practical Exercise

Extend the file organizer:

  1. Add more category mappings (e.g. code files, audio, video).
  2. Handle name collisions by appending a number to duplicate filenames.
  3. Add a β€œdry run” mode that prints what would move without moving anything.
  4. 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.
  • pathlib provides 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 pathlib preferred 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?

FAQ

Why add a dry-run mode? +

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.

How do I avoid overwriting files with the same name? +

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.

How do I make the script safe to run repeatedly? +

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.

Can it organize files automatically as they arrive? +

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.