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
222 changes: 215 additions & 7 deletions task_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@

import re
import datetime
from enum import Enum


class TaskStatus(Enum):
TODO = "待办"
IN_PROGRESS = "进行中"
COMPLETED = "已完成"
CANCELLED = "已取消"


STATUS_TRANSITIONS = {
TaskStatus.TODO: [TaskStatus.IN_PROGRESS, TaskStatus.CANCELLED],
TaskStatus.IN_PROGRESS: [TaskStatus.TODO, TaskStatus.COMPLETED, TaskStatus.CANCELLED],
TaskStatus.COMPLETED: [TaskStatus.IN_PROGRESS],
TaskStatus.CANCELLED: [TaskStatus.TODO],
}


class TaskManager:
def __init__(self, filename="data.txt"):
Expand All @@ -14,6 +31,77 @@ def create_backup(self):
shutil.copy(self.filename, backup_file)
print(f"📁 Backup created as: {backup_file}")

def _validate_status(self, status):
try:
return TaskStatus(status)
except ValueError:
return None

def _validate_progress(self, progress):
try:
p = int(progress)
if 0 <= p <= 100:
return p
return None
except ValueError:
return None

def _can_transition_to(self, current_status, new_status):
if current_status in STATUS_TRANSITIONS:
return new_status in STATUS_TRANSITIONS[current_status]
return False

def _get_available_transitions(self, current_status):
if current_status in STATUS_TRANSITIONS:
return STATUS_TRANSITIONS[current_status]
return []

def _parse_tasks(self, lines):
tasks = []
current_task = {}
for line in lines:
line = line.strip()
if line == "==========================":
if current_task:
tasks.append(current_task)
current_task = {}
elif ":" in line:
key, value = line.split(":", 1)
key = key.strip()
value = value.strip()
current_task[key] = value
if current_task:
tasks.append(current_task)
return tasks

def _task_to_lines(self, task):
lines = []
lines.append(f"Title:{task.get('Title', '')}\n")
lines.append(f"Description:{task.get('Description', '')}\n")
lines.append(f"Deadline:{task.get('Deadline', '')}\n")
lines.append(f"Priority:{task.get('Priority', '')}\n")
lines.append(f"Category:{task.get('Category', '')}\n")
lines.append(f"Status:{task.get('Status', '待办')}\n")
lines.append(f"Progress:{task.get('Progress', '0')}\n")
lines.append("==========================\n")
return lines

def _input_status(self, prompt="Enter status (待办/进行中/已完成/已取消): "):
while True:
status_input = input(prompt).strip()
status = self._validate_status(status_input)
if status:
return status.value
print("❌ Invalid status. Choose: 待办, 进行中, 已完成, 已取消")

def _input_progress(self, prompt="Enter progress (0-100): "):
while True:
progress_input = input(prompt).strip()
progress = self._validate_progress(progress_input)
if progress is not None:
return str(progress)
print("❌ Invalid progress. Enter a number between 0 and 100.")

def add_task(self):
print("==== Welcome to Task Manager ====")
title = input("Enter title: ").strip()
Expand All @@ -32,6 +120,8 @@ def add_task(self):
else:
print("❌ Invalid priority. Choose High, Medium, or Low.")
category = input("Enter category: ").strip()
status = self._input_status()
progress = self._input_progress()
try:
with open(self.filename, "a", encoding="utf-8") as f:

Expand All @@ -40,6 +130,8 @@ def add_task(self):
f.write(f"Deadline:{deadline}\n")
f.write(f"Priority:{priority}\n")
f.write(f"Category:{category}\n")
f.write(f"Status:{status}\n")
f.write(f"Progress:{progress}\n")
f.write("==========================\n")
except Exception as e:
print(f"Error saving task: {e}")
Expand All @@ -59,7 +151,6 @@ def search_task(self, field):

for line in lines:
if line.strip() == "==========================":
# Check current block before resetting
if any(re.match(pattern, l.strip(), re.IGNORECASE) for l in block):
print("\n".join(block))
print("========================")
Expand All @@ -78,6 +169,79 @@ def search_task(self, field):
if not found:
print(f"No contact found with that {field.lower()}.")

def filter_by_status(self):
try:
with open(self.filename, "r", encoding="utf-8") as f:
lines = f.readlines()
except FileNotFoundError:
print("⚠️ No file found")
return

status_value = self._input_status("Enter status to filter (待办/进行中/已完成/已取消): ")
tasks = self._parse_tasks(lines)
filtered_tasks = [t for t in tasks if t.get("Status", "待办") == status_value]

if not filtered_tasks:
print(f"No tasks found with status: {status_value}")
return

print(f"\n📋 Tasks with status: {status_value}")
for idx, task in enumerate(filtered_tasks, 1):
print(f"\nTask {idx}:")
print(f"Title: {task.get('Title', '')}")
print(f"Description: {task.get('Description', '')}")
print(f"Deadline: {task.get('Deadline', '')}")
print(f"Priority: {task.get('Priority', '')}")
print(f"Category: {task.get('Category', '')}")
print(f"Status: {task.get('Status', '待办')}")
print(f"Progress: {task.get('Progress', '0')}%")

