I've been freelancing for 4 years. The biggest lesson: admin work will eat you alive if you let it.
Chasing invoices. Sending project updates. Tracking billable hours. Following up on proposals. Writing weekly reports.
None of it makes you money directly. All of it is necessary. The solution: automate it.
Here are the 5 scripts I run on autopilot every week.
1. Automatic Invoice Follow-Up
Checks your invoice records and sends a polite follow-up if payment is 7+ days late.
import smtplib
import csv
from datetime import datetime, timedelta
from email.mime.text import MIMEText
def check_overdue_invoices(invoices_file="invoices.csv"):
overdue = []
today = datetime.now().date()
with open(invoices_file) as f:
reader = csv.DictReader(f)
for row in reader:
if row["status"] == "unpaid":
due_date = datetime.strptime(row["due_date"], "%Y-%m-%d").date()
days_overdue = (today - due_date).days
if days_overdue > 7:
overdue.append({**row, "days_overdue": days_overdue})
return overdue
def send_followup(invoice, email_from, email_password):
body = f"Hi {invoice['client_name']},\n\nFollowing up on invoice #{invoice['invoice_id']} for ${invoice['amount']}.\n\nCould you let me know the payment status?\n\nThanks"
msg = MIMEText(body)
msg["Subject"] = f"Follow-up: Invoice #{invoice['invoice_id']} ({invoice['days_overdue']} days overdue)"
msg["From"] = email_from
msg["To"] = invoice["client_email"]
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(email_from, email_password)
server.sendmail(email_from, invoice["client_email"], msg.as_string())
print(f"Follow-up sent for Invoice #{invoice['invoice_id']}")
overdue = check_overdue_invoices()
for inv in overdue:
send_followup(inv, "your@email.com", "your-app-password")
CSV columns: invoice_id, client_name, client_email, amount, due_date, status
2. Time Tracker to Weekly Report
Reads your time logs and generates a client-ready weekly summary automatically.
from collections import defaultdict
from datetime import datetime, timedelta
import csv
def generate_weekly_report(time_log="time_log.csv"):
week_ago = datetime.now() - timedelta(days=7)
client_hours = defaultdict(list)
with open(time_log) as f:
for row in csv.DictReader(f):
date = datetime.strptime(row["date"], "%Y-%m-%d")
if date >= week_ago:
client_hours[row["client"]].append({
"date": row["date"],
"task": row["task"],
"hours": float(row["hours"])
})
report = f"Weekly Report - Week of {week_ago.strftime('%B %d')}\n"
report += "=" * 50 + "\n\n"
for client, entries in client_hours.items():
total = sum(e["hours"] for e in entries)
report += f"Client: {client} | Total: {total:.1f} hours\n"
for e in entries:
report += f" {e['date']} - {e['task']} ({e['hours']}h)\n"
report += "\n"
filename = f"report_{datetime.now().strftime('%Y%m%d')}.txt"
with open(filename, "w") as f:
f.write(report)
print(f"Report saved to {filename}")
generate_weekly_report()
3. Proposal Follow-Up Tracker
Tracks sent proposals and reminds you to follow up at day 3, 7, and 14.
import csv
from datetime import datetime
def check_pending_proposals(proposals_file="proposals.csv"):
today = datetime.now().date()
with open(proposals_file) as f:
for row in csv.DictReader(f):
if row["status"] == "sent":
sent_date = datetime.strptime(row["sent_date"], "%Y-%m-%d").date()
days_since = (today - sent_date).days
if days_since in [3, 7, 14]:
print(f"\n📧 Follow up on day {days_since}:")
print(f" Client: {row['client']}")
print(f" Project: {row['project']}")
print(f" Value: ${row['amount']}")
check_pending_proposals()
4. Daily Earnings Snapshot
Calculates your actual hourly rate and daily earnings. Useful reality check.
import csv
from datetime import datetime
def daily_earnings(time_log="time_log.csv"):
today = datetime.now().strftime("%Y-%m-%d")
total_hours = 0
total_earned = 0
with open(time_log) as f:
for row in csv.DictReader(f):
if row["date"] == today:
hours = float(row["hours"])
rate = float(row.get("rate", 0))
total_hours += hours
total_earned += hours * rate
rate = total_earned / total_hours if total_hours > 0 else 0
print(f"Today: {total_hours:.1f}h worked | ${total_earned:.2f} earned | ${rate:.2f}/hr")
daily_earnings()
5. Client Relationship Greeter
Sends birthday and work-anniversary messages to clients automatically.
import csv
import smtplib
from datetime import datetime
from email.mime.text import MIMEText
def check_client_occasions(clients_file="clients.csv", email_from="", email_password=""):
today = datetime.now()
with open(clients_file) as f:
for row in csv.DictReader(f):
if row.get("birthday"):
bday = datetime.strptime(row["birthday"], "%Y-%m-%d")
if bday.month == today.month and bday.day == today.day:
send_greeting(row, "birthday", email_from, email_password)
def send_greeting(client, occasion, email_from, email_password):
body = f"Hi {client['name']},\n\nHappy {occasion}! Hope you're having a great day.\n\nBest"
msg = MIMEText(body)
msg["Subject"] = f"Happy {occasion.capitalize()}, {client['name']}!"
msg["From"] = email_from
msg["To"] = client["email"]
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
server.login(email_from, email_password)
server.sendmail(email_from, client["email"], msg.as_string())
check_client_occasions()
Running All 5 Together
# Every weekday morning at 8am
0 8 * * 1-5 cd ~/scripts && python invoice_followup.py && python weekly_report.py && python proposals.py && python earnings.py && python client_occasions.py
Want the Full Toolkit?
I spent two years building polished versions of these with error handling, HTML email templates, logging, and 7 more scripts.
The Python Business Automation Toolkit has 12 scripts total for $29. The 5 above are yours free — the toolkit has the production-ready versions plus expense categorization, inventory alerts, payment reminders, and more.
Which of these would save you the most time? Let me know in the comments.
Top comments (0)