feat(taskflow): add core task API, storage persistence, csv export, stats page, and test coverage
This commit is contained in:
314
README.md
Normal file
314
README.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# 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`)
|
||||
- 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
|
||||
- Start datetime
|
||||
- Done datetime
|
||||
- Cycle time
|
||||
|
||||
- Bottom:
|
||||
- Kanban board:
|
||||
- Backlog
|
||||
- In Progress
|
||||
- Done
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user