Project Overview and Data Layer
Let's put everything you've learned together by building a command-line Task Manager application. This project uses file handling, classes, error handling, and user input to create a complete CRUD (Create, Read, Update, Delete) application.
We'll store tasks in a JSON file so they persist between sessions. Each task will have an ID, title, description, status, and creation date.
- Add new tasks with a title and description
- View all tasks or filter by status (pending/completed)
- Mark tasks as completed
- Delete tasks
- Data persists in a JSON file between runs
import json
import os
from datetime import datetime
DATA_FILE = "tasks.json"
class Task:
"""Represents a single task."""
def __init__(self, task_id, title, description="", completed=False, created_at=None):
self.task_id = task_id
self.title = title
self.description = description
self.completed = completed
self.created_at = created_at or datetime.now().strftime("%Y-%m-%d %H:%M")
def to_dict(self):
"""Convert task to a dictionary for JSON storage."""
return {
"id": self.task_id,
"title": self.title,
"description": self.description,
"completed": self.completed,
"created_at": self.created_at
}
@classmethod
def from_dict(cls, data):
"""Create a Task from a dictionary."""
return cls(
task_id=data["id"],
title=data["title"],
description=data.get("description", ""),
completed=data.get("completed", False),
created_at=data.get("created_at")
)
def __str__(self):
status = "Done" if self.completed else "Pending"
return f"[{self.task_id}] [{status}] {self.title}" JavaScript
# Simplified Task class demo
class Task:
def __init__(self, task_id, title, completed=False):
self.task_id = task_id
self.title = title
self.completed = completed
def __str__(self):
status = "Done" if self.completed else "Pending"
return f"[{self.task_id}] [{status}] {self.title}"
def to_dict(self):
return {"id": self.task_id, "title": self.title, "completed": self.completed}
# Create tasks
tasks = [
Task(1, "Learn Python basics", True),
Task(2, "Build a project", False),
Task(3, "Practice daily", False)
]
for task in tasks:
print(task) - The @classmethod decorator creates an alternative constructor. Task.from_dict(data) creates a Task object from a dictionary, which is useful when loading from JSON.
Task Manager Class with CRUD Operations
The TaskManager class handles all operations: loading and saving tasks from the JSON file, adding new tasks, listing them, completing them, and deleting them.
This class encapsulates all the data management logic, keeping the main program loop clean and focused on user interaction.
class TaskManager:
"""Manages a collection of tasks with file persistence."""
def __init__(self, filepath=DATA_FILE):
self.filepath = filepath
self.tasks = []
self.load_tasks()
def load_tasks(self):
"""Load tasks from the JSON file."""
if os.path.exists(self.filepath):
try:
with open(self.filepath, "r") as f:
data = json.load(f)
self.tasks = [Task.from_dict(t) for t in data]
except (json.JSONDecodeError, KeyError) as e:
print(f"Warning: Could not load tasks ({e}). Starting fresh.")
self.tasks = []
else:
self.tasks = []
def save_tasks(self):
"""Save all tasks to the JSON file."""
with open(self.filepath, "w") as f:
json.dump([t.to_dict() for t in self.tasks], f, indent=2)
def _next_id(self):
"""Generate the next available task ID."""
if not self.tasks:
return 1
return max(t.task_id for t in self.tasks) + 1
def add_task(self, title, description=""):
"""Add a new task and save."""
task = Task(self._next_id(), title, description)
self.tasks.append(task)
self.save_tasks()
print(f"Task added: {task}")
return task
def list_tasks(self, show_all=True):
"""Display tasks. If show_all is False, show only pending."""
filtered = self.tasks if show_all else [t for t in self.tasks if not t.completed]
if not filtered:
print("No tasks found.")
return
for task in filtered:
desc = f" - {task.description}" if task.description else ""
print(f" {task}{desc}")
print(f"\n Total: {len(filtered)} task(s)")
def complete_task(self, task_id):
"""Mark a task as completed."""
for task in self.tasks:
if task.task_id == task_id:
task.completed = True
self.save_tasks()
print(f"Completed: {task.title}")
return
print(f"Task #{task_id} not found.")
def delete_task(self, task_id):
"""Delete a task by ID."""
for i, task in enumerate(self.tasks):
if task.task_id == task_id:
removed = self.tasks.pop(i)
self.save_tasks()
print(f"Deleted: {removed.title}")
return
print(f"Task #{task_id} not found.") JavaScript
# Simulated TaskManager demo (without file I/O)
class Task:
def __init__(self, tid, title, done=False):
self.tid = tid
self.title = title
self.done = done
def __str__(self):
s = "Done" if self.done else "Pending"
return f"[{self.tid}] [{s}] {self.title}"
tasks = []
next_id = 1
def add(title):
global next_id
t = Task(next_id, title)
tasks.append(t)
next_id += 1
print(f"Added: {t}")
add("Learn Python")
add("Build a project")
add("Get hired")
tasks[0].done = True
print("\nAll tasks:")
for t in tasks:
print(f" {t}") - The save_tasks() method is called after every modification to ensure data persists. In a larger application, you might batch writes for performance.
The Main Program Loop
The main function ties everything together with a menu-driven interface. It uses a while loop to keep the program running until the user chooses to quit, and handles invalid input gracefully.
This is the complete entry point. Save all three sections into a single file (task_manager.py) and run it with 'python task_manager.py'.
def main():
"""Main application loop."""
manager = TaskManager()
print("=== Task Manager ===")
print("Type 'help' for available commands.\n")
while True:
command = input("\n> ").strip().lower()
if command == "help":
print("Commands:")
print(" add — Add a new task")
print(" list — Show all tasks")
print(" pending — Show pending tasks only")
print(" done ID — Mark task as completed")
print(" delete ID— Delete a task")
print(" quit — Exit the program")
elif command == "add":
title = input("Task title: ").strip()
if not title:
print("Title cannot be empty.")
continue
desc = input("Description (optional): ").strip()
manager.add_task(title, desc)
elif command == "list":
manager.list_tasks(show_all=True)
elif command == "pending":
manager.list_tasks(show_all=False)
elif command.startswith("done "):
try:
task_id = int(command.split()[1])
manager.complete_task(task_id)
except (IndexError, ValueError):
print("Usage: done <task_id>")
elif command.startswith("delete "):
try:
task_id = int(command.split()[1])
manager.delete_task(task_id)
except (IndexError, ValueError):
print("Usage: delete <task_id>")
elif command in ("quit", "exit", "q"):
print("Goodbye!")
break
else:
print("Unknown command. Type 'help' for options.")
if __name__ == "__main__":
main() JavaScript
# Complete Task Manager — save as task_manager.py
# and run with: python task_manager.py
#
# Sample session:
# > add
# Task title: Learn Python
# Description: Complete all 25 lessons
# Task added: [1] [Pending] Learn Python
#
# > add
# Task title: Build project
# Description: Create task manager app
# Task added: [2] [Pending] Build project
#
# > list
# [1] [Pending] Learn Python - Complete all 25 lessons
# [2] [Pending] Build project - Create task manager app
# Total: 2 task(s)
#
# > done 1
# Completed: Learn Python
#
# > list
# [1] [Done] Learn Python - Complete all 25 lessons
# [2] [Pending] Build project - Create task manager app
# Total: 2 task(s)
#
# > quit
# Goodbye! - Congratulations on completing the Python course! You've learned variables, data types, control flow, data structures, functions, OOP, file handling, error handling, decorators, APIs, and best practices. Keep building projects to solidify your skills!
