Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions python/.tasks.txt
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't add random files to the repo. It pollutes the software, so that someone cloning would then have your task list. See .gitignore to add these so you can't add them to the repo

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Empty file added python/SEPwC_formative
Empty file.
Binary file not shown.
Binary file not shown.
Binary file added python/__pycache__/todo.cpython-313.pyc
Binary file not shown.
Binary file not shown.
66 changes: 66 additions & 0 deletions python/test_todo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import unittest
from unittest.mock import patch
from todo import add_task, list_tasks, remove_task, TASK_FILE

class TestTodoFunctions(unittest.TestCase):

def setUp(self):
"""Ensure an empty task file before each test."""
if os.path.exists(TASK_FILE):
os.remove(TASK_FILE)

def tearDown(self):
"""Clean up the task file after each test."""
if os.path.exists(TASK_FILE):
os.remove(TASK_FILE)

def test_add_task(self):
"""Test that a task is added correctly."""
add_task("First")
self.assertTrue(os.path.exists(TASK_FILE)) # Check if the file exists

with open(TASK_FILE, "r") as file:
tasks = file.readlines()

self.assertEqual(len(tasks), 1) # This checks that the numbers of tasks added is 1 (as only added one)
self.assertEqual(tasks[0].strip(), "First") # This checks that the task added is the same as the one that was added

def test_list_tasks(self):
"""Test printing all the tasks."""
add_task("First")
add_task("Second")

tasks = list_tasks()

self.assertIn("1. First", tasks) # Check if the first task is in the list
self.assertIn("2. Second", tasks) # Check if the second task is in the list

def test_remove_task(self):
"""Test removing a task in the middle of the list."""
add_task("First")
add_task("Second")
add_task("Third")

remove_task(2)

with open(TASK_FILE, "r") as file:
tasks = file.readlines()

self.assertEqual(len(tasks), 2)
self.assertEqual(tasks[1].strip(), "Third") # Check that the second task is now the third task because the middle task was removed

def test_remove_task_invalid_index(self):
"""Test removing a task with an invalid index."""
add_task("First")
with patch("builtins.print") as mock_print:
remove_task(5) # Invalid index out of range
mock_print.assert_called_with("Invalid task number. Please choose between 1 and 1.")

with open(TASK_FILE, "r") as file:
tasks = file.readlines()
self.assertEqual(len(tasks), 1)
self.assertEqual(tasks[0].strip(), "First")

if __name__ == "__main__":
unittest.main()
59 changes: 52 additions & 7 deletions python/todo.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,67 @@
"""A command-line to-do list application that supports adding, listing, and removing tasks"""

import argparse
import os

TASK_FILE = ".tasks.txt"
NO_TASKS_FOUND_MSG = "No tasks found. Please add a task using -a or --add option."
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice :-)


def add_task(task):
"""Function: add_task

Input - a task to add to the list
Return - nothing
"""
"""Add new task to task file"""
with open(TASK_FILE, "a", encoding="utf-8") as file:
file.write(task + "\n")

def list_tasks():
return
"""Read tasks from task file and return them as a numbered list"""
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try not to do multiple things in the same commit. The edits to the method comments should be separate to the addition of the remove_tasks function

if not os.path.exists(TASK_FILE):
return NO_TASKS_FOUND_MSG

with open(TASK_FILE, "r", encoding="utf-8") as file:
lines = [line.strip() for line in file if line.strip()]

if not lines:
return NO_TASKS_FOUND_MSG

numbered_tasks = [f"{idx + 1}. {line}" for idx, line in enumerate(lines)]
return "\n".join(numbered_tasks)

def remove_task(index):
return
"""Remove a task by its number from the task file"""
if not os.path.exists(TASK_FILE):
print(NO_TASKS_FOUND_MSG)
return

# Read all non-empty lines from the file
# strip() to remove any leading/trailing whitespace
with open(TASK_FILE, "r", encoding="utf-8") as file:
tasks = [line.strip() for line in file if line.strip()]

if not tasks:
print("No tasks to remove.")
return

# Check for valid input from user
if index < 1 or index > len(tasks):
print(f"Invalid task number. Please choose between 1 and {len(tasks)}.")
return

# Remove the specified task
removed = tasks.pop(index - 1)

# Write remaining tasks back to the file
if tasks:
with open(TASK_FILE, "w", encoding="utf-8") as file:
file.write("\n".join(tasks) + "\n")
print(f"Task '{removed}' removed successfully.")
print("Remaining tasks:")
for i, task in enumerate(tasks, 1):
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not call list tasks, rather than duplicate code?

print(f"{i}. {task}")
else:
os.remove(TASK_FILE)
print(f"Task '{removed}' removed. No tasks left. File deleted.")

def main():
"""Parse the command-line arguments and run the selected task operation"""
parser = argparse.ArgumentParser(description="Command-line Todo List")
parser.add_argument(
"-a",
Expand All @@ -38,6 +82,7 @@ def main():

if args.add:
add_task(args.add)
print (f"Task '{args.add}' added successfully.")
elif args.list:
tasks = list_tasks()
print(tasks)
Expand Down