From 1c71df781c581558164bc73e7f519e93fa2da868 Mon Sep 17 00:00:00 2001 From: julius Date: Thu, 8 May 2025 07:07:14 +0200 Subject: [PATCH] feat: adjustments for Demo Team (`team_id = 42`) --- cutt/analysis.py | 64 ++++++++++++++++++++++++++++++++++++++++++----- cutt/main.py | 4 +++ cutt/player.py | 22 +++++++++++++++- cutt/security.py | 2 ++ src/Footer.tsx | 5 ++-- src/MVPChart.tsx | 1 + src/Network.tsx | 1 + src/TeamPanel.tsx | 1 + 8 files changed, 91 insertions(+), 9 deletions(-) diff --git a/cutt/analysis.py b/cutt/analysis.py index 30da656..eff8809 100644 --- a/cutt/analysis.py +++ b/cutt/analysis.py @@ -1,4 +1,6 @@ import io +import itertools +import random import base64 from typing import Annotated from fastapi import APIRouter, HTTPException, Security, status @@ -12,6 +14,7 @@ import numpy as np import matplotlib from cutt.security import TeamScopedRequest, verify_team_scope +from cutt.demo import demo_players matplotlib.use("agg") import matplotlib.pyplot as plt @@ -55,14 +58,65 @@ def sociogram_json(): def graph_json( - request: Annotated[ - TeamScopedRequest, Security(verify_team_scope, scopes=["analysis"]) - ], + request: Annotated[TeamScopedRequest, Security(verify_team_scope)], networkx_graph: bool = False, ): nodes = [] edges = [] player_map = {} + if request.team_id == 42: + players = [request.user] + demo_players + random.seed(42) + for p in players: + nodes.append({"id": p.display_name, "label": p.display_name}) + for p, other in itertools.permutations(players, 2): + value = random.random() + if value > 0.5: + edges.append( + { + "id": f"{p.display_name}->{other.display_name}", + "source": p.display_name, + "target": other.display_name, + "size": max(value, 0.3), + "data": { + "relation": 2, + "origSize": max(value, 0.3), + "origFill": "#bed4ff", + }, + } + ) + elif value < 0.1: + edges.append( + { + "id": f"{p.display_name}-x>{other.display_name}", + "source": p.display_name, + "target": other.display_name, + "size": 0.3, + "data": {"relation": 0, "origSize": 0.3, "origFill": "#ff7c7c"}, + "fill": "#ff7c7c", + } + ) + G = nx.DiGraph() + G.add_nodes_from([n["id"] for n in nodes]) + G.add_weighted_edges_from( + [ + ( + e["source"], + e["target"], + e["size"] if e["data"]["relation"] == 2 else -e["size"], + ) + for e in edges + ] + ) + in_degrees = G.in_degree(weight="weight") + nodes = [ + dict(node, **{"data": {"inDegree": in_degrees[node["id"]]}}) + for node in nodes + ] + if networkx_graph: + return G + return JSONResponse({"nodes": nodes, "edges": edges}) + with Session(engine) as session: players = session.exec( select(P) @@ -240,9 +294,7 @@ async def render_sociogram(params: Params): def mvp( - request: Annotated[ - TeamScopedRequest, Security(verify_team_scope, scopes=["analysis"]) - ], + request: Annotated[TeamScopedRequest, Security(verify_team_scope)], ): ranks = dict() with Session(engine) as session: diff --git a/cutt/main.py b/cutt/main.py index 5e50eb4..1dba77e 100644 --- a/cutt/main.py +++ b/cutt/main.py @@ -76,6 +76,8 @@ def submit_mvps( mvps: MVPRanking, user: Annotated[Player, Depends(get_current_active_user)], ): + if mvps.team == 42: + return JSONResponse("DEMO team, nothing happens") if user.id == mvps.user: with Session(engine) as session: statement = select(Team).where(Team.id == mvps.team) @@ -121,6 +123,8 @@ def get_mvps( def submit_chemistry( chemistry: Chemistry, user: Annotated[Player, Depends(get_current_active_user)] ): + if chemistry.team == 42: + return JSONResponse("DEMO team, nothing happens") if user.id == chemistry.user: with Session(engine) as session: statement = select(Team).where(Team.id == chemistry.team) diff --git a/cutt/player.py b/cutt/player.py index 9deb00a..622b2f6 100644 --- a/cutt/player.py +++ b/cutt/player.py @@ -12,6 +12,7 @@ from cutt.security import ( read_player_me, verify_team_scope, ) +from cutt.demo import demo_players P = Player @@ -28,6 +29,12 @@ class PlayerRequest(BaseModel): class AddPlayerRequest(PlayerRequest): ... +DEMO_TEAM_REQUEST = HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="DEMO Team, nothing happens", +) + + def add_player( r: AddPlayerRequest, request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], @@ -72,6 +79,8 @@ def modify_player( r: ModifyPlayerRequest, request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], ): + if request.team_id == 42: + raise DEMO_TEAM_REQUEST with Session(engine) as session: player = session.exec( select(P) @@ -101,6 +110,8 @@ def disable_player( r: DisablePlayerRequest, request: Annotated[TeamScopedRequest, Depends(verify_team_scope)], ): + if request.team_id == 42: + raise DEMO_TEAM_REQUEST with Session(engine) as session: player = session.exec( select(P) @@ -148,6 +159,13 @@ async def list_all_players(): async def list_players( team_id: int, user: Annotated[Player, Depends(get_current_active_user)] ): + if team_id == 42: + return [ + user.model_dump( + include={"id", "display_name", "username", "number", "email"} + ) + ] + demo_players + with Session(engine) as session: current_user = session.exec( select(P) @@ -179,7 +197,9 @@ async def list_players( def read_teams_me(user: Annotated[P, Depends(get_current_active_user)]): with Session(engine) as session: - return [p.teams for p in session.exec(select(P).where(P.id == user.id))][0] + return [p.teams for p in session.exec(select(P).where(P.id == user.id))][0] + [ + {"country": "nowhere", "id": 42, "location": "everywhere", "name": "DEMO"} + ] player_router.add_api_route( diff --git a/cutt/security.py b/cutt/security.py index b23ffb0..61f572e 100644 --- a/cutt/security.py +++ b/cutt/security.py @@ -170,6 +170,8 @@ class TeamScopedRequest(BaseModel): async def verify_team_scope( team_id: int, user: Annotated[Player, Depends(get_current_active_user)] ): + if team_id == 42: + return TeamScopedRequest(user=user, team_id=team_id) allowed_scopes = set(user.scopes.split()) if f"team:{team_id}" not in allowed_scopes: raise HTTPException( diff --git a/src/Footer.tsx b/src/Footer.tsx index ee46271..04578a1 100644 --- a/src/Footer.tsx +++ b/src/Footer.tsx @@ -4,10 +4,11 @@ import { useSession } from "./Session"; export default function Footer() { const location = useLocation(); - const { user } = useSession(); + const { user, teams } = useSession(); return (