Skip to content

Commit

Permalink
feat: generate calendars for league events
Browse files Browse the repository at this point in the history
  • Loading branch information
brownben committed Apr 11, 2024
1 parent f0c2dce commit db682e4
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
2 changes: 2 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies = [
"fastapi~=0.110.1",
"google-auth~=2.29.0",
"httpx~=0.27.0",
"icalendar~=5.0.12",
"piccolo==0.119.0",
"uvicorn~=0.29.0",
"requests~=2.31.0"
Expand All @@ -26,6 +27,7 @@ optional-dependencies.dev = [
"respx~=0.21.1",
"ruff~=0.3.5",
"time-machine~=2.14.1",
"types-icalendar~=5.0.0",
"types-requests~=2.31.0"
]

Expand Down
7 changes: 7 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ h11==0.14.0
httpcore==1.0.5
# via httpx
httpx==0.27.0
icalendar==5.0.12
idna==3.6
# via
# anyio
Expand Down Expand Up @@ -74,9 +75,15 @@ pydantic==1.10.15
# via
# fastapi
# piccolo
python-dateutil==2.9.0.post0
# via icalendar
pytz==2024.1
# via icalendar
requests==2.31.0
rsa==4.9
# via google-auth
six==1.16.0
# via python-dateutil
sniffio==1.3.1
# via
# anyio
Expand Down
52 changes: 52 additions & 0 deletions backend/src/routes/leagues.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import asyncio
from typing import Awaitable, Iterable

import icalendar
from fastapi import Depends, Path
from fastapi.responses import PlainTextResponse
from fastapi.routing import APIRouter

from ..database import (
Expand Down Expand Up @@ -166,6 +168,56 @@ async def get_league_events(
return events


def generate_event_calendar_description(event: EventWithLeagueDetails) -> str:
description = ""

if event.organiser:
description += f"Organised by {event.organiser}\n"
if event.part_of:
description += f"Part of {event.part_of}\n"
if event.more_information:
description += f"\n{event.more_information}\n"
if event.website:
description += f"\n{event.website}\n"

return description


@router.get("/{league_name}/events/calendar")
async def get_league_events_calendar(
league_name: str = Path(
title="League Name",
description="Name of the league to get the results for",
example="Sprintelope 2021",
),
) -> PlainTextResponse:
league, events = await asyncio.gather(
Leagues.get_by_name(league_name),
Events.get_by_league(league_name),
)

if not league:
raise HTTP_404(f"Couldn't find league with name `{league_name}`")

calendar = icalendar.Calendar()
calendar["name"] = league.name
calendar.add("x-wr-calname", league.name)

for event in events:
calendar_event = icalendar.Event()
calendar_event.add("uid", f"{event.id}@munroleagues.com")
calendar_event.add("summary", f"{league.name} - {event.name}")
calendar_event.add("description", generate_event_calendar_description(event))
calendar_event.add("dtstart", event.date)
calendar_event.add("location", event.name)
calendar_event.add("url", event.website)
calendar_event.add("transp", "TRANSPARENT") # doesn't show as busy

calendar.add_component(calendar_event)

return PlainTextResponse(calendar.to_ical(), media_type="text/calendar")


def get_results_for_event(
event: LeagueEvent, league_class: LeagueClass
) -> Awaitable[Iterable[Result]]:
Expand Down
22 changes: 22 additions & 0 deletions backend/src/types/ics.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from datetime import date
from typing import Iterable

class Calendar:
events: set[Event]
def __init__(self) -> None: ...
def serialize_iter(self) -> Iterable[str]: ...

class Event:
def __init__(
self,
name: str | None,
begin: date | None,
description: str | None,
location: str | None,
url: str | None,
organizer: Organizer | None,
) -> None: ...
def make_all_day(self) -> None: ...

class Organizer:
def __init__(self, email: str | None, common_name: str | None) -> None: ...

0 comments on commit db682e4

Please sign in to comment.