All checks were successful
Gitea Actions Demo / build-and-push (push) Successful in 25s
- Implemented PendingRewardDialog for handling pending reward requests. - Created RewardConfirmDialog for confirming reward redemption. - Developed TaskConfirmDialog for task confirmation with child name display. test: add unit tests for ChildView and ParentView components - Added comprehensive tests for ChildView including task triggering and SSE event handling. - Implemented tests for ParentView focusing on override modal and SSE event management. test: add ScrollingList component tests - Created tests for ScrollingList to verify item fetching, loading states, and custom item classes. - Included tests for two-step click interactions and edit button display logic. - Moved toward hashed passwords.
150 lines
4.9 KiB
Markdown
150 lines
4.9 KiB
Markdown
# Tracking Feature Implementation Summary
|
|
|
|
## ✅ Implementation Complete
|
|
|
|
All acceptance criteria from [feat-tracking.md](.github/specs/active/feat-dynamic-points/feat-tracking.md) have been implemented and tested.
|
|
|
|
---
|
|
|
|
## 📦 What Was Delivered
|
|
|
|
### Backend
|
|
|
|
1. **Data Model** ([tracking_event.py](backend/models/tracking_event.py))
|
|
- `TrackingEvent` dataclass with full type safety
|
|
- Factory method `create_event()` for server-side timestamp generation
|
|
- Delta invariant validation (`delta == points_after - points_before`)
|
|
|
|
2. **Database Layer** ([tracking.py](backend/db/tracking.py))
|
|
- New TinyDB table: `tracking_events.json`
|
|
- Helper functions: `insert_tracking_event`, `get_tracking_events_by_child`, `get_tracking_events_by_user`, `anonymize_tracking_events_for_user`
|
|
- Offset-based pagination with sorting by `occurred_at` (desc)
|
|
|
|
3. **Audit Logging** ([tracking_logger.py](backend/utils/tracking_logger.py))
|
|
- Per-user rotating file handlers (`logs/tracking_user_<user_id>.log`)
|
|
- 10MB max file size, 5 backups
|
|
- Structured log format with all event metadata
|
|
|
|
4. **API Integration** ([child_api.py](backend/api/child_api.py))
|
|
- Tracking added to:
|
|
- `POST /child/<id>/trigger-task` → action: `activated`
|
|
- `POST /child/<id>/request-reward` → action: `requested`
|
|
- `POST /child/<id>/trigger-reward` → action: `redeemed`
|
|
- `POST /child/<id>/cancel-request-reward` → action: `cancelled`
|
|
|
|
5. **Admin API** ([tracking_api.py](backend/api/tracking_api.py))
|
|
- `GET /admin/tracking` with filters:
|
|
- `child_id` (required if no `user_id`)
|
|
- `user_id` (admin only)
|
|
- `entity_type` (task|reward|penalty)
|
|
- `action` (activated|requested|redeemed|cancelled)
|
|
- `limit` (default 50, max 500)
|
|
- `offset` (default 0)
|
|
- Returns total count for future pagination UI
|
|
|
|
6. **SSE Events** ([event_types.py](backend/events/types/event_types.py), [tracking_event_created.py](backend/events/types/tracking_event_created.py))
|
|
- New event type: `TRACKING_EVENT_CREATED`
|
|
- Payload: `tracking_event_id`, `child_id`, `entity_type`, `action`
|
|
- Emitted on every tracking event creation
|
|
|
|
---
|
|
|
|
### Frontend
|
|
|
|
1. **TypeScript Models** ([models.ts](frontend/vue-app/src/common/models.ts))
|
|
- `TrackingEvent` interface (1:1 parity with Python)
|
|
- Type aliases: `EntityType`, `ActionType`
|
|
- `TrackingEventCreatedPayload` for SSE events
|
|
|
|
2. **API Helpers** ([api.ts](frontend/vue-app/src/common/api.ts))
|
|
- `getTrackingEventsForChild()` function with all filter params
|
|
|
|
3. **SSE Registration**
|
|
- Event type registered in type union
|
|
- Ready for future UI components
|
|
|
|
---
|
|
|
|
### Tests
|
|
|
|
**Backend Unit Tests** ([test_tracking.py](backend/tests/test_tracking.py)):
|
|
|
|
- ✅ Tracking event creation with factory method
|
|
- ✅ Delta invariant validation
|
|
- ✅ Insert and query tracking events
|
|
- ✅ Filtering by `entity_type` and `action`
|
|
- ✅ Offset-based pagination
|
|
- ✅ User anonymization on deletion
|
|
- ✅ Points change correctness (positive/negative/zero delta)
|
|
- ✅ No points change for request/cancel actions
|
|
|
|
---
|
|
|
|
## 🔑 Key Design Decisions
|
|
|
|
1. **Append-only tracking table** - No deletions, only anonymization on user deletion
|
|
2. **Server timestamps** - `occurred_at` always uses server time (UTC) to avoid client clock drift
|
|
3. **Separate logging** - Per-user audit logs independent of database
|
|
4. **Offset pagination** - Simpler than cursors, sufficient for expected scale
|
|
5. **No UI (yet)** - API/models/SSE only; UI deferred to future phase
|
|
|
|
---
|
|
|
|
## 🚀 Usage Examples
|
|
|
|
### Backend: Create a tracking event
|
|
|
|
```python
|
|
from models.tracking_event import TrackingEvent
|
|
from db.tracking import insert_tracking_event
|
|
from utils.tracking_logger import log_tracking_event
|
|
|
|
event = TrackingEvent.create_event(
|
|
user_id='user123',
|
|
child_id='child456',
|
|
entity_type='task',
|
|
entity_id='task789',
|
|
action='activated',
|
|
points_before=50,
|
|
points_after=60,
|
|
metadata={'task_name': 'Homework'}
|
|
)
|
|
|
|
insert_tracking_event(event)
|
|
log_tracking_event(event)
|
|
```
|
|
|
|
### Frontend: Query tracking events
|
|
|
|
```typescript
|
|
import { getTrackingEventsForChild } from "@/common/api";
|
|
|
|
const res = await getTrackingEventsForChild({
|
|
childId: "child456",
|
|
entityType: "task",
|
|
limit: 20,
|
|
offset: 0,
|
|
});
|
|
|
|
const data = await res.json();
|
|
// { tracking_events: [...], total: 42, count: 20, limit: 20, offset: 0 }
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Migration Notes
|
|
|
|
1. **New database file**: `backend/data/db/tracking_events.json` will be created automatically on first tracking event.
|
|
2. **New log directory**: `backend/logs/tracking_user_<user_id>.log` files will be created per user.
|
|
3. **No breaking changes** to existing APIs or data models.
|
|
|
|
---
|
|
|
|
## 🔮 Future Enhancements (Not in This Phase)
|
|
|
|
- Admin/parent UI for viewing tracking history
|
|
- Badges and certificates based on tracking data
|
|
- Analytics and reporting dashboards
|
|
- Export tracking data (CSV, JSON)
|
|
- Time-based filters (date range queries)
|