- FastAPI skeleton, SQLAlchemy models (§13), Alembic initial migration - SchedulingProvider interface with google_calendar (free/busy read-only), partner_api (Appendix B client) and mock implementations - Proposal engine: create → provider-routed delivery → owner actions (resolve/confirm+SMS/reject) → expiry + reminders (§9) - Signed single-use action links, .ics METHOD:REQUEST attachment - Partner outcome webhook with HMAC verification + polling fallback - SmsProvider (console) with Bosnian templates (§5.5), EmailProvider (console/SMTP) - Fake partner API server in tests/ — Appendix B reference implementation - 43 tests: slot math, proposal lifecycle, action links, partner contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
43 lines
1.2 KiB
Python
43 lines
1.2 KiB
Python
"""Generate the .ics attachment (METHOD:REQUEST) for the first-choice slot (§9.1).
|
|
|
|
The .ics is a convenience so Gmail offers "Add to calendar" — the owner can move
|
|
and rearrange the event before saving. Gogo never writes to the owner's calendar.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from icalendar import Calendar, Event, vCalAddress, vText
|
|
|
|
from gogo.config import get_settings
|
|
|
|
|
|
def build_ics(
|
|
*,
|
|
uid: str,
|
|
start: datetime,
|
|
end: datetime,
|
|
summary: str,
|
|
description: str,
|
|
organizer_email: str = "noreply@gogotelefon.ba",
|
|
) -> bytes:
|
|
cal = Calendar()
|
|
cal.add("prodid", "-//Gogo Telefon//gogotelefon.ba//BS")
|
|
cal.add("version", "2.0")
|
|
cal.add("method", "REQUEST")
|
|
|
|
ev = Event()
|
|
ev.add("uid", f"{uid}@gogotelefon.ba")
|
|
ev.add("dtstart", start)
|
|
ev.add("dtend", end)
|
|
ev.add("summary", summary)
|
|
ev.add("description", description)
|
|
ev.add("status", "TENTATIVE")
|
|
organizer = vCalAddress(f"MAILTO:{organizer_email}")
|
|
organizer.params["cn"] = vText("Gogo Telefon")
|
|
ev["organizer"] = organizer
|
|
ev.add("url", get_settings().base_url)
|
|
cal.add_component(ev)
|
|
return cal.to_ical()
|