How to Build a Python Script That Archives Old Reports Automatically
If you want to archive reports python-style without turning your shared drive into a junk drawer, start with one simple rule: decide what counts as “old” and where those files should go. Most office scripts fail because they begin with code instead of a filing policy. Pick a source folder, pick an archive folder, and define the threshold clearly. Older than 30 days? Older than the end of the last quarter? Files not modified since a certain date? Be specific, because your script will do exactly what you tell it to do, including bad ideas.
A reliable archive structure usually looks something like this: Reports/ for current work and Archive/2024/11/ or Archive/2024/Q4/ for stored files. That makes retrieval sane later, which is the whole point of automate document storage in the first place. If you dump everything into one archive folder, you’re not really organizing anything. You’re just moving the mess to another room.
Use Python’s Standard Library Before You Reach for Anything Fancy
You can do a lot of file management automation with nothing more than Python’s standard library. For this job, pathlib gives you clean file paths, shutil handles moving files, and datetime helps you compare timestamps. That’s enough for a solid first version.
Here’s the core idea. You scan a reports folder, check each file’s modification date, and move anything older than your cutoff into a dated archive folder.
```
from pathlib import Path from datetime import datetime, timedelta import shutil
source_folder = Path("reports") archive_root = Path("archive") cutoff_date = datetime.now() - timedelta(days=30)
for file_path in source_folder.iterdir(): if file_path.is_file(): modified_time = datetime.fromtimestamp(file_path.stat().st_mtime) if modified_time < cutoff_date: year_folder = archive_root / str(modified_time.year) month_folder = year_folder / f"{modified_time.month:02}" month_folder.mkdir(parents=True, exist_ok=True) shutil.move(str(file_path), str(month_folder / file_path.name))
print("Old reports archived.")
```
That script is intentionally plain. Good. Plain scripts are easier to debug, easier to hand off, and less likely to break when someone else in the office touches them six months from now. If your workflow is local folders, shared network directories, or a synced drive, this is often enough to save real time immediately.
Make the Script Safer Before You Let It Touch Real Files
Now for the part people skip. A script that moves files without guardrails is how you create a Friday afternoon problem. Before you run anything in production, add a dry-run mode, logging, and duplicate-file handling. Those three things turn a risky office script into something you can actually trust.
A dry run prints what the script would move without moving anything. Logging writes actions to a text file so you can prove what happened later. Duplicate handling matters because archived folders often end up with files that share the same name, especially if people export “monthly_report.pdf” over and over like they’re trying to make future retrieval impossible.
Here’s a safer version:
```
from pathlib import Path from datetime import datetime, timedelta import shutil
source_folder = Path("reports") archive_root = Path("archive") log_file = Path("archive_log.txt") cutoff_date = datetime.now() - timedelta(days=30) dry_run = True
def unique_destination(destination): if not destination.exists(): return destination stem = destination.stem suffix = destination.suffix parent = destination.parent counter = 1 while True: new_path = parent / f"{stem}_{counter}{suffix}" if not new_path.exists(): return new_path counter += 1
with log_file.open("a", encoding="utf-8") as log: for file_path in source_folder.iterdir(): if file_path.is_file(): modified_time = datetime.fromtimestamp(file_path.stat().st_mtime) if modified_time < cutoff_date: target_folder = archive_root / str(modified_time.year) / f"{modified_time.month:02}" target_folder.mkdir(parents=True, exist_ok=True) destination = unique_destination(target_folder / file_path.name)
message = f"MOVE: {file_path} -> {destination}\n" print(message.strip()) log.write(message)
if not dry_run: shutil.move(str(file_path), str(destination))
```
Run it with dry_run = True first. Read the output. Spot anything weird? Fix that now, not after the sales team asks where last month’s report went. Also, test on a copy of your folders. Always. People love the word “automation” right up until it automates the wrong thing.
Pick the Right Archive Logic for the Way Your Reports Are Actually Used
There isn’t one perfect way to automate document storage. The right logic depends on how your team finds old reports later. If people search by date, archive by year and month. If finance works by quarter, use quarter folders. If several departments drop files into one place, you may want a structure like Archive/Operations/2024/11/ and Archive/Sales/2024/11/ . Retrieval should drive design. Not the other way around.
You also need to decide which date matters. File modification time is the easiest choice, but it may not match the business meaning of “old.” Some teams rename files with dates like report_2024-10-15.xlsx . Others generate PDFs on one day but revise metadata later, which makes modification time misleading. If filenames contain reliable dates, parsing the filename may be smarter than reading filesystem metadata. That takes a little extra code, but it prevents a lot of head-scratching when files land in the “wrong” month.
Another practical choice: move or copy? Moving keeps the live folder clean, which is usually the goal. Copying is safer if the archive is a backup layer and you still want current folders untouched. Just be honest about storage costs and clutter. In most office environments, moving old reports out of the active directory is what actually reduces confusion.
Schedule It So the Script Runs Without You Thinking About It
A script you have to remember to run is half a solution. Once it works, schedule it. On Windows, Task Scheduler is the obvious choice. On macOS or Linux, use cron or a launch agent depending on the setup. The best time is usually early morning or late evening, when nobody is actively editing reports and network usage is low.
If you’re on Windows, save the script as something like archive_reports.py , then create a basic task that runs:
```
python C:\path\to\archive_reports.py
```
Set it to run daily or weekly based on how fast reports pile up. Weekly is often enough for routine file management automation. Daily makes sense if the folder gets heavy quickly or if your retention rules are strict. On Linux or macOS, a cron job might look like this:
```
0 2 * * 1 /usr/bin/python3 /path/to/archive_reports.py
```
That runs every Monday at 2:00 AM. Quiet. Predictable. Exactly what you want. One more tip: point scheduled jobs to full absolute paths, not relative ones. Relative paths behave differently depending on where the job starts, and that’s a dumb problem to debug at 8:30 in the morning.
Small Upgrades That Make the Script Feel Professional
Once the basic workflow works, a few upgrades make it far more useful. First, move your settings into variables at the top or into a simple config file so nobody has to dig through logic to change the archive age or folder paths. Second, add exception handling so one locked file doesn’t kill the whole run. Third, send yourself a short email or write a clear log summary showing how many files were moved and whether anything failed.
You can also filter by extension if your reports live beside random junk. Maybe you only want to archive .pdf , .xlsx , and .docx . That’s common in office scripts, especially when a shared folder also collects screenshots, temp files, or exports from other systems. A simple extension whitelist keeps the script focused.
Here’s the kind of logic worth adding next: skip files that are open, ignore hidden system files, archive only approved extensions, and write errors to a separate log. None of that is flashy. It’s just the difference between a quick demo and a tool people keep using. That’s really the standard to aim for. Not clever code. Reliable code that quietly keeps old reports where they belong.