Files
Senad Uka e855650f09 M1: backend core + proposal engine
- 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>
2026-07-11 09:45:06 +02:00

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()