From 03b28a042973b2056ba6a579a87562a804a1875f Mon Sep 17 00:00:00 2001 From: "Galio (Demaciains)" Date: Wed, 25 Mar 2026 19:56:44 +0000 Subject: [PATCH] refactor: extract validation and path utilities to utils module - Create utils/ directory with __init__.py - Create utils/validation.py with validate_description, validate_task_id, validate_task_file - Create utils/paths.py with get_config_path, get_tasks_file - Update commands/add.py to import from utils - Update commands/done.py to import from utils - Update commands/list.py to import from utils - Update test_task.py to import from utils - Update task.py to use get_config_path from utils - Add .gitignore for __pycache__ and .pytest_cache No behavior changes - all existing tests pass. Closes #3 --- .gitignore | 3 +++ commands/add.py | 18 ++---------------- commands/done.py | 16 ++-------------- commands/list.py | 17 +---------------- task.py | 4 ++-- test_task.py | 3 +-- utils/__init__.py | 1 + utils/paths.py | 13 +++++++++++++ utils/validation.py | 27 +++++++++++++++++++++++++++ 9 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 .gitignore create mode 100644 utils/__init__.py create mode 100644 utils/paths.py create mode 100644 utils/validation.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75c6182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.pyc +.pytest_cache/ diff --git a/commands/add.py b/commands/add.py index 1b1a943..dba12e4 100644 --- a/commands/add.py +++ b/commands/add.py @@ -1,22 +1,8 @@ """Add task command.""" import json -from pathlib import Path - - -def get_tasks_file(): - """Get path to tasks file.""" - return Path.home() / ".local" / "share" / "task-cli" / "tasks.json" - - -def validate_description(description): - """Validate task description.""" - # NOTE: Validation logic scattered here - should be in utils (refactor bounty) - if not description: - raise ValueError("Description cannot be empty") - if len(description) > 200: - raise ValueError("Description too long (max 200 chars)") - return description.strip() +from utils.paths import get_tasks_file +from utils.validation import validate_description def add_task(description): diff --git a/commands/done.py b/commands/done.py index c9dfd42..995f058 100644 --- a/commands/done.py +++ b/commands/done.py @@ -1,20 +1,8 @@ """Mark task done command.""" import json -from pathlib import Path - - -def get_tasks_file(): - """Get path to tasks file.""" - return Path.home() / ".local" / "share" / "task-cli" / "tasks.json" - - -def validate_task_id(tasks, task_id): - """Validate task ID exists.""" - # NOTE: Validation logic scattered here - should be in utils (refactor bounty) - if task_id < 1 or task_id > len(tasks): - raise ValueError(f"Invalid task ID: {task_id}") - return task_id +from utils.paths import get_tasks_file +from utils.validation import validate_task_id def mark_done(task_id): diff --git a/commands/list.py b/commands/list.py index 714315d..14ce660 100644 --- a/commands/list.py +++ b/commands/list.py @@ -1,26 +1,11 @@ """List tasks command.""" import json -from pathlib import Path - - -def get_tasks_file(): - """Get path to tasks file.""" - return Path.home() / ".local" / "share" / "task-cli" / "tasks.json" - - -def validate_task_file(): - """Validate tasks file exists.""" - # NOTE: Validation logic scattered here - should be in utils (refactor bounty) - tasks_file = get_tasks_file() - if not tasks_file.exists(): - return [] - return tasks_file +from utils.validation import validate_task_file def list_tasks(): """List all tasks.""" - # NOTE: No --json flag support yet (feature bounty) tasks_file = validate_task_file() if not tasks_file: print("No tasks yet!") diff --git a/task.py b/task.py index 53cc8ed..997927c 100644 --- a/task.py +++ b/task.py @@ -8,12 +8,12 @@ from commands.add import add_task from commands.list import list_tasks from commands.done import mark_done +from utils.paths import get_config_path def load_config(): """Load configuration from file.""" - config_path = Path.home() / ".config" / "task-cli" / "config.yaml" - # NOTE: This will crash if config doesn't exist - known bug for bounty testing + config_path = get_config_path() with open(config_path) as f: return f.read() diff --git a/test_task.py b/test_task.py index ba98e43..af8e67b 100644 --- a/test_task.py +++ b/test_task.py @@ -3,8 +3,7 @@ import json import pytest from pathlib import Path -from commands.add import add_task, validate_description -from commands.done import validate_task_id +from utils.validation import validate_description, validate_task_id def test_validate_description(): diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..3857fda --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1 @@ +"""Shared utilities for task CLI.""" diff --git a/utils/paths.py b/utils/paths.py new file mode 100644 index 0000000..518e1be --- /dev/null +++ b/utils/paths.py @@ -0,0 +1,13 @@ +"""Shared path helpers for task CLI.""" + +from pathlib import Path + + +def get_config_path(): + """Get path to configuration file.""" + return Path.home() / ".config" / "task-cli" / "config.yaml" + + +def get_tasks_file(): + """Get path to tasks file.""" + return Path.home() / ".local" / "share" / "task-cli" / "tasks.json" diff --git a/utils/validation.py b/utils/validation.py new file mode 100644 index 0000000..8ac2c2e --- /dev/null +++ b/utils/validation.py @@ -0,0 +1,27 @@ +"""Shared validation logic for task CLI.""" + +from utils.paths import get_tasks_file + + +def validate_description(description): + """Validate task description.""" + if not description: + raise ValueError("Description cannot be empty") + if len(description) > 200: + raise ValueError("Description too long (max 200 chars)") + return description.strip() + + +def validate_task_id(tasks, task_id): + """Validate task ID exists.""" + if task_id < 1 or task_id > len(tasks): + raise ValueError(f"Invalid task ID: {task_id}") + return task_id + + +def validate_task_file(): + """Validate tasks file exists.""" + tasks_file = get_tasks_file() + if not tasks_file.exists(): + return [] + return tasks_file