✦ Python Intermediate & OOP Level 2 final project

L2 · Concept 02 · Final Project: Task Manager

Your Task Manager
Comes to Life.

Build the methods that make a TaskManager useful while keeping its behavior predictable.

Learning goalExtend the existing TaskManager with predictable methods for finding, viewing, completing, and deleting Task objects.
01 Learn02 Work locally03 Check your understanding

Learning as a guest

Learning as a guest? Save your progress!

Create a free account to learn with Sloby AI, check your understanding, and track your progress.

The entire platform is free throughout 2026. Foundations stays free forever.

TaskManager manages the collection.

Task stores data for one task. TaskManager owns the list, so methods that search, change, or remove items belong on TaskManager.

def get_task(self, task_id: str) -> Task:
    for task in self.tasks:
        if task.task_id == task_id:
            return task
    raise ValueError("Task was not found.")

The loop examines each existing Task. A matching task_id returns the same object from the list. A missing ID raises ValueError, so every caller gets one clear, predictable result.

This lesson uses one policy: duplicate IDs are rejected with ValueError, and a missing ID also raises ValueError. Repeating complete_task() is safe: the task simply remains completed.

↳

Build on the existing method: add_task() remains the starting point. Before appending, it should check whether get_task() can already find the same ID and reject an ambiguous duplicate.

Changing an object already stored in the list
task = Task("task-1", "Read the plan")
manager = TaskManager()
manager.add_task(task)
task.completed = True

print(manager.tasks[0].completed)

The list stores a reference to the Task object, not a separate copy. When completed becomes True, the Task reached through manager.tasks[0] shows the same changed state.

Methods can build on each other
def complete_task(self, task_id: str) -> Task:
    task = self.get_task(task_id)
    task.completed = True
    return task

A small method can reuse get_task() instead of repeating the search loop. You will write the remaining methods yourself in the local project.

Choose predictable behavior

  1. View and findLet list_tasks() return the task list and get_task(task_id) return one matching Task.
  2. Complete safelyLet complete_task() set completed to True. Calling it again should not duplicate or break the task.
  3. Delete deliberatelyLet delete_task() remove the matching Task after it is found. A later lookup for that ID should raise ValueError.

Common task-management mistakes

  • Allowing duplicate task IDsTwo different tasks with one ID make get_task() ambiguous. Reject the duplicate before changing the list.
  • Returning a hidden failureDo not silently return an unrelated Task for a missing ID. Use the same clear ValueError policy in each operation.

Make Your Task Manager Useful

Do this in VS Code

Continue in the extracted task_manager_starter folder from Concept 28. The lesson diagrams call the project task_manager, but you should keep working in the same starter folder. Do not create a new project or download a new ZIP.

Continue your existing project
task_manager/
├── main.py
├── models/task.py
└── services/task_manager.py

Keep Task and the existing add_task() method. Extend TaskManager step by step, then demonstrate the behavior in main.py.

Extend the existing project

  1. Inspect the foundationOpen services/task_manager.py and read add_task() before adding another method.
  2. Implement one method at a timeAdd list_tasks(), get_task(), complete_task(), and delete_task(). Decide where each operation should use get_task().
  3. Demonstrate the behaviorUpdate main.py, run it, and compare the results with the scenarios below.
    python3 main.py
    # or: python main.py
↳

Manual checks: two different tasks → find one → complete it twice → delete it Also try a missing ID and a duplicate ID. Both should follow the ValueError policy you chose.

Before asking Sloby for help

  • Trace the task IDPrint task_id values while you follow the list. Check spelling before changing the loop.

What will this program print?

Follow the same Task object through the manager list.

task = Task("task-1", "Read the plan")
manager = TaskManager()
manager.add_task(task)
task.completed = True

print(manager.tasks[0].completed)
Choose the output

Build confidence independently

Show only pending tasks

Add a pending_tasks() method that returns tasks whose completed state is False. Think about filtering the existing list before writing code.

Your goalDemonstrate two different tasks, complete one, and show that pending_tasks() returns only the unfinished Task. Keep duplicate and missing-ID behavior predictable.
task_manager/
├── main.py
└── services/task_manager.py

Work in your local VS Code folder

Progressive hints
  • Start with an empty result list.
  • Inspect task.completed for every task in self.tasks.
  • Append only tasks whose completed value is False.

Ask Sloby for one guiding question about filtering or method responsibilities. Do not ask it to write the completed TaskManager for you.

Sloby, the Python AI Tutor mascot

Your learning mentor

Sign in to ask Sloby

Create a free account or sign in to get AI guidance while you learn.

Sign in to check your understanding

Create a free account or sign in to receive AI feedback and save your learning progress.

Python AI Tutor · Learn by understanding, not just by running code.