diff --git a/README.md b/README.md index 91da9a7..ee118b0 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,38 @@ python task.py --help # Add a task python task.py add "Buy groceries" +# Add a task with JSON output +python task.py add "Buy groceries" --json + # List tasks python task.py list +# List tasks with JSON output +python task.py list --json + # Complete a task python task.py done 1 + +# Complete a task with JSON output +python task.py done 1 --json +``` + +### JSON Output Examples + +```bash +python task.py add "Buy groceries" --json +``` + +```json +{"success": true, "message": "Added task 1: Buy groceries", "task": {"id": 1, "description": "Buy groceries", "done": false}} +``` + +```bash +python task.py list --json +``` + +```json +{"success": true, "tasks": [{"id": 1, "description": "Buy groceries", "done": false}]} ``` ## Testing diff --git a/commands/add.py b/commands/add.py index 1b1a943..646293d 100644 --- a/commands/add.py +++ b/commands/add.py @@ -19,7 +19,7 @@ def validate_description(description): return description.strip() -def add_task(description): +def add_task(description, json_output=False): """Add a new task.""" description = validate_description(description) @@ -31,7 +31,16 @@ def add_task(description): tasks = json.loads(tasks_file.read_text()) task_id = len(tasks) + 1 - tasks.append({"id": task_id, "description": description, "done": False}) + task = {"id": task_id, "description": description, "done": False} + tasks.append(task) tasks_file.write_text(json.dumps(tasks, indent=2)) + if json_output: + print(json.dumps({ + "success": True, + "message": f"Added task {task_id}: {description}", + "task": task, + })) + return + print(f"Added task {task_id}: {description}") diff --git a/commands/done.py b/commands/done.py index c9dfd42..7f1bd08 100644 --- a/commands/done.py +++ b/commands/done.py @@ -17,10 +17,13 @@ def validate_task_id(tasks, task_id): return task_id -def mark_done(task_id): +def mark_done(task_id, json_output=False): """Mark a task as complete.""" tasks_file = get_tasks_file() if not tasks_file.exists(): + if json_output: + print(json.dumps({"success": False, "error": "No tasks found!"})) + return print("No tasks found!") return @@ -31,7 +34,17 @@ def mark_done(task_id): if task["id"] == task_id: task["done"] = True tasks_file.write_text(json.dumps(tasks, indent=2)) + if json_output: + print(json.dumps({ + "success": True, + "message": f"Marked task {task_id} as done: {task['description']}", + "task": task, + })) + return print(f"Marked task {task_id} as done: {task['description']}") return + if json_output: + print(json.dumps({"success": False, "error": f"Task {task_id} not found"})) + return print(f"Task {task_id} not found") diff --git a/commands/list.py b/commands/list.py index 714315d..584f7ac 100644 --- a/commands/list.py +++ b/commands/list.py @@ -18,20 +18,29 @@ def validate_task_file(): return tasks_file -def list_tasks(): +def list_tasks(json_output=False): """List all tasks.""" - # NOTE: No --json flag support yet (feature bounty) tasks_file = validate_task_file() if not tasks_file: + if json_output: + print(json.dumps({"success": True, "tasks": []})) + return print("No tasks yet!") return tasks = json.loads(tasks_file.read_text()) if not tasks: + if json_output: + print(json.dumps({"success": True, "tasks": []})) + return print("No tasks yet!") return + if json_output: + print(json.dumps({"success": True, "tasks": tasks})) + return + for task in tasks: status = "✓" if task["done"] else " " print(f"[{status}] {task['id']}. {task['description']}") diff --git a/task.py b/task.py index 53cc8ed..867cb51 100644 --- a/task.py +++ b/task.py @@ -25,22 +25,28 @@ def main(): # Add command add_parser = subparsers.add_parser("add", help="Add a new task") add_parser.add_argument("description", help="Task description") + add_parser.add_argument("--json", action="store_true", dest="json_output", + help="Output JSON") # List command list_parser = subparsers.add_parser("list", help="List all tasks") + list_parser.add_argument("--json", action="store_true", dest="json_output", + help="Output JSON") # Done command done_parser = subparsers.add_parser("done", help="Mark task as complete") done_parser.add_argument("task_id", type=int, help="Task ID to mark done") + done_parser.add_argument("--json", action="store_true", dest="json_output", + help="Output JSON") args = parser.parse_args() if args.command == "add": - add_task(args.description) + add_task(args.description, json_output=args.json_output) elif args.command == "list": - list_tasks() + list_tasks(json_output=args.json_output) elif args.command == "done": - mark_done(args.task_id) + mark_done(args.task_id, json_output=args.json_output) else: parser.print_help() diff --git a/test_task.py b/test_task.py index ba98e43..ff9657a 100644 --- a/test_task.py +++ b/test_task.py @@ -1,9 +1,13 @@ """Basic tests for task CLI.""" import json -import pytest +import subprocess +import sys from pathlib import Path -from commands.add import add_task, validate_description + +import pytest + +from commands.add import validate_description from commands.done import validate_task_id @@ -28,3 +32,50 @@ def test_validate_task_id(): with pytest.raises(ValueError): validate_task_id(tasks, 99) + + +def run_cli(tmp_path, *args): + """Run the CLI with an isolated HOME directory.""" + env = {"HOME": str(tmp_path)} + result = subprocess.run( + [sys.executable, "task.py", *args], + cwd=Path(__file__).parent, + env=env, + capture_output=True, + text=True, + check=True, + ) + return json.loads(result.stdout) + + +def test_add_command_supports_json_output(tmp_path): + """Add command emits valid JSON output.""" + payload = run_cli(tmp_path, "add", "Buy groceries", "--json") + assert payload["success"] is True + assert payload["task"] == { + "id": 1, + "description": "Buy groceries", + "done": False, + } + + +def test_list_command_supports_json_output(tmp_path): + """List command emits valid JSON output.""" + run_cli(tmp_path, "add", "Buy groceries", "--json") + payload = run_cli(tmp_path, "list", "--json") + assert payload["success"] is True + assert payload["tasks"] == [ + {"id": 1, "description": "Buy groceries", "done": False} + ] + + +def test_done_command_supports_json_output(tmp_path): + """Done command emits valid JSON output.""" + run_cli(tmp_path, "add", "Buy groceries", "--json") + payload = run_cli(tmp_path, "done", "1", "--json") + assert payload["success"] is True + assert payload["task"] == { + "id": 1, + "description": "Buy groceries", + "done": True, + }