Building a Personal Task Manager in Python: Journey, Functionality & Debugging

Sagnik DevSagnik Dev
4 min read

Introduction

As a beginner learning Python, I wanted to build a project that ties together many concepts I’ve recently learned — classes, decorators, file handling, JSON, exception handling, and more. This led me to create a Personal Task Manager, a simple yet functional CLI app that helps manage tasks with categories, timestamps, and status tracking.

In this article, I have shared my journey building it, explained how it works under the hood, and discussed some of the bugs I faced and fixed along the way. You can check the project out on GitHub here:
🔗 Personal Task Manager on GitHub

Why This Project?

I wanted something simple but meaningful. A task manager checked all the boxes:

  • Helps reinforce OOP concepts

  • Involves file operations and JSON

  • Uses decorators, timestamps, and error handling

  • Can be run from the command line, mimicking real-world utility

Features of the Task Manager

  • Add tasks with categories and timestamps

  • View all tasks with status and metadata

  • Mark tasks as done

  • Delete tasks

  • View tasks filtered by category

  • Persistent storage with JSON files

  • Logging decorator to track method calls

How It Works

The whole logic is organized in a clean TaskManager class (inside taskmanager.py) with the following flow:

  1. Tasks are stored in a list of dictionaries.

  2. Each task has attributes like task, done, timestamp, and category.

  3. All actions (add, delete, mark done, view) call a central save_tasks() to update the tasks.json file.

  4. A decorator wraps key functions to log what’s being executed.

  5. The run.py script allows users to interact via a simple CLI.

Going Underneath: How Each Action Works

1. Add Task

When you add a task, the program creates a dictionary containing the task description, a “done” status set to False, the current timestamp, and the category. This dictionary is appended to a list of tasks, which is then saved to a JSON file to persist data.

2. View Tasks

The program checks if any tasks exist and iterates through each one, displaying its index, completion status, task text, category, and timestamp in a neat, readable format.

3. Mark as Done

To mark a task done, the program updates the “done” flag for the task at the specified index, then saves the updated list back to the file.

4. Delete Task

Deletion removes the task from the internal list by index and saves the updated list to the file.

5. View by Category

Filtering by category scans all tasks and prints only those that match the user’s chosen category, ignoring case sensitivity.

6. Logger Decorator

A custom decorator wraps key methods to print log messages when they are called. This helped me track program flow during development and debugging.

7. File Operations

The program reads from and writes to a tasks.json file using Python’s json module. It gracefully handles missing or corrupted files by starting fresh if needed.

Bugs and Debugging

Fixing KeyError Due to Inconsistent Task Data Keys in JSON Storage:

One major bug I encountered was regarding incorrect use of dictionary keys — specifically, a KeyError caused by mismatched key names like "task" vs "title". Learned about the importance of consistent naming and carefully checking data structures.*

Another challenge was ensuring the program didn’t crash when the task file was missing or empty. Adding exception handling around file loading solved this, letting the program start with an empty list gracefully.

I also implemented a logging decorator that prints function calls, which made it easier to trace execution during testing.

*The program faced a runtime KeyError because it tried to access a key ("task") in task dictionaries that wasn’t always present. This happened when older saved data used a different key name ("title") for the task description. The inconsistency in data structure caused the code to break during task display or manipulation. To resolve this, the fix involved updating the data loading logic to detect and normalize these key differences—specifically, converting any "title" keys to "task" when loading tasks from the JSON file. This ensured all tasks followed a consistent format, preventing errors and improving robustness when handling persisted data.

Final Thoughts

This project was a great learning experience to combine many Python concepts into a useful tool. I’m excited to keep improving it, perhaps adding a GUI or integrating with online task services.

Feel free to check out my code on GitHub, and reach out if you have any tips or feedback!

0
Subscribe to my newsletter

Read articles from Sagnik Dev directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sagnik Dev
Sagnik Dev