365 lines
5.4 KiB
Markdown
365 lines
5.4 KiB
Markdown
# TaskFlow (Local Task Tracker)
|
|
|
|
Minimalistic local task tracker implementing a simple workflow:
|
|
|
|
```
|
|
Backlog → In Progress → Done
|
|
```
|
|
|
|
The service exposes a clean HTTP API, provides workflow metrics, persists state to disk, and includes a lightweight HTML dashboard.
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
- Python 3.14
|
|
- FastAPI 0.135
|
|
- Pydantic v2
|
|
- Pytest
|
|
- Docker (local runtime)
|
|
|
|
---
|
|
|
|
## Features
|
|
|
|
- CRUD operations for tasks
|
|
- Workflow transitions with validation:
|
|
- backlog → in_progress → done
|
|
- Idempotent `start` operation
|
|
- Metrics:
|
|
- Lead Time
|
|
- Cycle Time
|
|
- CSV export with injection protection
|
|
- Persistent storage (`data/tasks.json`)
|
|
- HTML dashboard (`/stats`)
|
|
- Lightweight dashboard data loading via internal `/ui_data/*` routes
|
|
- Atomic file writes with corruption recovery
|
|
|
|
---
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
.
|
|
├── app/ # Application code
|
|
│ ├── api/ # Transport layer (FastAPI routers)
|
|
│ ├── domain/ # Business logic
|
|
│ └── storage/# Persistence layer
|
|
├── data/ # Persistent storage (tasks.json)
|
|
├── tests/ # Test suite
|
|
└── Dockerfile
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
The project follows a layered architecture:
|
|
|
|
### 1. Transport Layer
|
|
- FastAPI routers
|
|
- Request/response validation (Pydantic)
|
|
- HTTP error mapping
|
|
|
|
### 2. Domain Layer
|
|
- Business rules
|
|
- Workflow transitions
|
|
- Metrics calculation
|
|
|
|
### 3. Storage Layer
|
|
- File-based persistence
|
|
- Atomic writes (tmp + rename)
|
|
- Corruption recovery (backup + reset)
|
|
|
|
---
|
|
|
|
## Domain Model
|
|
|
|
### Task
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"title": "string (max 100)",
|
|
"created_at": "datetime (UTC ISO-8601)",
|
|
"started_at": "datetime | null",
|
|
"done_at": "datetime | null"
|
|
}
|
|
```
|
|
|
|
### Status Derivation
|
|
|
|
Status is not stored explicitly:
|
|
|
|
- backlog → `started_at == null`
|
|
- in_progress → `started_at != null && done_at == null`
|
|
- done → `done_at != null`
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Tasks
|
|
|
|
#### List tasks
|
|
|
|
```
|
|
GET /api/tasks
|
|
```
|
|
|
|
Query params:
|
|
- `limit` (default: 100)
|
|
- `status` (optional: backlog | in_progress | done)
|
|
- `search` (optional substring match)
|
|
|
|
---
|
|
|
|
#### Get task by ID
|
|
|
|
```
|
|
GET /api/tasks/{id}
|
|
```
|
|
|
|
---
|
|
|
|
#### Create task
|
|
|
|
```
|
|
POST /api/tasks
|
|
```
|
|
|
|
---
|
|
|
|
#### Update task
|
|
|
|
```
|
|
PATCH /api/tasks/{id}
|
|
```
|
|
|
|
---
|
|
|
|
#### Delete task
|
|
|
|
```
|
|
DELETE /api/tasks/{id}
|
|
```
|
|
|
|
---
|
|
|
|
### Workflow Actions
|
|
|
|
#### Start task
|
|
|
|
```
|
|
POST /api/tasks/{id}/start
|
|
```
|
|
|
|
Rules:
|
|
- Idempotent (multiple calls do not change `started_at`)
|
|
- Valid only if task is in backlog
|
|
|
|
---
|
|
|
|
#### Complete task
|
|
|
|
```
|
|
POST /api/tasks/{id}/done
|
|
```
|
|
|
|
Rules:
|
|
- Allowed only from `in_progress`
|
|
- Enforces workflow integrity
|
|
|
|
---
|
|
|
|
### Export
|
|
|
|
```
|
|
GET /api/tasks/export
|
|
```
|
|
|
|
- Returns `text/csv`
|
|
- Includes header row
|
|
- Protects against CSV injection (`=`, `+`, `-`, `@`)
|
|
|
|
---
|
|
|
|
### Stats
|
|
|
|
```
|
|
GET /stats
|
|
```
|
|
|
|
HTML page:
|
|
|
|
- Top block:
|
|
- Selected task details:
|
|
- Title
|
|
- Status
|
|
- Created datetime
|
|
- Start datetime
|
|
- Done datetime
|
|
- Cycle time
|
|
|
|
- Bottom:
|
|
- Kanban board:
|
|
- Backlog
|
|
- In Progress
|
|
- Done
|
|
|
|
Implementation notes:
|
|
|
|
- `/stats` returns a lightweight HTML shell
|
|
- Task lists are loaded separately from an internal UI endpoint
|
|
- Selected task details are loaded on demand when a card is selected
|
|
- Full task payloads are not embedded into the HTML for every card
|
|
|
|
### Internal UI Data Routes
|
|
|
|
These routes are used only by the HTML dashboard and are intentionally hidden from Swagger / OpenAPI.
|
|
|
|
#### Board data
|
|
|
|
```
|
|
GET /ui_data/stats/board
|
|
```
|
|
|
|
Returns lightweight task list items grouped by status:
|
|
|
|
- `backlog_tasks`
|
|
- `in_progress_tasks`
|
|
- `done_tasks`
|
|
|
|
Each list item contains only:
|
|
|
|
- `id`
|
|
- `title`
|
|
- `status`
|
|
- `display_date_label`
|
|
- `display_date_value`
|
|
|
|
#### Task details
|
|
|
|
```
|
|
GET /ui_data/tasks/{id}
|
|
```
|
|
|
|
Returns the full details needed for the selected-task panel:
|
|
|
|
- `id`
|
|
- `title`
|
|
- `status`
|
|
- `created_at`
|
|
- `started_at`
|
|
- `done_at`
|
|
- `cycle_time`
|
|
|
|
---
|
|
|
|
## Error Format
|
|
|
|
All errors follow unified format:
|
|
|
|
```
|
|
{
|
|
"error": "invalid_*",
|
|
"message": "human readable description"
|
|
}
|
|
```
|
|
|
|
Examples:
|
|
|
|
- `invalid_id`
|
|
- `invalid_payload`
|
|
- `invalid_transition`
|
|
- `invalid_transaction`
|
|
|
|
HTTP codes:
|
|
|
|
- 2xx — success
|
|
- 4xx — client errors (validation, transitions)
|
|
|
|
---
|
|
|
|
## Data Persistence
|
|
|
|
- File: `data/tasks.json`
|
|
- Writes are atomic:
|
|
1. Write to temp file
|
|
2. Rename
|
|
|
|
### Corruption Handling
|
|
|
|
If JSON is invalid:
|
|
|
|
- Backup corrupted file
|
|
- Reset to empty state
|
|
- Service continues running
|
|
|
|
---
|
|
|
|
## Metrics
|
|
|
|
- **Lead Time** = `done_at - created_at`
|
|
- **Cycle Time** = `done_at - started_at`
|
|
|
|
Average values are computed across completed tasks.
|
|
|
|
---
|
|
|
|
## Testing Requirements
|
|
|
|
Test suite must cover:
|
|
|
|
1. CRUD operations and filters
|
|
2. Workflow transitions:
|
|
- valid transitions → 2xx
|
|
- invalid → `409 invalid_transaction`
|
|
3. Metrics correctness
|
|
4. CSV export:
|
|
- `text/csv`
|
|
- correct header
|
|
5. Persistence between restarts
|
|
6. Performance:
|
|
- typical requests ≤ 100 ms
|
|
|
|
---
|
|
|
|
## Running with Docker
|
|
|
|
### Build and run
|
|
|
|
```
|
|
docker build -t taskflow .
|
|
docker run -p 8000:8000 -v $(pwd)/data:/app/data taskflow
|
|
```
|
|
|
|
### One-command run (recommended)
|
|
|
|
```
|
|
docker compose up --build
|
|
```
|
|
|
|
---
|
|
|
|
## Conventions
|
|
|
|
- All timestamps are UTC (ISO-8601)
|
|
- Validation via Pydantic v2
|
|
- Strict API contracts
|
|
- No implicit state mutations
|
|
- Idempotent operations where required
|
|
|
|
---
|
|
|
|
## Goal
|
|
|
|
The project is considered complete when:
|
|
|
|
- The service runs in Docker with a single command
|
|
- All tests pass
|
|
- State persists across restarts
|
|
- API behaves according to specification
|
|
|
|
---
|