# ==========================================================
# https://www.planmytax.com/
#
# PlanMyTax TDS Certificate PDF Renamer
#
# Original:
# ABC123.pdf
#
# Renamed To:
# TDS Certificate - FY27 - Deductor - 2026.04 to 2026.06 - Deductee - ABC123.pdf
#
# Requirements:
# pip install pymupdf
# ==========================================================
import fitz # PyMuPDF
import re
import os
from pathlib import Path
# ==========================================================
# CONFIGURATION
# ==========================================================
PDF_FOLDER = r"D:\TDS\Certificates"
# ==========================================================
# HELPERS
# ==========================================================
def clean_filename(text):
"""Remove invalid Windows filename characters"""
if not text:
return ""
text = re.sub(r'[<>:"/\\|?*]', '', text)
text = re.sub(r'\s+', ' ', text)
return text.strip()
def convert_date_to_period(date_str):
"""
Converts:
01-Apr-2026
To:
2026.04
"""
months = {
'JAN': '01',
'FEB': '02',
'MAR': '03',
'APR': '04',
'MAY': '05',
'JUN': '06',
'JUL': '07',
'AUG': '08',
'SEP': '09',
'OCT': '10',
'NOV': '11',
'DEC': '12'
}
m = re.search(
r'(\d{1,2})[-/ ]([A-Za-z]{3})[-/ ](\d{4})',
date_str,
re.I
)
if not m:
return None
year = m.group(3)
month = months.get(m.group(2).upper())
if not month:
return None
return f"{year}.{month}"
def derive_fy(period_from):
"""
period_from = 2026.04
returns FY27
"""
if not period_from:
return "FY??"
year = int(period_from[:4])
month = int(period_from[5:7])
if month >= 4:
return f"FY{str(year + 1)[-2:]}"
else:
return f"FY{str(year)[-2:]}"
def extract_text(pdf_file):
text = ""
pdf = fitz.open(pdf_file)
for page in pdf:
text += page.get_text("text")
pdf.close()
return text
# ==========================================================
# FIELD EXTRACTION
# ==========================================================
def extract_fields(text):
deductor = ""
deductee = ""
period_from = ""
period_to = ""
# ------------------------------------------------------
# DEDUCTOR
# ------------------------------------------------------
m = re.search(
r'Name\s+and\s+Address\s+of\s+the\s+Deductor\s*(.*?)\n',
text,
re.I | re.S
)
if m:
deductor = clean_filename(m.group(1))
# ------------------------------------------------------
# DEDUCTEE
# ------------------------------------------------------
m = re.search(
r'Name\s+and\s+Address\s+of\s+the\s+Deductee\s*(.*?)\n',
text,
re.I | re.S
)
if m:
deductee = clean_filename(m.group(1))
# ------------------------------------------------------
# PERIOD
# ------------------------------------------------------
m = re.search(
r'Period\s+From\s+([^\n]+?)\s+To\s+([^\n]+)',
text,
re.I
)
if m:
period_from = convert_date_to_period(m.group(1))
period_to = convert_date_to_period(m.group(2))
return deductor, deductee, period_from, period_to
# ==========================================================
# MAIN
# ==========================================================
folder = Path(PDF_FOLDER)
if not folder.exists():
print(f"Folder not found: {PDF_FOLDER}")
raise SystemExit
print(f"\nProcessing folder: {folder}\n")
for pdf_file in folder.glob("*.pdf"):
try:
# --------------------------------------------------
# Skip already processed files
# --------------------------------------------------
if pdf_file.stem.startswith("TDS Certificate - "):
print(f"SKIPPED (already renamed): {pdf_file.name}")
continue
text = extract_text(pdf_file)
deductor, deductee, pfrom, pto = extract_fields(text)
# --------------------------------------------------
# Defaults
# --------------------------------------------------
if not deductor:
deductor = "Unknown Client"
if not deductee:
deductee = "Unknown Supplier"
# --------------------------------------------------
# Financial Year
# Derived from Period From
# --------------------------------------------------
fy = derive_fy(pfrom)
# --------------------------------------------------
# Period
# --------------------------------------------------
if pfrom and pto:
if pfrom == pto:
period = pfrom
else:
period = f"{pfrom} to {pto}"
else:
period = "Unknown Period"
# --------------------------------------------------
# Prevent excessively long filenames
# --------------------------------------------------
deductor = deductor[:50]
deductee = deductee[:50]
# --------------------------------------------------
# Keep Original Filename
# --------------------------------------------------
original_name = pdf_file.stem
prefix = (
f"TDS Certificate - {fy}"
f" - {deductor}"
f" - {period}"
f" - {deductee}"
)
new_name = (
prefix
+ " - "
+ original_name
+ pdf_file.suffix
)
new_name = clean_filename(new_name)
new_path = pdf_file.with_name(new_name)
# --------------------------------------------------
# Diagnostic Output
# --------------------------------------------------
print("\n------------------------------------------")
print("File :", pdf_file.name)
print("FY :", fy)
print("Deductor :", deductor)
print("Deductee :", deductee)
print("Period :", period)
print("------------------------------------------")
# --------------------------------------------------
# Rename
# --------------------------------------------------
if new_path.exists():
print("TARGET EXISTS - SKIPPED")
continue
os.rename(pdf_file, new_path)
print("RENAMED TO:")
print(new_name)
except Exception as e:
print(f"\nERROR: {pdf_file.name}")
print(str(e))
print("\nCompleted.")