Add event management endpoints

- Implemented endpoints for creating, retrieving, and managing events.
- Added `EventSchema` for event serialization and deserialization.
- Updated `Event` model to include relationships with `PlayerEvent` and `Team`.
- Modified `Player` and `Team` models to include relationships with `Event`.
- Added new file `events.py` to handle event-related API routes.
master
John Montagu, the 4th Earl of Sandvich 2024-11-20 15:12:44 -08:00
parent 3394f2271e
commit ea030e012d
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
4 changed files with 144 additions and 1 deletions

View File

@ -0,0 +1,117 @@
#! /usr/bin/env python3
# vim:fenc=utf-8
#
# Copyright © 2024 sandvich <sandvich@archtop>
#
# Distributed under terms of the MIT license.
from datetime import datetime
from flask import Blueprint, abort
from spectree import Response
from models.player_event import PlayerEvent
from models.player import Player
from spec import BaseModel, spec
from middleware import assert_team_authority, requires_authentication, requires_team_membership
from models.event import Event, EventSchema
from models.player_team import PlayerTeam
from app_db import db
api_events = Blueprint("events", __name__, url_prefix="/events")
@api_events.get("/<int:event_id>")
@spec.validate(
resp=Response(
HTTP_200=EventSchema,
),
operation_id="get_event",
)
def get_event(event_id: int):
event = db.session.query(Event).filter(Event.id == event_id).one_or_none()
if not event:
abort(404)
return EventSchema.from_model(event).dict(by_alias=True)
@api_events.get("/team/id/<int:team_id>")
@spec.validate(
resp=Response(
HTTP_200=list[EventSchema],
),
operation_id="get_team_events",
)
def get_team_events(team_id: int):
events = db.session.query(
Event
).filter(
Event.team_id == team_id
).all()
def map_to_schema(event: Event):
return EventSchema.from_model(event).dict(by_alias=True)
return list(map(map_to_schema, events))
@api_events.get("/user/id/<int:user_id>")
def get_user_events(user_id: int):
raise NotImplementedError()
class CreateEventJson(BaseModel):
name: str
description: str
start_time: datetime
player_ids: list[int]
@api_events.post("/team/id/<int:team_id>")
@spec.validate(
resp=Response(
HTTP_200=EventSchema,
)
)
@requires_authentication
@requires_team_membership
def create_event(player_team: PlayerTeam, json: CreateEventJson, **_):
event = Event()
event.team_id = player_team.team_id
event.name = json.name
event.description = json.description
event.start_time = json.start_time
db.session.add(event)
db.session.flush()
db.session.refresh(event)
players_teams = db.session.query(
PlayerTeam
).join(
Player
).where(
PlayerTeam.team_id == player_team.team_id
).where(
PlayerTeam.player_id.in_(json.player_ids)
).all()
for player_team in players_teams:
player = player_team.player
player_event = PlayerEvent()
player_event.player_id = player.steam_id
player_event.event_id = event.id
db.session.add(player_event)
db.session.commit()
return EventSchema.from_model(event).dict(by_alias=True), 200
@api_events.patch("/<int:event_id>/players")
@requires_authentication
@requires_team_membership
def set_event_players(player_team: PlayerTeam, event_id: int, **_):
assert_team_authority(player_team, None)
# merge players into event
db.session.query(Event).filter(Event.id == event_id).update({"players": []})
raise NotImplementedError()

View File

@ -1,10 +1,12 @@
from datetime import datetime
from sqlalchemy.orm import mapped_column, relationship
from sqlalchemy.orm.attributes import Mapped
from sqlalchemy.orm.properties import ForeignKey
from sqlalchemy.types import TIMESTAMP, Integer, String, Text
from sqlalchemy.sql import func
from sqlalchemy_utc import UtcDateTime
import app_db
import spec
class Event(app_db.BaseModel):
@ -14,10 +16,30 @@ class Event(app_db.BaseModel):
name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=True)
start_time: Mapped[datetime] = mapped_column(UtcDateTime, nullable=False)
team_id: Mapped[int] = mapped_column(Integer, nullable=False)
team_id: Mapped[int] = mapped_column(ForeignKey("teams.id"), nullable=False)
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
team: Mapped["Team"] = relationship("Team", back_populates="events")
players: Mapped["PlayerEvent"] = relationship("PlayerEvent", back_populates="event")
class EventSchema(spec.BaseModel):
id: int
team_id: int
name: str
description: str
start_time: datetime
created_at: datetime
@classmethod
def from_model(cls, model: Event) -> "EventSchema":
return cls(
id=model.id,
name=model.name,
description=model.description,
start_time=model.start_time,
team_id=model.team_id,
created_at=model.created_at,
)
from models.team import Team
from models.player_event import PlayerEvent

View File

@ -15,6 +15,7 @@ class Player(app_db.BaseModel):
teams: Mapped[list["PlayerTeam"]] = relationship(back_populates="player")
auth_sessions: Mapped[list["AuthSession"]] = relationship(back_populates="player")
events: Mapped[list["PlayerEvent"]] = relationship(back_populates="player")
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
@ -28,4 +29,5 @@ class PlayerSchema(spec.BaseModel):
from models.auth_session import AuthSession
from models.player_event import PlayerEvent
from models.player_team import PlayerTeam

View File

@ -19,6 +19,7 @@ class Team(app_db.BaseModel):
players: Mapped[list["PlayerTeam"]] = relationship(back_populates="team")
invites: Mapped[list["TeamInvite"]] = relationship(back_populates="team")
integrations: Mapped[list["TeamIntegration"]] = relationship(back_populates="team")
events: Mapped[list["Event"]] = relationship(back_populates="team")
created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
@ -42,3 +43,4 @@ class TeamSchema(spec.BaseModel):
from models.player_team import PlayerTeam
from models.team_integration import TeamIntegration
from models.team_invite import TeamInvite
from models.event import Event