From 304001632315afc1eca7e95184577cb42bc650af Mon Sep 17 00:00:00 2001 From: Tiago vasconcelos Date: Tue, 19 Oct 2021 16:12:03 +0100 Subject: [PATCH] events untested commit --- lnbits/extensions/events/README.md | 33 ++ lnbits/extensions/events/__init__.py | 19 + lnbits/extensions/events/config.json | 6 + lnbits/extensions/events/crud.py | 159 ++++++ lnbits/extensions/events/migrations.py | 91 +++ lnbits/extensions/events/models.py | 41 ++ .../events/templates/events/_api_docs.html | 23 + .../events/templates/events/display.html | 207 +++++++ .../events/templates/events/error.html | 35 ++ .../events/templates/events/index.html | 538 ++++++++++++++++++ .../events/templates/events/register.html | 173 ++++++ .../events/templates/events/ticket.html | 45 ++ lnbits/extensions/events/views.py | 107 ++++ lnbits/extensions/events/views_api.py | 211 +++++++ 14 files changed, 1688 insertions(+) create mode 100644 lnbits/extensions/events/README.md create mode 100644 lnbits/extensions/events/__init__.py create mode 100644 lnbits/extensions/events/config.json create mode 100644 lnbits/extensions/events/crud.py create mode 100644 lnbits/extensions/events/migrations.py create mode 100644 lnbits/extensions/events/models.py create mode 100644 lnbits/extensions/events/templates/events/_api_docs.html create mode 100644 lnbits/extensions/events/templates/events/display.html create mode 100644 lnbits/extensions/events/templates/events/error.html create mode 100644 lnbits/extensions/events/templates/events/index.html create mode 100644 lnbits/extensions/events/templates/events/register.html create mode 100644 lnbits/extensions/events/templates/events/ticket.html create mode 100644 lnbits/extensions/events/views.py create mode 100644 lnbits/extensions/events/views_api.py diff --git a/lnbits/extensions/events/README.md b/lnbits/extensions/events/README.md new file mode 100644 index 00000000..11b62fec --- /dev/null +++ b/lnbits/extensions/events/README.md @@ -0,0 +1,33 @@ +# Events + +## Sell tickets for events and use the built-in scanner for registering attendants + +Events alows you to make tickets for an event. Each ticket is in the form of a uniqque QR code. After registering, and paying for ticket, the user gets a QR code to present at registration/entrance. + +Events includes a shareable ticket scanner, which can be used to register attendees. + +## Usage + +1. Create an event\ + ![create event](https://i.imgur.com/dadK1dp.jpg) +2. Fill out the event information: + + - event name + - wallet (normally there's only one) + - event information + - closing date for event registration + - begin and end date of the event + + ![event info](https://imgur.com/KAv68Yr.jpg) + +3. Share the event registration link\ + ![event ticket](https://imgur.com/AQWUOBY.jpg) + + - ticket example\ + ![ticket example](https://i.imgur.com/trAVSLd.jpg) + + - QR code ticket, presented after invoice paid, to present at registration\ + ![event ticket](https://i.imgur.com/M0ROM82.jpg) + +4. Use the built-in ticket scanner to validate registered, and paid, attendees\ + ![ticket scanner](https://i.imgur.com/zrm9202.jpg) diff --git a/lnbits/extensions/events/__init__.py b/lnbits/extensions/events/__init__.py new file mode 100644 index 00000000..da29358b --- /dev/null +++ b/lnbits/extensions/events/__init__.py @@ -0,0 +1,19 @@ +from fastapi import APIRouter + +from lnbits.db import Database +from lnbits.helpers import template_renderer + +db = Database("ext_events") + + +events_ext: APIRouter = APIRouter( + prefix="/events", + tags=["Events"] +) + +def events_renderer(): + return template_renderer(["lnbits/extensions/events/templates"]) + +from .views import * # noqa +from .views_api import * # noqa + diff --git a/lnbits/extensions/events/config.json b/lnbits/extensions/events/config.json new file mode 100644 index 00000000..6bc144ab --- /dev/null +++ b/lnbits/extensions/events/config.json @@ -0,0 +1,6 @@ +{ + "name": "Events", + "short_description": "Sell and register event tickets", + "icon": "local_activity", + "contributors": ["benarc"] +} diff --git a/lnbits/extensions/events/crud.py b/lnbits/extensions/events/crud.py new file mode 100644 index 00000000..4a24b797 --- /dev/null +++ b/lnbits/extensions/events/crud.py @@ -0,0 +1,159 @@ +from typing import List, Optional, Union + +from lnbits.helpers import urlsafe_short_hash + +from . import db +from .models import CreateEvent, Events, Tickets + +# TICKETS + + +async def create_ticket( + payment_hash: str, wallet: str, event: str, name: str, email: str +) -> Tickets: + await db.execute( + """ + INSERT INTO events.ticket (id, wallet, event, name, email, registered, paid) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + (payment_hash, wallet, event, name, email, False, False), + ) + + ticket = await get_ticket(payment_hash) + assert ticket, "Newly created ticket couldn't be retrieved" + return ticket + + +async def set_ticket_paid(payment_hash: str) -> Tickets: + row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,)) + if row[6] != True: + await db.execute( + """ + UPDATE events.ticket + SET paid = true + WHERE id = ? + """, + (payment_hash,), + ) + + eventdata = await get_event(row[2]) + assert eventdata, "Couldn't get event from ticket being paid" + + sold = eventdata.sold + 1 + amount_tickets = eventdata.amount_tickets - 1 + await db.execute( + """ + UPDATE events.events + SET sold = ?, amount_tickets = ? + WHERE id = ? + """, + (sold, amount_tickets, row[2]), + ) + + ticket = await get_ticket(payment_hash) + assert ticket, "Newly updated ticket couldn't be retrieved" + return ticket + + +async def get_ticket(payment_hash: str) -> Optional[Tickets]: + row = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (payment_hash,)) + return Tickets(**row) if row else None + + +async def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]: + if isinstance(wallet_ids, str): + wallet_ids = [wallet_ids] + + q = ",".join(["?"] * len(wallet_ids)) + rows = await db.fetchall( + f"SELECT * FROM events.ticket WHERE wallet IN ({q})", (*wallet_ids,) + ) + return [Tickets(**row) for row in rows] + + +async def delete_ticket(payment_hash: str) -> None: + await db.execute("DELETE FROM events.ticket WHERE id = ?", (payment_hash,)) + + +# EVENTS + + +async def create_event( + data: CreateEvent +) -> Events: + event_id = urlsafe_short_hash() + await db.execute( + """ + INSERT INTO events.events (id, wallet, name, info, closing_date, event_start_date, event_end_date, amount_tickets, price_per_ticket, sold) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + event_id, + data.wallet, + data.name, + data.info, + data.closing_date, + data.event_start_date, + data.event_end_date, + data.amount_tickets, + data.price_per_ticket, + 0, + ), + ) + + event = await get_event(event_id) + assert event, "Newly created event couldn't be retrieved" + return event + + +async def update_event(event_id: str, **kwargs) -> Events: + q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()]) + await db.execute( + f"UPDATE events.events SET {q} WHERE id = ?", (*kwargs.values(), event_id) + ) + event = await get_event(event_id) + assert event, "Newly updated event couldn't be retrieved" + return event + + +async def get_event(event_id: str) -> Optional[Events]: + row = await db.fetchone("SELECT * FROM events.events WHERE id = ?", (event_id,)) + return Events(**row) if row else None + + +async def get_events(wallet_ids: Union[str, List[str]]) -> List[Events]: + if isinstance(wallet_ids, str): + wallet_ids = [wallet_ids] + + q = ",".join(["?"] * len(wallet_ids)) + rows = await db.fetchall( + f"SELECT * FROM events.events WHERE wallet IN ({q})", (*wallet_ids,) + ) + + return [Events(**row) for row in rows] + + +async def delete_event(event_id: str) -> None: + await db.execute("DELETE FROM events.events WHERE id = ?", (event_id,)) + + +# EVENTTICKETS + + +async def get_event_tickets(event_id: str, wallet_id: str) -> List[Tickets]: + rows = await db.fetchall( + "SELECT * FROM events.ticket WHERE wallet = ? AND event = ?", + (wallet_id, event_id), + ) + return [Tickets(**row) for row in rows] + + +async def reg_ticket(ticket_id: str) -> List[Tickets]: + await db.execute( + "UPDATE events.ticket SET registered = ? WHERE id = ?", (True, ticket_id) + ) + ticket = await db.fetchone("SELECT * FROM events.ticket WHERE id = ?", (ticket_id,)) + rows = await db.fetchall( + "SELECT * FROM events.ticket WHERE event = ?", (ticket[1],) + ) + return [Tickets(**row) for row in rows] diff --git a/lnbits/extensions/events/migrations.py b/lnbits/extensions/events/migrations.py new file mode 100644 index 00000000..d8f3d94e --- /dev/null +++ b/lnbits/extensions/events/migrations.py @@ -0,0 +1,91 @@ +async def m001_initial(db): + + await db.execute( + """ + CREATE TABLE events.events ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + name TEXT NOT NULL, + info TEXT NOT NULL, + closing_date TEXT NOT NULL, + event_start_date TEXT NOT NULL, + event_end_date TEXT NOT NULL, + amount_tickets INTEGER NOT NULL, + price_per_ticket INTEGER NOT NULL, + sold INTEGER NOT NULL, + time TIMESTAMP NOT NULL DEFAULT """ + + db.timestamp_now + + """ + ); + """ + ) + + await db.execute( + """ + CREATE TABLE events.tickets ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + event TEXT NOT NULL, + name TEXT NOT NULL, + email TEXT NOT NULL, + registered BOOLEAN NOT NULL, + time TIMESTAMP NOT NULL DEFAULT """ + + db.timestamp_now + + """ + ); + """ + ) + + +async def m002_changed(db): + + await db.execute( + """ + CREATE TABLE events.ticket ( + id TEXT PRIMARY KEY, + wallet TEXT NOT NULL, + event TEXT NOT NULL, + name TEXT NOT NULL, + email TEXT NOT NULL, + registered BOOLEAN NOT NULL, + paid BOOLEAN NOT NULL, + time TIMESTAMP NOT NULL DEFAULT """ + + db.timestamp_now + + """ + ); + """ + ) + + for row in [list(row) for row in await db.fetchall("SELECT * FROM events.tickets")]: + usescsv = "" + + for i in range(row[5]): + if row[7]: + usescsv += "," + str(i + 1) + else: + usescsv += "," + str(1) + usescsv = usescsv[1:] + await db.execute( + """ + INSERT INTO events.ticket ( + id, + wallet, + event, + name, + email, + registered, + paid + ) + VALUES (?, ?, ?, ?, ?, ?, ?) + """, + ( + row[0], + row[1], + row[2], + row[3], + row[4], + row[5], + True, + ), + ) + await db.execute("DROP TABLE events.tickets") diff --git a/lnbits/extensions/events/models.py b/lnbits/extensions/events/models.py new file mode 100644 index 00000000..c775382f --- /dev/null +++ b/lnbits/extensions/events/models.py @@ -0,0 +1,41 @@ +from fastapi.param_functions import Query +from pydantic import BaseModel + + +class CreateEvent(BaseModel): + wallet: str + name: str + info: str + closing_date: str + event_start_date: str + event_end_date: str + amount_tickets: int = Query(..., ge=0) + price_per_ticket: int = Query(..., ge=0) + +class CreateTicket(BaseModel): + name: str + email: str + +class Events(BaseModel): + id: str + wallet: str + name: str + info: str + closing_date: str + event_start_date: str + event_end_date: str + amount_tickets: int + price_per_ticket: int + sold: int + time: int + + +class Tickets(BaseModel): + id: str + wallet: str + event: str + name: str + email: str + registered: bool + paid: bool + time: int diff --git a/lnbits/extensions/events/templates/events/_api_docs.html b/lnbits/extensions/events/templates/events/_api_docs.html new file mode 100644 index 00000000..a5c82174 --- /dev/null +++ b/lnbits/extensions/events/templates/events/_api_docs.html @@ -0,0 +1,23 @@ + + + +
+ Events: Sell and register ticket waves for an event +
+

