"""Data models for the Daily Briefing service."""
from dataclasses import dataclass, field
from datetime import date, datetime
from enum import Enum
from typing import Any
class EventType(str, Enum):
"""Type of calendar event."""
MEETING = "meeting"
FLIGHT = "flight"
HOTEL = "hotel"
CAR_RENTAL = "car_rental"
TRAIN = "train"
OTHER = "other"
class ConflictType(str, Enum):
"""Type of scheduling conflict."""
OVERLAP = "overlap"
BACK_TO_BACK = "back_to_back"
TRAVEL_CONFLICT = "travel_conflict"
class Priority(str, Enum):
"""Priority level."""
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
@dataclass
class CalendarEvent:
"""A calendar event from any source."""
id: str
title: str
start: datetime
end: datetime
source: str
event_type: EventType = EventType.OTHER
location: str | None = None
description: str | None = None
is_all_day: bool = False
attendees: list[str] = field(default_factory=list)
@dataclass
class Conflict:
"""A scheduling conflict between events."""
conflict_type: ConflictType
events: list[CalendarEvent]
description: str
severity: Priority = Priority.MEDIUM
@dataclass
class ActionItem:
"""An action item from a meeting or other source."""
id: str
text: str
source: str
source_id: str | None = None
meeting_title: str | None = None
meeting_date: date | None = None
assignee: str | None = None
due_date: date | None = None
priority: Priority = Priority.MEDIUM
@dataclass
class MeetingSummary:
"""Summary of a past meeting."""
id: str
title: str
date: datetime
duration_minutes: int
participants: list[str]
overview: str | None = None
action_items: list[str] = field(default_factory=list)
key_topics: list[str] = field(default_factory=list)
@dataclass
class TravelInfo:
"""Information about upcoming travel."""
id: str
title: str
start_date: date
end_date: date
destination: str | None = None
events: list[CalendarEvent] = field(default_factory=list)
alerts: list[str] = field(default_factory=list)
@dataclass
class FreeSlot:
"""An available time slot."""
start: datetime
end: datetime
duration_minutes: int
@dataclass
class DailyBriefing:
"""The complete daily briefing."""
date: date
generated_at: datetime
# Today's schedule
events: list[CalendarEvent] = field(default_factory=list)
conflicts: list[Conflict] = field(default_factory=list)
free_slots: list[FreeSlot] = field(default_factory=list)
# Recent meetings
recent_meetings: list[MeetingSummary] = field(default_factory=list)
# Action items
action_items: list[ActionItem] = field(default_factory=list)
# Travel
upcoming_travel: list[TravelInfo] = field(default_factory=list)
travel_alerts: list[str] = field(default_factory=list)
# Summary stats
total_meetings: int = 0
total_meeting_hours: float = 0.0
focus_time_hours: float = 0.0
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
return {
"date": self.date.isoformat(),
"generated_at": self.generated_at.isoformat(),
"schedule": {
"events": [
{
"id": e.id,
"title": e.title,
"start": e.start.isoformat(),
"end": e.end.isoformat(),
"type": e.event_type.value,
"location": e.location,
"source": e.source,
}
for e in self.events
],
"conflicts": [
{
"type": c.conflict_type.value,
"description": c.description,
"severity": c.severity.value,
"events": [e.title for e in c.events],
}
for c in self.conflicts
],
"free_slots": [
{
"start": s.start.isoformat(),
"end": s.end.isoformat(),
"duration_minutes": s.duration_minutes,
}
for s in self.free_slots
],
},
"recent_meetings": [
{
"title": m.title,
"date": m.date.isoformat(),
"duration_minutes": m.duration_minutes,
"overview": m.overview,
"action_items": m.action_items,
}
for m in self.recent_meetings
],
"action_items": [
{
"text": a.text,
"source": a.source,
"meeting_title": a.meeting_title,
"priority": a.priority.value,
}
for a in self.action_items
],
"travel": {
"upcoming": [
{
"title": t.title,
"start_date": t.start_date.isoformat(),
"end_date": t.end_date.isoformat(),
"destination": t.destination,
}
for t in self.upcoming_travel
],
"alerts": self.travel_alerts,
},
"stats": {
"total_meetings": self.total_meetings,
"total_meeting_hours": round(self.total_meeting_hours, 1),
"focus_time_hours": round(self.focus_time_hours, 1),
},
}