1. Project Overview & Architecture
Welcome to Major Project 3: Desktop Automated File & System Organizer! In this major project, you will build an automated operating system utility that interacts directly with your computer's file system. You will combine file system operations (using pathlib and shutil), safe path resolution, dry-run simulation modes, and system logging to create a production-grade utility for decluttering directories like Downloads or Desktop.
Project Core Objectives
- Automated Directory Classification: Inspect file extensions dynamically and map files to dedicated category folders (Images, Documents, Archives, Audio, etc.).
- Dry-Run Safety Mode: Allow users to preview actions before making any permanent file system modifications on disk.
- Collision Resolution: Prevent file overwrites by appending timestamps or numbers when destination files already exist.
- System Activity Logging: Maintain an audit trail of all file movements using Python's standard
loggingmodule.
2. Functional Requirements & Feature Specifications
Your File Organizer must fulfill the following technical standards:
| Feature Component | Functional Specification | Python Mechanism |
|---|---|---|
| Path Resolution | Handles relative and absolute pathing cross-platform (Mac, Windows, Linux). | pathlib.Path with .resolve(). |
| File Operations | Creates category folders on demand and transfers files cleanly. | Path.mkdir(exist_ok=True), shutil.move(). |
| Duplicate Protection | Renames files dynamically if a file with the same name exists at destination. | Custom path collision checker with datetime suffixes. |
| Audit Logging | Records date, time, and move details to organizer.log. |
logging.basicConfig() and logging.info(). |
3. Complete Reference Implementation
Examine the full, modular Python script below to understand how file system operations, logging, and collision safety combine:
import shutil
import logging
from pathlib import Path
from datetime import datetime
# Set up audit logging
logging.basicConfig(
filename="organizer.log",
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s"
)
# Extension mapping dictionary
CATEGORY_MAP = {
"Images": [".jpg", ".jpeg", ".png", ".gif", ".svg", ".bmp", ".webp"],
"Documents": [".pdf", ".docx", ".doc", ".txt", ".xlsx", ".pptx", ".csv"],
"Audio": [".mp3", ".wav", ".aac", ".flac", ".ogg"],
"Videos": [".mp4", ".mkv", ".mov", ".avi"],
"Archives": [".zip", ".tar", ".gz", ".7z", ".rar"],
"Code": [".py", ".js", ".html", ".css", ".json", ".sql"]
}
def get_category(file_path):
"""Returns the matching category string for a file based on its extension."""
ext = file_path.suffix.lower()
for category, extensions in CATEGORY_MAP.items():
if ext in extensions:
return category
return "Others"
def resolve_duplicate(destination_path):
"""If destination file exists, appends a timestamp to prevent overwriting."""
if not destination_path.exists():
return destination_path
stem = destination_path.stem
suffix = destination_path.suffix
parent = destination_path.parent
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
return parent / f"{stem}_{timestamp}{suffix}"
def organize_directory(target_path_str, dry_run=True):
"""Scans and organizes files in the target directory."""
target = Path(target_path_str).resolve()
if not target.exists() or not target.is_dir():
print(f"โ Error: '{target}' is not a valid directory.")
return
mode_label = "๐ [DRY RUN PREVIEW]" if dry_run else "๐ [EXECUTING ORGANIZATION]"
print(f"\n{mode_label} Target: {target}")
# Reserved category folders to avoid moving newly created category directories
protected_folders = set(CATEGORY_MAP.keys()) | {"Others"}
moved_count = 0
for item in target.iterdir():
# Skip subdirectories, protected folders, and the log file
if item.is_dir() or item.name in protected_folders or item.name == "organizer.log":
continue
category = get_category(item)
dest_folder = target / category
dest_file_path = resolve_duplicate(dest_folder / item.name)
if dry_run:
print(f" โข [PREVIEW] Would move: '{item.name}' โ '{category}/'")
else:
dest_folder.mkdir(exist_ok=True)
shutil.move(str(item), str(dest_file_path))
log_msg = f"Moved: '{item.name}' โ '{category}/{dest_file_path.name}'"
print(f" โ
{log_msg}")
logging.info(log_msg)
moved_count += 1
if not dry_run:
print(f"\n๐ Finished! Successfully organized {moved_count} files.")
logging.info(f"Directory organization completed for {target}. Total files moved: {moved_count}")
else:
print("\nโน๏ธ Preview complete. Choose option 2 from the main menu to perform actual changes.")
def main():
"""Main terminal driver menu."""
print("==================================================")
print(" ๐ AUTOMATED FILE & SYSTEM ORGANIZER ")
print("==================================================")
target_dir = input("Enter directory path to organize (Press Enter for current directory): ").strip()
if not target_dir:
target_dir = "."
while True:
print("\nORGANIZER MAIN MENU:")
print("1) Preview Changes (Dry-Run)")
print("2) Execute File Organization")
print("3) View Activity Log")
print("4) Exit Program")
choice = input("\nSelect choice (1-4): ").strip()
if choice == "1":
organize_directory(target_dir, dry_run=True)
elif choice == "2":
confirm = input("โ ๏ธ Are you sure you want to organize files on disk? (y/N): ").strip().lower()
if confirm == "y":
organize_directory(target_dir, dry_run=False)
else:
print("Operation cancelled.")
elif choice == "3":
log_file = Path("organizer.log")
if log_file.exists():
print("\n--- ๐ Recent Activity Log ---")
print(log_file.read_text(encoding="utf-8"))
else:
print("\nNo log file found yet.")
elif choice == "4":
print("\nExiting File Organizer. Stay organized!")
break
else:
print("โ ๏ธ Invalid selection. Please choose 1, 2, 3, or 4.")
if __name__ == "__main__":
main()
4. Common Project Pitfalls
โ ๏ธ Project Watchlist:
- Infinite Subfolder Traversal: If you do not skip existing category subdirectories during iteration (
item.is_dir()), your script might try to move subfolders into themselves. - Silent Overwriting: Using
shutil.move()without checking if the destination file exists will overwrite files with matching names. Always resolve duplicate names first. - Hardcoded Slash Separators: Avoid string concatenations like
folder + "/" + filename. Usepathlib.Pathslash operators (folder / filename) for cross-platform compatibility.
5. Interactive Project Workspace
Build & Test: File & System Organizer
Use the workspace below to customize your organizer script. Try extending the category mapping with custom file extensions or adding an undo option!
Major Project 3 Completed!
Awesome job! You have built a practical, system-level automation tool for managing file systems cleanly and safely.
6. Key Takeaways & Milestone Achievement
- The
pathliblibrary simplifies cross-platform file path management and metadata inspection. - Always implement a dry-run or preview mode when writing scripts that modify or move local files.
- System logging produces audit trails essential for troubleshooting and verifying automation runs.
๐ Milestone 3 Complete
Congratulations on completing Major Project 3! Up next: **Major Project 4: Web Scraper & Data Pipeline** where you will gather and process real-time data from the web!