File Handling Capstone Projects
A persistent, timestamped command-line note-taking application that logs notes to a text file with automated datetime headers and keyword searching:
# =========================================================================
# PROJECT 1: FILE-BASED TIMESTAMPED NOTES APPLICATION
# =========================================================================
import datetime as dt
from pathlib import Path
NOTES_FILE = Path("daily_notes.txt")
def add_note(note_text):
"""Append a new note with timestamp."""
timestamp = dt.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(NOTES_FILE, "a", encoding="utf-8") as f:
f.write(f"[{timestamp}] {note_text.strip()}\n")
return "โ
Note saved successfully!"
def view_all_notes():
"""Read and display all notes from file."""
if not NOTES_FILE.exists():
return "โน๏ธ No notes recorded yet."
with open(NOTES_FILE, "r", encoding="utf-8") as f:
return f.read().strip()
def search_notes(keyword):
"""Search for notes containing a specific keyword."""
if not NOTES_FILE.exists():
return []
matches = []
with open(NOTES_FILE, "r", encoding="utf-8") as f:
for line in f:
if keyword.lower() in line.lower():
matches.append(line.strip())
return matches
# Run Notes App Demonstration:
print("--- ๐ Notes Application Demo ---")
add_note("Studied Python Exception Handling (try-except-else-finally)")
add_note("Built CSV Expense Tracker with DictReader")
add_note("Reviewing FastAPI background tasks for production")
print("\n--- All Saved Notes ---")
print(view_all_notes())
print("\n--- Search Results for 'Python' ---")
for note in search_notes("Python"):
print("โข", note)
- Uses append mode (
"a") to safely preserve all historical entries. - Case-insensitive streaming search using
keyword.lower() in line.lower()without loading unnecessary memory.
A file-backed Contact Book manager supporting contact addition, search by name or phone, and listing contacts alphabetically:
# =========================================================================
# PROJECT 2: FILE-BACKED CONTACT BOOK APPLICATION
# =========================================================================
import json
from pathlib import Path
CONTACTS_FILE = Path("contacts_db.json")
def load_contacts():
"""Load contacts from JSON file safely."""
if not CONTACTS_FILE.exists():
return {}
try:
with open(CONTACTS_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except (json.JSONDecodeError, OSError):
return {}
def save_contacts(contacts_dict):
"""Save contacts dictionary to JSON file."""
with open(CONTACTS_FILE, "w", encoding="utf-8") as f:
json.dump(contacts_dict, f, indent=4)
def add_contact(name, phone, email, category="Personal"):
"""Add or update a contact record."""
contacts = load_contacts()
contacts[name.strip().title()] = {
"phone": phone.strip(),
"email": email.strip().lower(),
"category": category
}
save_contacts(contacts)
return f"โ
Contact '{name}' saved successfully!"
def search_contact(search_term):
"""Find contact by name substring or phone."""
contacts = load_contacts()
results = {}
for name, details in contacts.items():
if search_term.lower() in name.lower() or search_term in details["phone"]:
results[name] = details
return results
# Run Contact Book Demonstration:
print("--- ๐ฑ Contact Book Manager Demo ---")
add_contact("Balaji Dev", "+91 98765 43210", "balaji@example.com", "Work")
add_contact("Alex Smith", "+1 555 123 4567", "alex@example.com", "Friends")
add_contact("Chloe Davis", "+44 20 7946 0991", "chloe@techcorp.com", "Work")
print("\n--- Search Results for 'balaji' ---")
print(json.dumps(search_contact("balaji"), indent=2))
Handles missing database files gracefully by returning an empty dictionary ({}) and catching corrupted JSON with json.JSONDecodeError.
A complete financial expense tracking system that logs transactions to CSV, calculates total expenditures, and aggregates category summaries:
# =========================================================================
# PROJECT 3: FINANCIAL EXPENSE TRACKER USING CSV
# =========================================================================
import csv
import datetime as dt
from pathlib import Path
EXPENSES_FILE = Path("monthly_expenses.csv")
FIELDNAMES = ["date", "category", "description", "amount"]
def initialize_expense_file():
"""Ensure CSV file exists with proper headers."""
if not EXPENSES_FILE.exists():
with open(EXPENSES_FILE, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
writer.writeheader()
def log_expense(category, description, amount):
"""Log an expense entry to CSV."""
initialize_expense_file()
entry = {
"date": dt.date.today().isoformat(),
"category": category.title(),
"description": description.strip(),
"amount": f"{float(amount):.2f}"
}
with open(EXPENSES_FILE, "a", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=FIELDNAMES)
writer.writerow(entry)
return f"โ
Logged Rs.{float(amount):.2f} for '{description}'"
def generate_expense_analytics():
"""Calculate total spending and category breakdown."""
if not EXPENSES_FILE.exists():
return 0.0, {}
total_spent = 0.0
category_totals = {}
with open(EXPENSES_FILE, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
amt = float(row["amount"])
cat = row["category"]
total_spent += amt
category_totals[cat] = category_totals.get(cat, 0.0) + amt
return round(total_spent, 2), category_totals
# Run Expense Tracker Demonstration:
print("--- ๐ฐ Financial Expense Tracker Demo ---")
log_expense("Food", "Grocery Supermart Shopping", 1250.00)
log_expense("Utilities", "High-Speed Internet Bill", 999.00)
log_expense("Food", "Team Coffee & Snacks", 350.00)
log_expense("Education", "Python Masterclass Subscription", 1499.00)
total, breakdown = generate_expense_analytics()
print(f"\n๐ Total Monthly Expenditure: Rs.{total:,.2f}")
print("--- Category Breakdown ---")
for cat, amt in breakdown.items():
pct = (amt / total) * 100 if total > 0 else 0
print(f"โข {cat:12}: Rs.{amt:,.2f} ({pct:.1f}%)")
Uses csv.DictReader to stream expense rows and computes aggregate percentage breakdowns per category dynamically.
A comprehensive academic records CRUD engine managing student enrollments, subject mark sheets, GPA calculations, and student record exports:
# =========================================================================
# PROJECT 4: JSON STUDENT ACADEMIC RECORDS SYSTEM
# =========================================================================
import json
from pathlib import Path
STUDENT_DB = Path("students_database.json")
def load_db():
if not STUDENT_DB.exists(): return {"students": {}}
with open(STUDENT_DB, "r", encoding="utf-8") as f:
return json.load(f)
def save_db(data):
with open(STUDENT_DB, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4)
def register_student(student_id, name, branch):
"""Create a new student profile."""
db = load_db()
if student_id in db["students"]:
return f"โ Student ID {student_id} already exists!"
db["students"][student_id] = {
"name": name,
"branch": branch,
"marks": {},
"gpa": 0.0
}
save_db(db)
return f"๐ Student '{name}' registered successfully under ID: {student_id}"
def record_marks(student_id, subject, score):
"""Add subject score and recalculate GPA."""
db = load_db()
student = db["students"].get(student_id)
if not student:
return f"โ Student {student_id} not found!"
student["marks"][subject] = score
all_scores = list(student["marks"].values())
student["gpa"] = round(sum(all_scores) / len(all_scores), 2)
save_db(db)
return f"โ
Recorded {subject}: {score}/100 | New GPA: {student['gpa']}"
def get_student_report_card(student_id):
"""Generate a clean formatted report card."""
db = load_db()
student = db["students"].get(student_id)
if not student: return "Student not found!"
lines = [
"=" * 45,
f"๐ REPORT CARD: {student['name']} (ID: {student_id})",
f"Branch: {student['branch']} | Overall GPA: {student['gpa']}%",
"-" * 45,
"Subject Scores:"
]
for sub, mark in student["marks"].items():
lines.append(f" โข {sub:20}: {mark}/100")
lines.append("=" * 45)
return "\n".join(lines)
# Run Student Records Demonstration:
print("--- ๐ Student Records Management System ---")
register_student("STU101", "Balaji", "Computer Science")
record_marks("STU101", "Python Programming", 98)
record_marks("STU101", "Data Structures", 92)
record_marks("STU101", "Database Systems", 95)
print(get_student_report_card("STU101"))
- Full CRUD persistence in structured JSON.
- Automatic re-calculation of dynamic metrics (GPA).
- Modular functions with input validation and clean error returns.
If your JSON file is empty or contains invalid syntax, json.load() raises json.JSONDecodeError: Expecting value. Always wrap file loading in a try-except block and provide an empty dictionary fallback.
Create a mini bookmark manager that stores website URLs with categories in a JSON file "bookmarks.json" and prints all bookmarked sites.
import json
from pathlib import Path
bookmarks_file = Path("bookmarks.json")
bookmarks = {
"Python": ["https://python.org", "https://docs.python.org"],
"Compilers": ["https://www.ourcompiler.com"],
"Tools": ["https://github.com", "https://stackoverflow.com"]
}
with open(bookmarks_file, "w", encoding="utf-8") as f:
json.dump(bookmarks, f, indent=2)
print("Saved Bookmarks:")
with open(bookmarks_file, "r", encoding="utf-8") as f:
data = json.load(f)
for cat, urls in data.items():
print(f"๐ {cat}:")
for u in urls:
print(f" ๐ {u}")
Q How do I choose between saving data in Text, CSV, or JSON?
Use Text (.txt) for unformatted logs and timestamped notes. Use CSV (.csv) for flat tabular rows and spreadsheets. Use JSON (.json) for nested objects, configuration files, and REST API data interchange.
Q How can I ensure atomic writes to prevent data corruption during crashes?
Write data to a temporary file (e.g. "data.json.tmp") first, and then atomically rename it to "data.json" using Path.rename(). Renames are atomic on modern operating systems.
Q When should I migrate from JSON files to a database like SQLite or PostgreSQL?
Migrate to a database when: (1) multiple concurrent threads/users write simultaneously, (2) dataset exceeds tens of megabytes, or (3) you need complex relational SQL joins and indexing.