"""UserHistory — normalised view of a user's feedback events for inference.""" from __future__ import annotations from dataclasses import dataclass, field from datetime import datetime, timezone @dataclass class FeedbackEvent: action: str # 'done' | 'dismiss' | 'snooze' | 'helpful' | 'not_helpful' dwell_ms: int | None created_at: str # ISO 8601 @property def hour(self) -> int: """Hour of day (0-23) when the feedback was recorded.""" try: dt = datetime.fromisoformat(self.created_at.replace("Z", "+00:00")) except ValueError: return 12 if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) return dt.hour @dataclass class TaskCompletion: """A completed task that had a due date — used for lateness inference.""" project_id: str | None completed_at: str # ISO 8601 due_at: str # ISO 8601 @property def lateness_days(self) -> float: """Days between due_at and completed_at. Negative = completed early.""" try: def _parse(s: str) -> datetime: dt = datetime.fromisoformat(s.replace("Z", "+00:00")) return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc) return (_parse(self.completed_at) - _parse(self.due_at)).total_seconds() / 86_400 except ValueError: return 0.0 @dataclass class UserHistory: user_id: str events: list[FeedbackEvent] = field(default_factory=list) task_completions: list[TaskCompletion] = field(default_factory=list)