def status_statistics(self):
try:
with open(self.filename, "r", encoding="utf-8") as f:
lines = f.readlines()
except FileNotFoundError:
print("⚠️ No file found")
return

tasks = self._parse_tasks(lines)
if not tasks:
print("No tasks found.")
return

stats = {
"待办": 0,
"进行中": 0,
"已完成": 0,
"已取消": 0,
}

total_progress = 0
completed_progress = 0

for task in tasks:
status = task.get("Status", "待办")
if status in stats:
stats[status] += 1

progress = int(task.get("Progress", 0))
total_progress += progress
if status == "已完成":
completed_progress += 100

total = len(tasks)
avg_progress = total_progress / total if total > 0 else 0

print("\n📊 Task Status Statistics")
print("=" * 40)
print(f"Total Tasks: {total}")
print()
for status, count in stats.items():
percentage = (count / total * 100) if total > 0 else 0
print(f"{status}: {count} ({percentage:.1f}%)")
print("=" * 40)
print(f"Average Progress: {avg_progress:.1f}%")
print(f"Completion Rate: {(stats['已完成'] / total * 100) if total > 0 else 0:.1f}%")

def delete_task(self):
try:
Expand All @@ -100,7 +264,6 @@ def delete_task(self):
if confirm == "yes":
self.create_backup()
found_any = True
# skip
else:
new_lines.extend(block + ["==========================\n"])
else:
Expand Down Expand Up @@ -154,11 +317,51 @@ def update_task(self):
print("❌ Invalid priority.")
new_category = input("Enter category: ")

current_task = self._parse_tasks(block + ["==========================\n"])[0] if block else {}
current_status_str = current_task.get("Status", "待办")
current_status = self._validate_status(current_status_str)

print(f"\nCurrent Status: {current_status_str}")
if current_status:
available_transitions = self._get_available_transitions(current_status)
if available_transitions:
print(f"Available transitions: {[s.value for s in available_transitions]}")

new_status = self._input_status("Enter new status (待办/进行中/已完成/已取消): ")

if current_status:
new_status_enum = self._validate_status(new_status)
if new_status_enum and not self._can_transition_to(current_status, new_status_enum):
print(f"⚠️ Warning: Transition from {current_status_str} to {new_status} is not recommended.")
confirm_transition = input("Do you want to proceed anyway? (yes/no): ").strip().lower()
if confirm_transition != "yes":
new_status = current_status_str
print(f"Status kept as: {current_status_str}")

new_progress = self._input_progress("Enter new progress (0-100): ")

new_progress_int = int(new_progress)
if new_status == "已完成" and new_progress_int < 100:
print("⚠️ Task marked as completed but progress is less than 100%.")
auto_complete = input("Set progress to 100% automatically? (yes/no): ").strip().lower()
if auto_complete == "yes":
new_progress = "100"
print("Progress set to 100%.")

if new_progress_int == 100 and new_status != "已完成":
print("⚠️ Progress is 100% but task is not marked as completed.")
auto_complete = input("Mark as completed? (yes/no): ").strip().lower()
if auto_complete == "yes":
new_status = "已完成"
print("Task marked as completed.")

new_lines.append(f"Title:{new_title}\n")
new_lines.append(f"Description:{new_description}\n")
new_lines.append(f"Deadline:{new_deadline}\n")
new_lines.append(f"Priority:{new_priority}\n")
new_lines.append(f"Category:{new_category}\n")
new_lines.append(f"Status:{new_status}\n")
new_lines.append(f"Progress:{new_progress}\n")
new_lines.append("==========================\n")
else:
new_lines.extend(block + ["==========================\n"])
Expand Down Expand Up @@ -227,15 +430,17 @@ def print_all_tasks(self):
print("4. Update Task ")
print("5. List All Titles")
print("6. Print All Tasks")
print("7. Exit")
choice = input("Enter your choice (1-7): ").strip()
print("7. Filter by Status")
print("8. Status Statistics")
print("9. Exit")
choice = input("Enter your choice (1-9): ").strip()

if choice == "1":
obj.add_task()
elif choice == "2":
print("Make sure write (Title//Category) otherwise it does not work")
print("Make sure write (Title//Category//Status) otherwise it does not work")
print()
field = input("Search by (Title//Category): ").strip().capitalize()
field = input("Search by (Title//Category//Status): ").strip().capitalize()
obj.search_task(field)
elif choice == "3":
obj.delete_task()
Expand All @@ -246,8 +451,11 @@ def print_all_tasks(self):
elif choice == "6":
obj.print_all_tasks()
elif choice == "7":
obj.filter_by_status()
elif choice == "8":
obj.status_statistics()
elif choice == "9":
print("👋 Exiting Task Manager. Goodbye!")
break
else:
print("❌ Invalid input. Try again.")

Loading