+ Events alows you to make a wave of tickets for an event, each ticket is + in the form of a unqiue QRcode, which the user presents at registration. + Events comes with a shareable ticket scanner, which can be used to + register attendees.
+ + Created by, Ben Arc + +

+
+
+
diff --git a/lnbits/extensions/events/templates/events/display.html b/lnbits/extensions/events/templates/events/display.html new file mode 100644 index 00000000..4c1f557f --- /dev/null +++ b/lnbits/extensions/events/templates/events/display.html @@ -0,0 +1,207 @@ +{% extends "public.html" %} {% block page %} +
+
+ + +

{{ event_name }}

+
+
{{ event_info }}
+
+ + + + +
+ Submit + Cancel +
+
+
+
+ + +
+ Link to your ticket! +

+

You'll be redirected in a few moments...

+
+
+
+ + + + + + +
+ Copy invoice + Close +
+
+
+
+ +{% endblock %} {% block scripts %} + +{% endblock %} diff --git a/lnbits/extensions/events/templates/events/error.html b/lnbits/extensions/events/templates/events/error.html new file mode 100644 index 00000000..f231177b --- /dev/null +++ b/lnbits/extensions/events/templates/events/error.html @@ -0,0 +1,35 @@ +{% extends "public.html" %} {% block page %} +
+
+ + +
+

