From 2937c50572e734da6dd51c22c1645124d1c8224e Mon Sep 17 00:00:00 2001 From: cyfraeviolae Date: Wed, 3 Apr 2024 03:58:43 -0400 Subject: ics --- app.py | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/app.py b/app.py index 2cfc8e8..925ec27 100644 --- a/app.py +++ b/app.py @@ -6,6 +6,7 @@ import secrets import random from enum import Enum from dataclasses import dataclass +import io import datetime from contextlib import asynccontextmanager from typing import Any @@ -22,14 +23,19 @@ from litestar.contrib.jinja import JinjaTemplateEngine from litestar.exceptions import HTTPException from litestar.template.config import TemplateConfig from litestar.response import Template, Redirect +from litestar.response.file import ASGIFileResponse +from litestar.response.streaming import ASGIStreamingResponse from litestar.static_files import create_static_files_router from litestar.enums import RequestEncodingType from litestar.params import Body from litestar import Request from litestar.datastructures import State -# automake calendar invite ics +import ics + +# TODO admin remove attendees # use url_fors, timezones? +# error handling, auth errors, sql errors, input validation class Base(DeclarativeBase): pass @@ -57,6 +63,16 @@ class Event(Base): def get_invites(self): return json.loads(self.invites) + def to_ics(self): + c = ics.Calendar() + e = ics.Event() + e.name = self.title + e.begin = self.time + e.location = self.location + e.description = self.description + c.events.add(e) + return c + @asynccontextmanager async def db_connection(app: Litestar) -> AsyncGenerator[None, None]: engine = getattr(app.state, "engine", None) @@ -85,14 +101,6 @@ parts = [ def gen_iden(): return "-".join(random.choices(parts, k=8)) -iden = 'Weave-Stream-Limber-Exalted-Sluice-Reaper-Myrtle-Incite' - -title = 'Hot Pot at the Cafe Cyfrae Violae' -when = datetime.datetime.fromisoformat("2024-04-07T13:00:00") -where = '133 E 4th St, Apt 6, New York NY 10003' -what = "Hot pot, Anxi oolong, baijiu, Hua Zhou." -EVENTS = {} - @get("/") async def index() -> Template: return Template(template_name="index.html") @@ -112,6 +120,19 @@ async def event(state: State, iden: str, password: str = "") -> Template: context = dict(event=event, manage=manage) return Template(template_name="event.html", context=context) +@get("/calendar/{iden:str}") +async def calendar(state: State, iden: str) -> ASGIStreamingResponse: + async with sessionmaker(bind=state.engine) as session: + async with session.begin(): + query = select(Event).where(Event.iden == iden) + result = await session.execute(query) + event = result.scalar_one() + ics = event.to_ics() + f = io.StringIO(ics.serialize()) + return ASGIStreamingResponse(iterator=f, media_type='application/octet-stream', headers={ + 'Content-Disposition': 'inline; filename=event.ics', + }) + @dataclass class EditRequest: title: str @@ -164,7 +185,6 @@ async def join(state: State, request: Request, iden: str, data: Annotated[JoinRe result = await session.execute(query) event = result.scalar_one() name = data.name - # TODO if name exists reject invites = json.loads(event.invites) invites.append(name) event.invites = json.dumps(invites) @@ -177,6 +197,7 @@ app = Litestar( route_handlers=[ index, event, + calendar, create, edit, join, -- cgit v1.2.3