Compare commits
2 Commits
6902ffdca6
...
1c71df781c
Author | SHA1 | Date | |
---|---|---|---|
1c71df781c | |||
6378488fd0 |
@ -1,4 +1,6 @@
|
|||||||
import io
|
import io
|
||||||
|
import itertools
|
||||||
|
import random
|
||||||
import base64
|
import base64
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from fastapi import APIRouter, HTTPException, Security, status
|
from fastapi import APIRouter, HTTPException, Security, status
|
||||||
@ -12,6 +14,7 @@ import numpy as np
|
|||||||
import matplotlib
|
import matplotlib
|
||||||
|
|
||||||
from cutt.security import TeamScopedRequest, verify_team_scope
|
from cutt.security import TeamScopedRequest, verify_team_scope
|
||||||
|
from cutt.demo import demo_players
|
||||||
|
|
||||||
matplotlib.use("agg")
|
matplotlib.use("agg")
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
@ -55,14 +58,65 @@ def sociogram_json():
|
|||||||
|
|
||||||
|
|
||||||
def graph_json(
|
def graph_json(
|
||||||
request: Annotated[
|
request: Annotated[TeamScopedRequest, Security(verify_team_scope)],
|
||||||
TeamScopedRequest, Security(verify_team_scope, scopes=["analysis"])
|
|
||||||
],
|
|
||||||
networkx_graph: bool = False,
|
networkx_graph: bool = False,
|
||||||
):
|
):
|
||||||
nodes = []
|
nodes = []
|
||||||
edges = []
|
edges = []
|
||||||
player_map = {}
|
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:
|
with Session(engine) as session:
|
||||||
players = session.exec(
|
players = session.exec(
|
||||||
select(P)
|
select(P)
|
||||||
@ -240,9 +294,7 @@ async def render_sociogram(params: Params):
|
|||||||
|
|
||||||
|
|
||||||
def mvp(
|
def mvp(
|
||||||
request: Annotated[
|
request: Annotated[TeamScopedRequest, Security(verify_team_scope)],
|
||||||
TeamScopedRequest, Security(verify_team_scope, scopes=["analysis"])
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
ranks = dict()
|
ranks = dict()
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
|
27
cutt/demo.py
Normal file
27
cutt/demo.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import random
|
||||||
|
from cutt.db import Player
|
||||||
|
|
||||||
|
names = [
|
||||||
|
"August",
|
||||||
|
"Beate",
|
||||||
|
"Ceasar",
|
||||||
|
"Daedalus",
|
||||||
|
"Elli",
|
||||||
|
"Ford P.",
|
||||||
|
"Gabriel",
|
||||||
|
"Hugo",
|
||||||
|
"Ivar Johansson",
|
||||||
|
"Jürgen Gordon Malinauskas",
|
||||||
|
]
|
||||||
|
demo_players = [
|
||||||
|
Player.model_validate(
|
||||||
|
{
|
||||||
|
"id": i,
|
||||||
|
"display_name": name,
|
||||||
|
"username": name.lower().replace(" ", "").replace(".", ""),
|
||||||
|
"number": str(random.randint(0, 100)),
|
||||||
|
"email": name.lower().replace(" ", "").replace(".", "") + "@example.org",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for i, name in enumerate(names)
|
||||||
|
]
|
@ -76,6 +76,8 @@ def submit_mvps(
|
|||||||
mvps: MVPRanking,
|
mvps: MVPRanking,
|
||||||
user: Annotated[Player, Depends(get_current_active_user)],
|
user: Annotated[Player, Depends(get_current_active_user)],
|
||||||
):
|
):
|
||||||
|
if mvps.team == 42:
|
||||||
|
return JSONResponse("DEMO team, nothing happens")
|
||||||
if user.id == mvps.user:
|
if user.id == mvps.user:
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
statement = select(Team).where(Team.id == mvps.team)
|
statement = select(Team).where(Team.id == mvps.team)
|
||||||
@ -121,6 +123,8 @@ def get_mvps(
|
|||||||
def submit_chemistry(
|
def submit_chemistry(
|
||||||
chemistry: Chemistry, user: Annotated[Player, Depends(get_current_active_user)]
|
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:
|
if user.id == chemistry.user:
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
statement = select(Team).where(Team.id == chemistry.team)
|
statement = select(Team).where(Team.id == chemistry.team)
|
||||||
|
@ -12,6 +12,7 @@ from cutt.security import (
|
|||||||
read_player_me,
|
read_player_me,
|
||||||
verify_team_scope,
|
verify_team_scope,
|
||||||
)
|
)
|
||||||
|
from cutt.demo import demo_players
|
||||||
|
|
||||||
P = Player
|
P = Player
|
||||||
|
|
||||||
@ -28,6 +29,12 @@ class PlayerRequest(BaseModel):
|
|||||||
class AddPlayerRequest(PlayerRequest): ...
|
class AddPlayerRequest(PlayerRequest): ...
|
||||||
|
|
||||||
|
|
||||||
|
DEMO_TEAM_REQUEST = HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="DEMO Team, nothing happens",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_player(
|
def add_player(
|
||||||
r: AddPlayerRequest,
|
r: AddPlayerRequest,
|
||||||
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
||||||
@ -72,6 +79,8 @@ def modify_player(
|
|||||||
r: ModifyPlayerRequest,
|
r: ModifyPlayerRequest,
|
||||||
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
||||||
):
|
):
|
||||||
|
if request.team_id == 42:
|
||||||
|
raise DEMO_TEAM_REQUEST
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
player = session.exec(
|
player = session.exec(
|
||||||
select(P)
|
select(P)
|
||||||
@ -101,6 +110,8 @@ def disable_player(
|
|||||||
r: DisablePlayerRequest,
|
r: DisablePlayerRequest,
|
||||||
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
request: Annotated[TeamScopedRequest, Depends(verify_team_scope)],
|
||||||
):
|
):
|
||||||
|
if request.team_id == 42:
|
||||||
|
raise DEMO_TEAM_REQUEST
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
player = session.exec(
|
player = session.exec(
|
||||||
select(P)
|
select(P)
|
||||||
@ -148,6 +159,13 @@ async def list_all_players():
|
|||||||
async def list_players(
|
async def list_players(
|
||||||
team_id: int, user: Annotated[Player, Depends(get_current_active_user)]
|
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:
|
with Session(engine) as session:
|
||||||
current_user = session.exec(
|
current_user = session.exec(
|
||||||
select(P)
|
select(P)
|
||||||
@ -179,7 +197,9 @@ async def list_players(
|
|||||||
|
|
||||||
def read_teams_me(user: Annotated[P, Depends(get_current_active_user)]):
|
def read_teams_me(user: Annotated[P, Depends(get_current_active_user)]):
|
||||||
with Session(engine) as session:
|
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(
|
player_router.add_api_route(
|
||||||
|
@ -170,6 +170,8 @@ class TeamScopedRequest(BaseModel):
|
|||||||
async def verify_team_scope(
|
async def verify_team_scope(
|
||||||
team_id: int, user: Annotated[Player, Depends(get_current_active_user)]
|
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())
|
allowed_scopes = set(user.scopes.split())
|
||||||
if f"team:{team_id}" not in allowed_scopes:
|
if f"team:{team_id}" not in allowed_scopes:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
@ -4,10 +4,11 @@ import { useSession } from "./Session";
|
|||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { user } = useSession();
|
const { user, teams } = useSession();
|
||||||
return (
|
return (
|
||||||
<footer className={location.pathname === "/network" ? "fixed-footer" : ""}>
|
<footer className={location.pathname === "/network" ? "fixed-footer" : ""}>
|
||||||
{user?.scopes.split(" ").includes("analysis") && (
|
{(user?.scopes.split(" ").includes("analysis") ||
|
||||||
|
teams?.activeTeam === 42) && (
|
||||||
<div className="navbar">
|
<div className="navbar">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<span>Form</span>
|
<span>Form</span>
|
||||||
|
@ -15,6 +15,7 @@ const MVPChart = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
||||||
|
teams?.activeTeam === 42 ||
|
||||||
navigate("/", { replace: true });
|
navigate("/", { replace: true });
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ export const GraphComponent = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
||||||
|
teams?.activeTeam === 42 ||
|
||||||
navigate("/", { replace: true });
|
navigate("/", { replace: true });
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ const TeamPanel = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
user?.scopes.includes(`team:${teams?.activeTeam}`) ||
|
||||||
|
teams?.activeTeam === 42 ||
|
||||||
navigate("/", { replace: true });
|
navigate("/", { replace: true });
|
||||||
}, [user]);
|
}, [user]);
|
||||||
const newPlayerTemplate = {
|
const newPlayerTemplate = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user