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 datetime import datetime
from sqlalchemy.orm import mapped_column, relationship from sqlalchemy.orm import mapped_column, relationship
from sqlalchemy.orm.attributes import Mapped from sqlalchemy.orm.attributes import Mapped
from sqlalchemy.orm.properties import ForeignKey
from sqlalchemy.types import TIMESTAMP, Integer, String, Text from sqlalchemy.types import TIMESTAMP, Integer, String, Text
from sqlalchemy.sql import func from sqlalchemy.sql import func
from sqlalchemy_utc import UtcDateTime from sqlalchemy_utc import UtcDateTime
import app_db import app_db
import spec
class Event(app_db.BaseModel): class Event(app_db.BaseModel):
@ -14,10 +16,30 @@ class Event(app_db.BaseModel):
name: Mapped[str] = mapped_column(String(255), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str] = mapped_column(Text, nullable=True) description: Mapped[str] = mapped_column(Text, nullable=True)
start_time: Mapped[datetime] = mapped_column(UtcDateTime, nullable=False) 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()) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=func.now())
team: Mapped["Team"] = relationship("Team", back_populates="events") 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.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") teams: Mapped[list["PlayerTeam"]] = relationship(back_populates="player")
auth_sessions: Mapped[list["AuthSession"]] = 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()) 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.auth_session import AuthSession
from models.player_event import PlayerEvent
from models.player_team import PlayerTeam 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") players: Mapped[list["PlayerTeam"]] = relationship(back_populates="team")
invites: Mapped[list["TeamInvite"]] = relationship(back_populates="team") invites: Mapped[list["TeamInvite"]] = relationship(back_populates="team")
integrations: Mapped[list["TeamIntegration"]] = 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()) 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.player_team import PlayerTeam
from models.team_integration import TeamIntegration from models.team_integration import TeamIntegration
from models.team_invite import TeamInvite from models.team_invite import TeamInvite
from models.event import Event