{{ event_name }} error

+
+ + +
{{ event_error }}
+
+
+
+
+
+ + {% endblock %} {% block scripts %} + + + + {% endblock %} +
diff --git a/lnbits/extensions/events/templates/events/index.html b/lnbits/extensions/events/templates/events/index.html new file mode 100644 index 00000000..1ad3d885 --- /dev/null +++ b/lnbits/extensions/events/templates/events/index.html @@ -0,0 +1,538 @@ +{% extends "base.html" %} {% from "macros.jinja" import window_vars with context +%} {% block page %} +
+
+ + + New Event + + + + + +
+
+
Events
+
+
+ Export to CSV +
+
+ + {% raw %} + + + {% endraw %} + +
+
+ + + +
+
+
Tickets
+
+
+ Export to CSV +
+
+ + {% raw %} + + + {% endraw %} + +
+
+
+
+ + +
+ {{SITE_TITLE}} Events extension +
+
+ + + {% include "events/_api_docs.html" %} + +
+
+ + + + +
+
+ +
+
+ + +
+
+ + +
+
Ticket closing date
+
+ +
+
+ +
+
Event begins
+
+ +
+
+ +
+
Event ends
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ Update Event + Create Event + Cancel +
+
+
+
+
+{% endblock %} {% block scripts %} {{ window_vars(user) }} + +{% endblock %} diff --git a/lnbits/extensions/events/templates/events/register.html b/lnbits/extensions/events/templates/events/register.html new file mode 100644 index 00000000..4dff9afb --- /dev/null +++ b/lnbits/extensions/events/templates/events/register.html @@ -0,0 +1,173 @@ +{% extends "public.html" %} {% block page %} + +
+
+ + +
+

