Day 2: To-Do List Interface(With Lottie) In Python

Welcome to Day Two of my 21-day project series! Today I’ll make a to-do list interface in python that plays Lottie animation on finishing tasks.

Because:

Glitter makes people happy.

This small reward system can potentially increase our chances of getting more stuff done.

Project Contents

I’ll just make a base class with a text-based interaction interface. It wouldn’t be able to store anything in the memory.

This class and its interface would provide the facility to add tasks, update them, finish them, delete them, and view the list of tasks in our To-do list.

I wouldn’t get into the step-by-step explanation of the code as the article becomes super long. (And I guess boring too.)

But I would post an explanatory article for it soon.

Full Source Code

import webbrowser
class TaskAlreadyExistsError(Exception):
    """Exception raised when a task already exists in the list."""
    def __init__(self, message):
        super().__init__(message)

class TaskNotFoundError(Exception):
    """Exception raised when a task is not found in the list."""
    def __init__(self, message):
        super().__init__(message)

class ToDo:
    """Class representing a to-do list."""

    def __init__(self) -> None:
        """Initialize an empty to-do list. Which is a dictionary"""
        self.tasks = {}

    def add_task(self, task:str, deadline:str) -> str:
        """Add a new task to the list.

        Args:
            task (str): The task description.
            deadline (str): The deadline in dd-mm-yy format.

        Returns:
            str: A confirmation message indicating that the task has been added.

        Raises:
            TaskAlreadyExistsError: If the task already exists in the list.
            TypeError: If the task parameter is not a string.
            ValueError: If the task parameter is an empty string.
        """
        if not isinstance(task, str):
            raise TypeError("Task must be a string.")
        if not task:
            raise ValueError("Task cannot be an empty string.")
        if task in self.tasks.keys():
            raise TaskAlreadyExistsError(f"{task} already exists in the list.")
        else:
            self.tasks[task] = {'status':False, 'deadline':deadline}
            print(self.tasks)
            return f"Task has been added to the list."
        
    def finish_task(self, task:str) -> str:
        """
        Mark a task as finished and open a web page for celebration.

        Args:
            task (str): The task to mark as finished.

        Returns:
            str: A message confirming that the task has been marked as finished.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            self.tasks[task]['status'] = True
            webbrowser.open("well_done.html")
            return "Task finished"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def remove_task(self, task:str) ->str:
        """
        Remove a task from the task list.

        Args:
            task (str): The task to remove.

        Returns:
            str: A message confirming that the task has been removed.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            del self.tasks[task]
            return "Task removed"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def update_task(self, task:str, new_deadline:str) -> str:
        """
        Update the status and deadline of a task.

        Args:
            task (str): The task to update.
            new_deadline (str): The new deadline for the task in dd-mm-yy format.

        Returns:
            str: A message confirming that the task has been updated.

        Raises:
            TypeError: If the task is not a string.
            TaskNotFoundError: If the task does not exist in the list.
        """
        try:
            if not isinstance(task, str):
                raise TypeError("Task must be a string.")
            self.tasks[task] = {'status': False, 'deadline': new_deadline}
            return "Task updated"
        except Exception:
            raise TaskNotFoundError("No such task in the list.")
    
    def view_tasks(self) -> list:
        """View the tasks in the list.

        Returns:
            list: A list of task descriptions and their status or completion.

        Raises:
            TaskNotFoundError: If there are no tasks in the list.
        """
        try:
            task_list = []
            for task in self.tasks:
                if self.tasks[task]['status'] == False:
                    task_list.append(f"{task} by {self.tasks[task]['deadline']}")
                if self.tasks[task]['status'] == True:
                    task_list.append(f"{task} finished")
            return task_list
        except Exception:
            raise TaskNotFoundError("There are no tasks in the list.")

if __name__ == "__main__":
    app = ToDo()
    while True:
        print("-------------------------------")
        print()
        print("1. Add task")
        print("2. View tasks")
        print("3. delete task")
        print("4. update task")
        print("5. finish task")
        print("6. exit app")
        print()
        print("-------------------------------")

        choice = int(input("Enter your choice: "))
        if choice == 1:
            task = input("Enter task: ")
            deadline = input("Enter task deadline: ")
            try:
                message = app.add_task(task, deadline)
                print(message)
            except TypeError as e:
                print(e)
            except ValueError as e:
                print(e)
            except TaskAlreadyExistsError as e:
                print(e)
        elif choice == 2:
            try:
                print("-------------------------------")
                task_list = app.view_tasks()
                for task in task_list:
                    print(task)
                print("-------------------------------")
            except TaskNotFoundError as e:
                print(e)
        elif choice == 3:
            try:
                task = input("Enter the task you want to remove: ")
                message = app.remove_task(task)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 4:
            try:
                task = input("Enter the task you want to update: ")
                deadline = input("What is the new deadline? ")
                message = app.update_task(task,deadline)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 5:
            try:
                task = input("Enter the task you have finised: ")
                message = app.finish_task(task)
                print(message)
            except TypeError as e:
                print(e)
            except TaskNotFoundError as e:
                print(e)
        elif choice == 6:
            print("Thanks for using")
            break
        else:
            print("Invalid choice!")

HTML Code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Well Done Champ</title>
</head>
<body>
    <script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
<lottie-player src="https://assets6.lottiefiles.com/packages/lf20_pKiaUR.json"  background="transparent"  speed="1"  style="width: 300px; height: 300px;"  loop  autoplay></lottie-player>

</body>
</html>

The code is well documented and well written it would be easy to understand. Here are the links to topics within the mini-project. You can check them if you have any doubts or confusion:

So how is the To-Do List interface?

Possible Improvements (That I’ll add after 21 days)

  1. Permanent storage
  2. GUI
  3. Task Priority
  4. Categories/Tags
  5. Due Date Reminders
  6. Search and Filtering
  7. Task Completion Statistics
  8. Undo/Redo
  9. Color Coding
  10. Task Notes
  11. Making it a web app

And a lot more…

Experience of the day

In case you don’t know this project is a part of the brand new series that I have started. #21days21projects in python. To read more about it you can check out this page.

And for those who already know here’s my experience of the day.

So, my experience for the day was quite good however it was tough as I fell sick.

I didn’t feel like I would be able to get this done today and my 21 days challenge would end on day one.

But, I am happy that it didn’t happen.

Other than that I wanted to add a feature of permanent storage using a CSV file as a storage format but I was out of time.

I guess I would add this feature after the end of the challenge period. Would definitely share it later on.

So, tell me how was your day? Did it go well or did you miss out?

Well, it’s great if it went well. Here’s a mini-celebration for you.

But, don’t be sad if you missed out.

It’s okay to not be consistent as long as you will get back on track from tomorrow. Or even better if you try doing at least something today. It’s much better than not doing anything at all today. Even if you just create a variable it’s okay.

And here’s a mini-celebration for you too.

I hope from today this to-do list interface would help you stick to your goal…

Your suggestions are welcomed!

Happy coding…

And wait, if you have some ideas for the upcoming projects do let me know that too.

Leave a Reply