Skip to content

Commit 1e51b46

Browse files
Extended api (#18)
* small readme corrections * extended the api * fix import sorting * fix & improve tests * fix formatting * reduce code duplication * format fix * fix typing issue
1 parent 3b3b2e1 commit 1e51b46

File tree

9 files changed

+211
-43
lines changed

9 files changed

+211
-43
lines changed

.isort.cfg

Lines changed: 0 additions & 11 deletions
This file was deleted.

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ Backend REST API for the Task Management application.
2020
```powershell
2121
(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -
2222
```
23+
or
24+
```bash
25+
curl -sSL https://install.python-poetry.org | python3 -
26+
```
2327

2428
2. Install dependencies:
2529
```powershell
@@ -81,7 +85,7 @@ poetry run pytest --cov
8185

8286
### USB Thermal Printer
8387
- Supports ESC/POS compatible printers
84-
- Configure vendor_id and product_id in `config/printers.ini`
88+
- Configure USB vendor_id and product_id in `config/printers.ini`
8589
- Prints task details with QR code for quick access
8690
- Default configuration for ZJ-5870 printer included
8791

openapi.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

poetry.lock

Lines changed: 19 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ force_grid_wrap = 0
6666
use_parentheses = true
6767
ensure_newline_before_comments = true
6868
line_length = 88
69-
skip = ["venv", ".venv", ".git", "__pycache__"]
69+
skip = ["venv", ".venv", ".git", "__pycache__", "output", "dist", "build", ".pytest_cache", ".coverage", "htmlcov"]
7070
known_first_party = ["taskmanagement_app"]
7171
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
7272

taskmanagement_app/api/v1/endpoints/tasks.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import logging
22
from datetime import datetime, timezone
3-
from typing import List, Optional
3+
from typing import Any, List, Optional
44

55
from fastapi import APIRouter, Depends, HTTPException, Query, Response
66
from sqlalchemy.orm import Session
77

8-
from taskmanagement_app.core.exceptions import TaskStatusError
8+
from taskmanagement_app.core.exceptions import TaskNotFoundError, TaskStatusError
99
from taskmanagement_app.core.printing.printer_factory import PrinterFactory
1010
from taskmanagement_app.crud.task import archive_task
1111
from taskmanagement_app.crud.task import complete_task as complete_task_crud
@@ -14,6 +14,7 @@
1414
get_task,
1515
get_tasks,
1616
read_random_task,
17+
reset_task_to_todo,
1718
)
1819
from taskmanagement_app.crud.task import start_task as start_task_crud
1920
from taskmanagement_app.db.models.task import TaskModel, TaskState
@@ -250,3 +251,20 @@ async def trigger_maintenance(db: Session = Depends(get_db)) -> dict:
250251
status_code=500,
251252
detail=f"Error running maintenance job: {str(e)}",
252253
)
254+
255+
256+
@router.patch("/{task_id}/reset-to-todo", response_model=Task)
257+
def reset_task_to_todo_endpoint(task_id: int, db: Session = Depends(get_db)) -> Any:
258+
"""Reset a task to todo state and clear its progress timestamps."""
259+
try:
260+
return reset_task_to_todo(db=db, task_id=task_id)
261+
except TaskNotFoundError:
262+
raise HTTPException(
263+
status_code=404,
264+
detail=f"Task with id {task_id} not found",
265+
)
266+
except TaskStatusError as e:
267+
raise HTTPException(
268+
status_code=400,
269+
detail=str(e),
270+
)

taskmanagement_app/core/exceptions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@ class TaskStatusError(TaskManagementError):
2525
transition is encountered."""
2626

2727
pass
28+
29+
30+
class TaskNotFoundError(TaskManagementError):
31+
"""Exception raised when a requested task cannot be found.
32+
33+
This exception is used when:
34+
- Task ID does not exist in the database
35+
- Task was deleted or is otherwise inaccessible
36+
"""
37+
38+
def __init__(self, task_id: int):
39+
self.task_id = task_id
40+
super().__init__(f"Task with id {task_id} not found")

taskmanagement_app/crud/task.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from sqlalchemy.orm import Session
77

8-
from taskmanagement_app.core.exceptions import TaskStatusError
8+
from taskmanagement_app.core.exceptions import TaskNotFoundError, TaskStatusError
99
from taskmanagement_app.db.models.task import TaskModel, TaskState
1010
from taskmanagement_app.schemas.task import TaskCreate, TaskUpdate
1111

@@ -249,10 +249,10 @@ def archive_task(db: Session, task_id: int) -> Optional[TaskModel]:
249249
if task:
250250
if task.state == TaskState.archived:
251251
raise TaskStatusError("Task is already archived")
252-
elif task.state != TaskState.done:
252+
elif task.state not in [TaskState.done, TaskState.todo]:
253253
raise TaskStatusError(
254254
f"Cannot archive task in state {task.state}. "
255-
"Task must be in 'done' state to be archived."
255+
"Task must be in 'done' or 'todo' state to be archived."
256256
)
257257
task.state = TaskState.archived
258258
db.commit()
@@ -317,3 +317,29 @@ def read_random_task(db: Session) -> Optional[TaskModel]:
317317

318318
# Use random.choices which allows weights
319319
return random.choices(tasks, weights=weights, k=1)[0]
320+
321+
322+
def reset_task_to_todo(db: Session, task_id: int) -> TaskModel:
323+
"""Reset a task to todo state and clear its progress timestamps.
324+
325+
Args:
326+
db: Database session
327+
task_id: ID of the task to reset
328+
329+
Returns:
330+
Updated task
331+
332+
Raises:
333+
TaskNotFoundError: If task doesn't exist
334+
"""
335+
task = get_task(db, task_id)
336+
if not task:
337+
raise TaskNotFoundError(task_id)
338+
339+
task.state = TaskState.todo
340+
task.started_at = None
341+
task.completed_at = None
342+
343+
db.commit()
344+
db.refresh(task)
345+
return task

0 commit comments

Comments
 (0)