{{ event_name }} Registration

+
+ +
+ + Scan ticket +
+
+
+ + + + + {% raw %} + + + {% endraw %} + + + +
+ + + +
+ +
+
+ Cancel +
+
+
+
+{% endblock %} {% block scripts %} + +{% endblock %} diff --git a/lnbits/extensions/events/templates/events/ticket.html b/lnbits/extensions/events/templates/events/ticket.html new file mode 100644 index 00000000..a53f834f --- /dev/null +++ b/lnbits/extensions/events/templates/events/ticket.html @@ -0,0 +1,45 @@ +{% extends "public.html" %} {% block page %} +
+
+ + +
+

{{ ticket_name }} Ticket

+
+
+ Bookmark, print or screenshot this page,
+ and present it for registration! +
+
+ + +
+ + Print +
+
+
+
+
+{% endblock %} {% block scripts %} + +{% endblock %} diff --git a/lnbits/extensions/events/views.py b/lnbits/extensions/events/views.py new file mode 100644 index 00000000..46aba428 --- /dev/null +++ b/lnbits/extensions/events/views.py @@ -0,0 +1,107 @@ +from datetime import date, datetime +from http import HTTPStatus + +from fastapi import Request +from fastapi.params import Depends +from fastapi.templating import Jinja2Templates +from starlette.exceptions import HTTPException +from starlette.responses import HTMLResponse + +from lnbits.core.models import User +from lnbits.decorators import check_user_exists + +from . import events_ext, events_renderer +from .crud import get_event, get_ticket + +templates = Jinja2Templates(directory="templates") + + +@events_ext.get("/", response_class=HTMLResponse) +async def index(request: Request, user: User = Depends(check_user_exists)): + return events_renderer.TemplateResponse("events/index.html", {"request": request, "user": user.dict()}) + + +@events_ext.get("/{event_id}", response_class=HTMLResponse) +async def display(request: Request, event_id): + event = await get_event(event_id) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." + ) + + if event.amount_tickets < 1: + return events_renderer.TemplateResponse( + "events/error.html", + { + "request": request, + "event_name": event.name, + "event_error": "Sorry, tickets are sold out :(" + } + ) + datetime_object = datetime.strptime(event.closing_date, "%Y-%m-%d").date() + if date.today() > datetime_object: + return events_renderer.TemplateResponse( + "events/error.html", + { + "request": request, + "event_name": event.name, + "event_error": "Sorry, ticket closing date has passed :(" + } + ) + + return events_renderer.TemplateResponse( + "events/display.html", + { + "request": request, + "event_id": event_id, + "event_name": event.name, + "event_info": event.info, + "event_price": event.price_per_ticket, + } + + ) + + +@events_ext.get("/ticket/{ticket_id}", response_class=HTMLResponse) +async def ticket(request: Request, ticket_id): + ticket = await get_ticket(ticket_id) + if not ticket: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Ticket does not exist." + ) + + event = await get_event(ticket.event) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." + ) + + return events_renderer.TemplateResponse( + "events/ticket.html", + { + "request": request, + "ticket_id": ticket_id, + "ticket_name": event.name, + "ticket_info": event.info, + + } + ) + + +@events_ext.get("/register/{event_id}", response_class=HTMLResponse) +async def register(request: Request, event_id): + event = await get_event(event_id) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." + ) + + return events_renderer.TemplateResponse( + "events/register.html", + { + "request": request, + "event_id": event_id, + "event_name": event.name, + "wallet_id": event.wallet, + } + ) diff --git a/lnbits/extensions/events/views_api.py b/lnbits/extensions/events/views_api.py new file mode 100644 index 00000000..2983b45b --- /dev/null +++ b/lnbits/extensions/events/views_api.py @@ -0,0 +1,211 @@ +from http import HTTPStatus + +from fastapi.param_functions import Query +from fastapi.params import Depends +from starlette.exceptions import HTTPException +from starlette.requests import Request + +from lnbits.core.crud import get_user, get_wallet +from lnbits.core.services import check_invoice_status, create_invoice +from lnbits.decorators import WalletTypeInfo, get_key_type +from lnbits.extensions.events.models import CreateEvent, CreateTicket + +from . import events_ext +from .crud import ( + create_event, + create_ticket, + delete_event, + delete_ticket, + get_event, + get_event_tickets, + get_events, + get_ticket, + get_tickets, + reg_ticket, + set_ticket_paid, + update_event, +) + +# Events + + +@events_ext.get("/api/v1/events") +async def api_events( + r: Request, + all_wallets: bool = Query(False), + wallet: WalletTypeInfo = Depends(get_key_type), +): + wallet_ids = [wallet.wallet.id] + + if all_wallets: + wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids + + return [event.dict() for event in await get_events(wallet_ids)] + +@events_ext.post("/api/v1/events") +@events_ext.put("/api/v1/events/") +async def api_event_create(data: CreateEvent, event_id=None, wallet: WalletTypeInfo = Depends(get_key_type)): + if event_id: + event = await get_event(event_id) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Event does not exist." + ) + + if event.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail=f"Not your event." + ) + event = await update_event(event_id, **data) + else: + event = await create_event(**data) + + return event.dict() + + +@events_ext.delete("/api/v1/events/{event_id}") +async def api_form_delete(event_id, wallet: WalletTypeInfo = Depends(get_key_type)): + event = await get_event(event_id) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Event does not exist." + ) + + if event.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail=f"Not your event." + ) + + await delete_event(event_id) + raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + + +#########Tickets########## + + +@events_ext.get("/api/v1/tickets") +async def api_tickets( + r: Request, + all_wallets: bool = Query(False), + wallet: WalletTypeInfo = Depends(get_key_type), +): + wallet_ids = [wallet.wallet.id] + + if all_wallets: + wallet_ids = (await get_user(wallet.wallet.user)).wallet_ids + + return [ticket.dict() for ticket in await get_tickets(wallet_ids)] + + +@events_ext.post("/api/v1/tickets/{event_id}/{sats}") +async def api_ticket_make_ticket(event_id, sats, data: CreateTicket): + event = await get_event(event_id) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Event does not exist." + ) + try: + payment_hash, payment_request = await create_invoice( + wallet_id=event.wallet, + amount=int(sats), + memo=f"{event_id}", + extra={"tag": "events"}, + ) + except Exception as e: + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=str(e)) + + ticket = await create_ticket( + payment_hash=payment_hash, wallet=event.wallet, event=event_id, name=data.name, email=data.email + ) + + if not ticket: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Event could not be fetched." + ) + + return {"payment_hash": payment_hash, "payment_request": payment_request} + + +@events_ext.get("/api/v1/tickets/{payment_hash}") +async def api_ticket_send_ticket(payment_hash): + ticket = await get_ticket(payment_hash) + + try: + status = await check_invoice_status(ticket.wallet, payment_hash) + is_paid = not status.pending + + except Exception: + raise HTTPException(status_code=HTTPStatus.NOT_FOUND, detail="Not paid") + + if is_paid: + wallet = await get_wallet(ticket.wallet) + payment = await wallet.get_payment(payment_hash) + await payment.set_pending(False) + ticket = await set_ticket_paid(payment_hash=payment_hash) + + return {"paid": True, "ticket_id": ticket.id} + + return {"paid": False} + + +@events_ext.delete("/api/v1/tickets/{ticket_id}") +async def api_ticket_delete(ticket_id, wallet: WalletTypeInfo = Depends(get_key_type)): + ticket = await get_ticket(ticket_id) + if not ticket: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail=f"Ticket does not exist." + ) + + if ticket.wallet != wallet.wallet.id: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail=f"Not your ticket." + ) + + await delete_ticket(ticket_id) + raise HTTPException(status_code=HTTPStatus.NO_CONTENT) + + + +# Event Tickets + + +@events_ext.get("/api/v1/eventtickets/{wallet_id}/{event_id}") +async def api_event_tickets(wallet_id, event_id): + return [ + ticket.dict() + for ticket in await get_event_tickets( + wallet_id=wallet_id, event_id=event_id + ) + ] + + +@events_ext.get("/api/v1/register/ticket/{ticket_id}") +async def api_event_register_ticket(ticket_id): + ticket = await get_ticket(ticket_id) + if not ticket: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, + detail="Ticket does not exist." + ) + + if not ticket.paid: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Ticket not paid for." + ) + + if ticket.registered == True: + raise HTTPException( + status_code=HTTPStatus.FORBIDDEN, + detail="Ticket already registered" + ) + + return [ticket.dict() for ticket in await reg_ticket(ticket_id)]