Project: To-Do List Application with File Storage

A to-do list application demonstrates fundamental programming concepts including data structures, file handling, CRUD operations, and user interface design while creating practical productivity tool. This project implements comprehensive task manager supporting adding new tasks with titles and descriptions, viewing all tasks with status indicators, updating existing tasks modifying content, deleting completed or unwanted tasks, marking tasks complete tracking progress, filtering by status showing pending or completed, and persistent storage saving tasks to JSON file ensuring data survives program restarts. Command-line to-do managers provide lightweight alternatives to web applications, integrate into developer workflows, and teach essential concepts applicable to larger database-driven applications.
This comprehensive project explores task data structure using dictionaries storing id, title, description, status, and creation date, file persistence with JSON module using json.dump() saving tasks and json.load() reading tasks, CRUD operations implementing create adding tasks with unique IDs, read displaying tasks in formatted lists, update modifying existing tasks, and delete removing tasks by ID, user interface creating menu-driven CLI with numbered options accepting user input validating choices, status management tracking pending and completed states toggling completion status, data validation ensuring non-empty titles checking valid IDs, and error handling catching file not found exceptions, JSON decode errors, and invalid input. This project teaches dictionary operations for structured data, list comprehension filtering tasks, file I/O reading and writing JSON, working with datetime for timestamps, string formatting for display, function organization structuring code logically, exception handling for robustness, and user experience design creating intuitive interfaces, providing foundation for web applications, mobile apps, and database-driven systems while creating immediately useful tool for personal task management.
Core Task Manager Implementation
The core implementation defines task structure using dictionaries and implements CRUD operations. Each task contains unique identifier, title, optional description, completion status, and creation timestamp. Functions handle task creation, retrieval, updating, and deletion maintaining data consistency.
# Core Task Manager Implementation
import json
import os
from datetime import datetime
import uuid
# === Task data structure ===
"""
Task Dictionary Structure:
{
'id': 'unique-uuid',
'title': 'Task title',
'description': 'Optional description',
'status': 'pending' or 'completed',
'created_at': 'YYYY-MM-DD HH:MM:SS',
'completed_at': 'YYYY-MM-DD HH:MM:SS' or None
}
"""
# Configuration
DATA_FILE = 'tasks.json'
# === File operations ===
def load_tasks():
"""
Load tasks from JSON file.
Returns:
list: List of task dictionaries
"""
if not os.path.exists(DATA_FILE):
return []
try:
with open(DATA_FILE, 'r') as f:
return json.load(f)
except json.JSONDecodeError:
print("Warning: Tasks file is corrupted. Starting fresh.")
return []
except Exception as e:
print(f"Error loading tasks: {e}")
return []
def save_tasks(tasks):
"""
Save tasks to JSON file.
Args:
tasks (list): List of task dictionaries
Returns:
bool: True if successful, False otherwise
"""
try:
with open(DATA_FILE, 'w') as f:
json.dump(tasks, f, indent=2)
return True
except Exception as e:
print(f"Error saving tasks: {e}")
return False
# === CRUD operations ===
def create_task(title, description=""):
"""
Create a new task.
Args:
title (str): Task title
description (str): Optional task description
Returns:
dict: Created task dictionary
"""
if not title or not title.strip():
raise ValueError("Task title cannot be empty")
task = {
'id': str(uuid.uuid4())[:8], # Short UUID
'title': title.strip(),
'description': description.strip(),
'status': 'pending',
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'completed_at': None
}
return task
def add_task(tasks, title, description=""):
"""
Add new task to list and save.
Args:
tasks (list): Current task list
title (str): Task title
description (str): Task description
Returns:
dict: Created task
"""
task = create_task(title, description)
tasks.append(task)
if save_tasks(tasks):
print(f"\nβ Task added successfully! (ID: {task['id']})")
return task
else:
print("\nβ Failed to save task")
return None
def get_task_by_id(tasks, task_id):
"""
Find task by ID.
Args:
tasks (list): Task list
task_id (str): Task ID to find
Returns:
dict or None: Task if found, None otherwise
"""
for task in tasks:
if task['id'] == task_id:
return task
return None
def update_task(tasks, task_id, title=None, description=None):
"""
Update existing task.
Args:
tasks (list): Task list
task_id (str): Task ID to update
title (str): New title (optional)
description (str): New description (optional)
Returns:
bool: True if updated, False otherwise
"""
task = get_task_by_id(tasks, task_id)
if not task:
print(f"\nβ Task with ID '{task_id}' not found")
return False
if title:
task['title'] = title.strip()
if description is not None: # Allow empty string
task['description'] = description.strip()
if save_tasks(tasks):
print(f"\nβ Task updated successfully!")
return True
else:
print("\nβ Failed to save changes")
return False
def delete_task(tasks, task_id):
"""
Delete task by ID.
Args:
tasks (list): Task list
task_id (str): Task ID to delete
Returns:
bool: True if deleted, False otherwise
"""
task = get_task_by_id(tasks, task_id)
if not task:
print(f"\nβ Task with ID '{task_id}' not found")
return False
tasks.remove(task)
if save_tasks(tasks):
print(f"\nβ Task deleted successfully!")
return True
else:
print("\nβ Failed to delete task")
return False
def mark_complete(tasks, task_id):
"""
Mark task as completed.
Args:
tasks (list): Task list
task_id (str): Task ID to mark complete
Returns:
bool: True if marked, False otherwise
"""
task = get_task_by_id(tasks, task_id)
if not task:
print(f"\nβ Task with ID '{task_id}' not found")
return False
if task['status'] == 'completed':
print("\nβ Task is already completed")
return False
task['status'] = 'completed'
task['completed_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if save_tasks(tasks):
print(f"\nβ Task marked as completed!")
return True
else:
print("\nβ Failed to save changes")
return False
def mark_pending(tasks, task_id):
"""
Mark task as pending (undo completion).
Args:
tasks (list): Task list
task_id (str): Task ID to mark pending
Returns:
bool: True if marked, False otherwise
"""
task = get_task_by_id(tasks, task_id)
if not task:
print(f"\nβ Task with ID '{task_id}' not found")
return False
if task['status'] == 'pending':
print("\nβ Task is already pending")
return False
task['status'] = 'pending'
task['completed_at'] = None
if save_tasks(tasks):
print(f"\nβ Task marked as pending!")
return True
else:
print("\nβ Failed to save changes")
return False
# === Task filtering ===
def get_pending_tasks(tasks):
"""
Get all pending tasks.
Args:
tasks (list): Task list
Returns:
list: Pending tasks
"""
return [task for task in tasks if task['status'] == 'pending']
def get_completed_tasks(tasks):
"""
Get all completed tasks.
Args:
tasks (list): Task list
Returns:
list: Completed tasks
"""
return [task for task in tasks if task['status'] == 'completed']
# === Display functions ===
def display_task(task, show_description=True):
"""
Display single task.
Args:
task (dict): Task to display
show_description (bool): Whether to show description
"""
status_icon = 'β' if task['status'] == 'completed' else 'β'
print(f"\n[{status_icon}] {task['title']}")
print(f" ID: {task['id']}")
if show_description and task['description']:
print(f" Description: {task['description']}")
print(f" Created: {task['created_at']}")
if task['completed_at']:
print(f" Completed: {task['completed_at']}")
def display_tasks(tasks, title="All Tasks"):
"""
Display list of tasks.
Args:
tasks (list): Tasks to display
title (str): Display title
"""
print("\n" + "="*60)
print(f" {title}")
print("="*60)
if not tasks:
print("\n No tasks to display.")
print()
return
for i, task in enumerate(tasks, 1):
status_icon = 'β' if task['status'] == 'completed' else 'β'
print(f"\n{i}. [{status_icon}] {task['title']}")
print(f" ID: {task['id']}")
if task['description']:
print(f" {task['description']}")
print()
def display_summary(tasks):
"""
Display task statistics.
Args:
tasks (list): Task list
"""
total = len(tasks)
pending = len(get_pending_tasks(tasks))
completed = len(get_completed_tasks(tasks))
print("\n" + "="*60)
print(" TASK SUMMARY")
print("="*60)
print(f" Total Tasks: {total}")
print(f" Pending: {pending}")
print(f" Completed: {completed}")
if total > 0:
completion_rate = (completed / total) * 100
print(f" Completion Rate: {completion_rate:.1f}%")
print()
print("Core task manager implementation completed!")uuid.uuid4(). More reliable than sequential numbers, prevents conflicts when merging task lists.Interactive Command-Line Interface
The interactive interface provides menu-driven interaction with numbered options for common operations. It displays clear menus, accepts user input with validation, calls appropriate functions, and handles errors gracefully. The interface loop continues until user chooses to exit maintaining persistent session.
# Interactive Command-Line Interface
import os
import sys
# Import core functions
from task_manager_core import (
load_tasks, save_tasks, add_task, update_task,
delete_task, mark_complete, mark_pending,
get_pending_tasks, get_completed_tasks,
display_tasks, display_task, display_summary,
get_task_by_id
)
def clear_screen():
"""Clear terminal screen."""
os.system('cls' if os.name == 'nt' else 'clear')
def display_menu():
"""Display main menu."""
print("\n" + "="*60)
print(" π TO-DO LIST MANAGER π")
print("="*60)
print("\n1. β Add New Task")
print("2. π View All Tasks")
print("3. β³ View Pending Tasks")
print("4. β
View Completed Tasks")
print("5. βοΈ Update Task")
print("6. β Mark Task Complete")
print("7. β Mark Task Pending")
print("8. ποΈ Delete Task")
print("9. π View Summary")
print("10. π Clear Screen")
print("0. πͺ Exit")
print("="*60)
def get_input(prompt, required=True):
"""
Get user input with validation.
Args:
prompt (str): Input prompt
required (bool): Whether input is required
Returns:
str: User input
"""
while True:
value = input(prompt).strip()
if not required or value:
return value
print("β This field is required. Please enter a value.")
def handle_add_task(tasks):
"""Handle adding new task."""
print("\n" + "="*60)
print(" ADD NEW TASK")
print("="*60)
title = get_input("\nTask title: ", required=True)
description = get_input("Description (optional): ", required=False)
try:
add_task(tasks, title, description)
except ValueError as e:
print(f"\nβ Error: {e}")
input("\nPress Enter to continue...")
def handle_view_tasks(tasks, filter_type='all'):
"""Handle viewing tasks."""
if filter_type == 'pending':
filtered = get_pending_tasks(tasks)
title = "Pending Tasks"
elif filter_type == 'completed':
filtered = get_completed_tasks(tasks)
title = "Completed Tasks"
else:
filtered = tasks
title = "All Tasks"
display_tasks(filtered, title)
input("Press Enter to continue...")
def handle_update_task(tasks):
"""Handle updating task."""
print("\n" + "="*60)
print(" UPDATE TASK")
print("="*60)
# Show current tasks
display_tasks(tasks, "Select Task to Update")
if not tasks:
input("Press Enter to continue...")
return
task_id = get_input("\nEnter task ID: ", required=True)
# Check if task exists
task = get_task_by_id(tasks, task_id)
if not task:
print(f"\nβ Task with ID '{task_id}' not found")
input("\nPress Enter to continue...")
return
# Display current task
print("\nCurrent task:")
display_task(task)
# Get new values
print("\nEnter new values (leave blank to keep current):")
new_title = get_input(f"Title [{task['title']}]: ", required=False)
new_description = get_input(f"Description [{task['description']}]: ", required=False)
# Update
update_task(
tasks,
task_id,
title=new_title if new_title else None,
description=new_description if new_description else None
)
input("\nPress Enter to continue...")
def handle_mark_complete(tasks):
"""Handle marking task as complete."""
print("\n" + "="*60)
print(" MARK TASK AS COMPLETE")
print("="*60)
# Show pending tasks
pending = get_pending_tasks(tasks)
display_tasks(pending, "Pending Tasks")
if not pending:
input("Press Enter to continue...")
return
task_id = get_input("\nEnter task ID to mark complete: ", required=True)
mark_complete(tasks, task_id)
input("\nPress Enter to continue...")
def handle_mark_pending(tasks):
"""Handle marking task as pending."""
print("\n" + "="*60)
print(" MARK TASK AS PENDING")
print("="*60)
# Show completed tasks
completed = get_completed_tasks(tasks)
display_tasks(completed, "Completed Tasks")
if not completed:
input("Press Enter to continue...")
return
task_id = get_input("\nEnter task ID to mark pending: ", required=True)
mark_pending(tasks, task_id)
input("\nPress Enter to continue...")
def handle_delete_task(tasks):
"""Handle deleting task."""
print("\n" + "="*60)
print(" DELETE TASK")
print("="*60)
# Show all tasks
display_tasks(tasks, "Select Task to Delete")
if not tasks:
input("Press Enter to continue...")
return
task_id = get_input("\nEnter task ID to delete: ", required=True)
# Confirm deletion
task = get_task_by_id(tasks, task_id)
if task:
print("\nTask to delete:")
display_task(task)
confirm = get_input("\nAre you sure? (yes/no): ", required=True)
if confirm.lower() in ['yes', 'y']:
delete_task(tasks, task_id)
else:
print("\nβ Deletion cancelled")
input("\nPress Enter to continue...")
def handle_summary(tasks):
"""Handle displaying summary."""
display_summary(tasks)
input("Press Enter to continue...")
def main():
"""Main application loop."""
# Load tasks
tasks = load_tasks()
print("\nβ Loaded {} tasks".format(len(tasks)))
input("Press Enter to continue...")
while True:
clear_screen()
display_menu()
choice = get_input("\nEnter choice (0-10): ", required=True)
if choice == '0':
# Exit
print("\n" + "="*60)
print(" Thank you for using To-Do List Manager!")
print(" Your tasks have been saved.")
print(" Goodbye! π")
print("="*60 + "\n")
break
elif choice == '1':
handle_add_task(tasks)
elif choice == '2':
handle_view_tasks(tasks, 'all')
elif choice == '3':
handle_view_tasks(tasks, 'pending')
elif choice == '4':
handle_view_tasks(tasks, 'completed')
elif choice == '5':
handle_update_task(tasks)
elif choice == '6':
handle_mark_complete(tasks)
elif choice == '7':
handle_mark_pending(tasks)
elif choice == '8':
handle_delete_task(tasks)
elif choice == '9':
handle_summary(tasks)
elif choice == '10':
clear_screen()
else:
print("\nβ Invalid choice! Please select 0-10.")
input("\nPress Enter to continue...")
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("\n\nβ Application interrupted. Tasks saved.\n")
sys.exit(0)
except Exception as e:
print(f"\nβ Unexpected error: {e}")
sys.exit(1)KeyboardInterrupt (Ctrl+C) gracefully, save data, and display friendly exit message. Improves user experience.Complete Application with Enhancements
The complete application adds advanced features including search functionality finding tasks by keyword, priority levels marking task importance, due dates with reminders, task categories for organization, and export capabilities generating reports. These enhancements demonstrate practical extensions building upon core functionality.
# Complete Application with Enhancements
import json
from datetime import datetime, timedelta
# === Enhanced task structure ===
"""
Enhanced Task Structure:
{
'id': 'unique-uuid',
'title': 'Task title',
'description': 'Description',
'status': 'pending' or 'completed',
'priority': 'low', 'medium', or 'high',
'category': 'work', 'personal', etc.,
'due_date': 'YYYY-MM-DD' or None,
'created_at': 'YYYY-MM-DD HH:MM:SS',
'completed_at': 'YYYY-MM-DD HH:MM:SS' or None
}
"""
# === Search functionality ===
def search_tasks(tasks, keyword):
"""
Search tasks by keyword.
Args:
tasks (list): Task list
keyword (str): Search keyword
Returns:
list: Matching tasks
"""
keyword = keyword.lower()
results = []
for task in tasks:
if (keyword in task['title'].lower() or
keyword in task.get('description', '').lower() or
keyword in task.get('category', '').lower()):
results.append(task)
return results
# === Priority management ===
def get_priority_tasks(tasks, priority):
"""
Get tasks by priority.
Args:
tasks (list): Task list
priority (str): Priority level
Returns:
list: Tasks with specified priority
"""
return [task for task in tasks
if task.get('priority', 'medium') == priority]
def sort_by_priority(tasks):
"""
Sort tasks by priority (high, medium, low).
Args:
tasks (list): Task list
Returns:
list: Sorted tasks
"""
priority_order = {'high': 0, 'medium': 1, 'low': 2}
return sorted(tasks,
key=lambda x: priority_order.get(x.get('priority', 'medium'), 1))
# === Due date management ===
def get_overdue_tasks(tasks):
"""
Get overdue tasks.
Args:
tasks (list): Task list
Returns:
list: Overdue pending tasks
"""
today = datetime.now().date()
overdue = []
for task in tasks:
if task['status'] == 'pending' and task.get('due_date'):
try:
due_date = datetime.strptime(task['due_date'], '%Y-%m-%d').date()
if due_date < today:
overdue.append(task)
except ValueError:
pass
return overdue
def get_upcoming_tasks(tasks, days=7):
"""
Get tasks due within specified days.
Args:
tasks (list): Task list
days (int): Number of days to look ahead
Returns:
list: Upcoming tasks
"""
today = datetime.now().date()
future = today + timedelta(days=days)
upcoming = []
for task in tasks:
if task['status'] == 'pending' and task.get('due_date'):
try:
due_date = datetime.strptime(task['due_date'], '%Y-%m-%d').date()
if today <= due_date <= future:
upcoming.append(task)
except ValueError:
pass
return upcoming
# === Category management ===
def get_categories(tasks):
"""
Get unique categories from tasks.
Args:
tasks (list): Task list
Returns:
list: Unique categories
"""
categories = set()
for task in tasks:
if task.get('category'):
categories.add(task['category'])
return sorted(list(categories))
def get_tasks_by_category(tasks, category):
"""
Get tasks in specific category.
Args:
tasks (list): Task list
category (str): Category name
Returns:
list: Tasks in category
"""
return [task for task in tasks
if task.get('category', '').lower() == category.lower()]
# === Export functionality ===
def export_to_text(tasks, filename='tasks_export.txt'):
"""
Export tasks to text file.
Args:
tasks (list): Task list
filename (str): Export filename
Returns:
bool: True if successful
"""
try:
with open(filename, 'w', encoding='utf-8') as f:
f.write("TO-DO LIST EXPORT\n")
f.write("=" * 60 + "\n")
f.write(f"Exported: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Total Tasks: {len(tasks)}\n")
f.write("=" * 60 + "\n\n")
for i, task in enumerate(tasks, 1):
status = 'β' if task['status'] == 'completed' else 'β'
f.write(f"{i}. [{status}] {task['title']}\n")
f.write(f" ID: {task['id']}\n")
if task.get('priority'):
f.write(f" Priority: {task['priority'].upper()}\n")
if task.get('category'):
f.write(f" Category: {task['category']}\n")
if task.get('due_date'):
f.write(f" Due: {task['due_date']}\n")
if task.get('description'):
f.write(f" Description: {task['description']}\n")
f.write(f" Created: {task['created_at']}\n")
if task.get('completed_at'):
f.write(f" Completed: {task['completed_at']}\n")
f.write("\n")
print(f"\nβ Tasks exported to {filename}")
return True
except Exception as e:
print(f"\nβ Export failed: {e}")
return False
# === Statistics ===
def generate_statistics(tasks):
"""
Generate detailed statistics.
Args:
tasks (list): Task list
Returns:
dict: Statistics dictionary
"""
stats = {
'total': len(tasks),
'pending': len([t for t in tasks if t['status'] == 'pending']),
'completed': len([t for t in tasks if t['status'] == 'completed']),
'overdue': len(get_overdue_tasks(tasks)),
'high_priority': len(get_priority_tasks(tasks, 'high')),
'categories': len(get_categories(tasks))
}
if stats['total'] > 0:
stats['completion_rate'] = (stats['completed'] / stats['total']) * 100
else:
stats['completion_rate'] = 0
return stats
def display_statistics(tasks):
"""
Display detailed statistics.
Args:
tasks (list): Task list
"""
stats = generate_statistics(tasks)
print("\n" + "="*60)
print(" DETAILED STATISTICS")
print("="*60)
print(f" Total Tasks: {stats['total']}")
print(f" Pending: {stats['pending']}")
print(f" Completed: {stats['completed']}")
print(f" Overdue: {stats['overdue']}")
print(f" High Priority: {stats['high_priority']}")
print(f" Categories: {stats['categories']}")
print(f" Completion Rate: {stats['completion_rate']:.1f}%")
print()
print("Enhanced features implementation completed!")Development Best Practices
- Validate all user input: Check for empty strings, invalid IDs, and malformed data. Provide clear error messages guiding users to correct input
- Handle file operations safely: Use try-except blocks catching FileNotFoundError and JSONDecodeError. Create backup before overwriting data
- Separate concerns: Organize code into modules - data operations, file I/O, user interface. Improves maintainability and testing
- Use consistent data structure: Define clear task dictionary schema. Document required and optional fields. Validate structure when loading
- Implement proper ID system: Use UUIDs preventing ID conflicts. Never reuse IDs even after deletion. Makes data merging possible
- Add timestamps: Record creation and modification times. Enables sorting by date, tracking task age, and audit trails
- Provide user feedback: Show success/error messages with icons (β, β, β ). Use clear language describing what happened and why
- Support undo operations: Implement marking completed tasks back to pending. Consider adding comprehensive undo/redo system
- Format output clearly: Use consistent formatting, visual separators, and icons. Make interface scannable and intuitive
- Write tests: Create unit tests for CRUD operations, edge cases, and error handling. Ensures reliability during enhancements
Conclusion
Building to-do list application demonstrates essential programming concepts through practical project. The implementation includes task data structure using dictionaries with id generated by uuid, title and description for content, status tracking pending or completed, timestamps for created_at and completed_at, and optional fields for priority, category, and due_date, file persistence with JSON using json.dump() serializing tasks to file with indent for readability, json.load() deserializing from file, error handling for corrupted files and missing data, and atomic writes preventing partial corruption, CRUD operations with create_task() generating new tasks with validation, add_task() appending to list and saving, get_task_by_id() finding specific tasks, update_task() modifying existing content, delete_task() removing by ID, mark_complete() and mark_pending() toggling status, and filtering functions for status, priority, and categories, and interactive CLI with menu-driven interface displaying numbered options, input validation ensuring data quality, clear user feedback with success/error messages, screen clearing for better UX, and keyboard interrupt handling for graceful exit.
Key learning outcomes include data structures using dictionaries for task representation, lists storing collections with append() and remove(), list comprehension filtering with conditions, file operations opening files with context managers, reading and writing JSON, handling file exceptions, working with datetime using datetime.now() for timestamps, strftime() formatting dates, parsing date strings with strptime(), and calculating date differences, function organization creating focused single-purpose functions, separating data and presentation logic, using return values effectively, exception handling catching specific exceptions like ValueError and FileNotFoundError, providing helpful error messages, and preventing crashes, user interface design creating intuitive menus, validating input appropriately, providing visual feedback with icons and formatting. Enhanced features include search functionality finding tasks by keyword in title, description, or category, priority management with low, medium, high levels, sorting by priority importance, due date tracking identifying overdue tasks, showing upcoming deadlines, category organization grouping related tasks, filtering by category, export capabilities generating text reports, creating backups, and statistics calculating completion rates, tracking overdue counts, analyzing by priority and category. This project provides foundation for web applications building Flask or Django versions, mobile applications creating React Native or Flutter apps, desktop GUI using Tkinter or PyQt, database integration replacing JSON with SQLite or PostgreSQL, and cloud synchronization adding online storage, demonstrating how basic concepts combine into functional software while teaching practical development skills applicable across programming domains.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


