diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f8c1b79 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +"""add_task.py updated""" +"""complete_task.py updated""" +"""view_tasks.py updated""" +11/18/2025 + +"""added export_data.py and what changed""" diff --git a/README.md b/README.md index e69de29..7f5b060 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,4 @@ +""" +#During Sprint 2, the add_task file was expanded to include an optional validated due_date, and every new task is created with a completion_date. +#This fixed the fact that Sprint 1 had an inconsistent JSON structure for add_tasks. view_tasks.py was improved to sort tasks by priority, show or hide completed tasks with a --show-completed flag, and display the new due-date and completion-date fields. complete_task.py was updated so finishing a task now records a timestamp and supports +# undoing completion. export_tasks.py was added, providing CSV/TXT export and filters for completed or pending tasks. diff --git a/add_task.py b/add_task.py index c3662fa..b732a24 100644 --- a/add_task.py +++ b/add_task.py @@ -1,65 +1,35 @@ import json -import os - -DATA_FILE = "data.json" - -def load_data(): - """Load task data from data.json or create default structure.""" - if os.path.exists(DATA_FILE): - with open(DATA_FILE, "r") as f: - return json.load(f) - return { - "tasks": [], - "settings": { - "app_name": "Task Manager", - "version": "1.0.0", - "max_tasks": 100 - } - } - -def save_data(data): - """Save task data back to data.json.""" - with open(DATA_FILE, "w") as f: - json.dump(data, f, indent=4) - -def get_next_id(tasks): - """Return the next task ID.""" - if not tasks: - return 1 - highest = 0 - for t in tasks: - if int(t.get("id", 0)) > highest: - highest = int(t["id"]) - return highest + 1 - -def add_task(description, priority): - """Add a new task with description and priority.""" - data = load_data() - tasks = data["tasks"] - - max_tasks = data.get("settings", {}).get("max_tasks", 100) - if len(tasks) >= max_tasks: - print("You have reached the maximum number of tasks.") - return - - new_task = { - "id": get_next_id(tasks), - "description": description, - "priority": priority, - "completed": False - } - - tasks.append(new_task) - save_data(data) - print(" Task added:", description, "(Priority:", priority + ")") - -if __name__ == "__main__": - print("Enter task description:") - desc = input().strip() - print("Enter priority (high, medium, low):") - prio = input().strip().lower() - if prio not in ["high", "medium", "low"]: - print("Priority must be: high, medium, or low.") - else: - add_task(desc, prio) +import re +DATA_FILE="data.json" + +def is_valid_date(d): + """Validate YYYY--MM--DD format""" + return re.match(r"^\d{4}-\d{2}-\d{2}$",d) is not None + +def add_task(): + description=input("Enter task description: ").strip() + priority=input("Enter priority (high/medium/low): ").strip().lower() + due_date=input("Enter due date (YYYY-MM-DD) or press Enter to skip: ") + if due_date=="": + due_date=None + elif not is_valid_date(due_date): + print("Invalid date format. Must be YYYY-MM-DD.") + return + with open(DATA_FILE,"r") as f: + data=json.load(f) + task_id=len(data["tasks"])+1 + new_task={ + "id":task_id, + "description":description, + "priority": priority, + "completed": False, + "due_date":due_date, + "completion_date":None + } + data["tasks"].append(new_task) + with open(DATA_FILE,"w") as f: + json.dump(data,f,indent=4) + print(f"Task #{task_id} added successfully!") +if __name__=="__main__": + add_task() diff --git a/complete_task.py b/complete_task.py index 53f4f38..2875faf 100644 --- a/complete_task.py +++ b/complete_task.py @@ -1,38 +1,37 @@ import json +from datetime import datetime + DATA_FILE="data.json" -def load_tasks(): - if not os.path.exists(DATA_FILE): - print("Error: data.json file not found.") - return {"tasks":[]} + +def complete_task(): + task_id=input("Enter task ID to complete: ").strip() + if not task_id.isdigit(): + print("Invalid ID format.") + return + task_id=int(task_id) with open(DATA_FILE,"r") as f: data=json.load(f) - return data -def save_tasks(data): - with open(DATA_FILE,"w") as f: - json.dump(data,f,indent=4) - -def complete_task(task_id): - data=load_tasks() - tasks=data["tasks"] - found=False - for task in tasks: - if task["id"]==task_id: - found=True - if task["completed"]: - print("Task",task_id,"is already completed.") - else: - task["completed"]=True - save_tasks(data) - print("Task",task_id,"marked as completed.") + found=None + for t in data["tasks"]: + if t["id"] == task_id: + found=t break if not found: - print("Task with ID",task_id,"not found.") -if __name__=="__main__": - import os - print("Enter the task to complete:") - task_id_input=input().strip() - if task_id_input.isdigit(): - task_id=int(task_id_input) - complete_task(task_id) + print("Task not found.") + return + if found["completed"]: + undo=input("Task is already completed. Undo completion? (yes/no): ") + if undo=="yes": + found["completed"]=False + found["completion_date"]=None + print("Completion undone.") + else: + print("No changes made.") else: - print("Please enter a valid task ID number.") + found["completed"]=True + found["completion_date"]=datetime.now().strftime("%Y-%m-%d") + print("Task marked completed!") + with open(DATA_FILE,"w") as f: + json.dump(data,f,indent=4) +if __name__=="__main__": + complete_task() diff --git a/data.json b/data.json index 4a07190..5376a53 100644 --- a/data.json +++ b/data.json @@ -1,8 +1,13 @@ { - "tasks":[], - "settings":{ - "app_name":"Task Manager", - "version":"1.0.0", - "max_tasks":100 - } + "tasks": [ + { + "id": 1, + "description": "Do something", + "priority": "high", + "completed": false, + "due_date": null, + "completion_date": null + } + ] } + diff --git a/delete_task.py b/delete_task.py index e69de29..8b13789 100644 --- a/delete_task.py +++ b/delete_task.py @@ -0,0 +1 @@ + diff --git a/deleted.log b/deleted.log new file mode 100644 index 0000000..86da04a --- /dev/null +++ b/deleted.log @@ -0,0 +1 @@ +[2025-11-19 05:17:13] Deleted Task ID=1 | "Finish homework" diff --git a/export_tasks.py b/export_tasks.py new file mode 100644 index 0000000..b1df345 --- /dev/null +++ b/export_tasks.py @@ -0,0 +1,87 @@ +import json +import os +import sys +from datetime import datetime + +DATA_FILE = "data.json" + +PRIORITY_ORDER = { + "high": 1, + "medium": 2, + "low": 3 +} + + +def load_data(): + """Load tasks from JSON file.""" + if os.path.exists(DATA_FILE): + with open(DATA_FILE, "r") as f: + return json.load(f) + return {"tasks": []} + + +def export_to_csv(tasks, filename="tasks_export.csv"): + """Write tasks to a CSV file.""" + with open(filename, "w") as f: + f.write("id,description,priority,completed,due_date,completion_date\n") + for t in tasks: + f.write(f"{t['id']}," + f"\"{t['description']}\"," + f"{t['priority']}," + f"{t['completed']}," + f"{t.get('due_date','')}," + f"{t.get('completion_date','')}\n") + print(f"Export complete! File saved as {filename}") + + +def export_to_txt(tasks, filename="tasks_export.txt"): + """Write tasks to a TXT file.""" + with open(filename, "w") as f: + for t in tasks: + f.write(f"ID: {t['id']}\n") + f.write(f"Description: {t['description']}\n") + f.write(f"Priority: {t['priority']}\n") + f.write(f"Completed: {t['completed']}\n") + f.write(f"Due Date: {t.get('due_date','None')}\n") + f.write(f"Completion Date: {t.get('completion_date','None')}\n") + f.write("-" * 40 + "\n") + print(f"Export complete! File saved as {filename}") + + +def export_tasks(): + args = sys.argv[1:] + + data = load_data() + tasks = data.get("tasks", []) + + # Filter flags + completed_only = "--completed-only" in args + pending_only = "--pending-only" in args + export_txt = "--txt" in args + + if completed_only and pending_only: + print("Error: Cannot use --completed-only and --pending-only together.") + return + + if completed_only: + tasks = [t for t in tasks if t.get("completed")] + elif pending_only: + tasks = [t for t in tasks if not t.get("completed")] + + # Sort tasks by priority for consistent output + tasks.sort(key=lambda t: PRIORITY_ORDER.get(t["priority"], 99)) + + if not tasks: + print("No tasks match the selected filters.") + return + + # Choose file format + if export_txt: + export_to_txt(tasks) + else: + export_to_csv(tasks) + + +if __name__ == "__main__": + export_tasks() + diff --git a/tasks_export.csv b/tasks_export.csv new file mode 100644 index 0000000..d2f9be7 --- /dev/null +++ b/tasks_export.csv @@ -0,0 +1,2 @@ +id,description,priority,completed,due_date,completion_date +1,"Do something",high,False,None,None diff --git a/view_tasks.py b/view_tasks.py index 4d37c7a..f117560 100644 --- a/view_tasks.py +++ b/view_tasks.py @@ -1,34 +1,32 @@ import json -import os +import sys +DATA_FILE="data.json" -DATA_FILE = "data.json" - -def load_data(): - """Load tasks from JSON file or return empty list wrapper.""" - if os.path.exists(DATA_FILE): - with open(DATA_FILE, "r") as f: - return json.load(f) - return {"tasks": []} +PRIORITY_ORDER={ + "high":1, + "medium":2, + "low":3 +} def view_tasks(): - """Display all tasks neatly with ID, status, priority, description.""" - data = load_data() - tasks = data.get("tasks", []) - - if not tasks: - print("No tasks available.") - return - - print("\n Task List") - print("-" * 50) - for task in tasks: - status = " Completed" if task.get("completed") else " Not Completed" - print("ID:", task.get("id")) - print("Description:", task.get("description")) - print("Priority:", task.get("priority")) - print("Status:", status) - print("-" * 50) - -if __name__ == "__main__": - view_tasks() - + show_completed="--show-completed" in sys.argv + with open(DATA_FILE,"r") as f: + data=json.load(f) + tasks=data["tasks"] + if not show_completed: + tasks=[t for t in tasks if not t["completed"]] + tasks.sort(key=lambda t: PRIORITY_ORDER.get(t["priority"],99)) + if not tasks: + print("No tasks to display.") + return + for t in tasks: + status="Completed" if t["completed"] else "Pending" + due=t["due_date"] if t["due_date"] else "None" + print(f"[{t['id']}] {t['description']} ({t['priority']})") + print(f" Due: {due}") + print(f" Status: {status}") + if t["completion_date"]: + print(f" Completed on: {t['completion_date']}") + print() +if __name__=="__main__": + view_tasks()