← back to djacobs__Pinwheel

Function bodies 688 total

All specs Real LLM only Function bodies
format_response_mock function · python · L859-L1011 (153 LOC)
src/pinwheel/ai/search.py
def format_response_mock(question: str, result: QueryResult) -> str:
    """Format a QueryResult into a readable Discord message string.

    No AI call. Produces structured, plain-language responses.
    """
    if result.error:
        return result.error

    data = result.data

    if result.query_type == "standings":
        standings = data.get("standings", [])
        if not standings:
            return "No games have been played yet."
        lines = ["**League Standings**"]
        for i, s in enumerate(standings, 1):
            name = s.get("team_name", s.get("team_id", "???"))
            w = s.get("wins", 0)
            lo = s.get("losses", 0)
            lines.append(f"{i}. **{name}** ({w}W-{lo}L)")
        return "\n".join(lines)

    elif result.query_type == "team_record":
        name = data.get("team_name", "Unknown")
        w = data.get("wins", 0)
        lo = data.get("losses", 0)
        return f"**{name}** are {w}-{lo} this season."

    elif result.query_type 
format_response_ai function · python · L1033-L1063 (31 LOC)
src/pinwheel/ai/search.py
async def format_response_ai(
    question: str,
    result: QueryResult,
    api_key: str,
) -> str:
    """Format a QueryResult using Claude API. Falls back to mock on failure."""
    if result.error:
        return result.error

    user_msg = (
        f"Original question: {question}\n\n"
        f"Query type: {result.query_type}\n\n"
        f"Raw data:\n{json.dumps(result.data, indent=2, default=str)}"
    )

    from pinwheel.ai.usage import cacheable_system

    model = "claude-sonnet-4-6"
    try:
        client = anthropic.AsyncAnthropic(api_key=api_key)
        response = await client.messages.create(
            model=model,
            max_tokens=500,
            system=cacheable_system(SEARCH_FORMATTER_SYSTEM_PROMPT),
            messages=[{"role": "user", "content": user_msg}],
        )
        return response.content[0].text.strip()

    except (anthropic.APIError, KeyError, IndexError) as e:
        logger.warning("AI response format failed, using mock: %s", e)
      
compute_cost function · python · L72-L91 (20 LOC)
src/pinwheel/ai/usage.py
def compute_cost(
    model: str,
    input_tokens: int,
    output_tokens: int,
    cache_read_tokens: int = 0,
    cache_creation_tokens: int = 0,
) -> float:
    """Compute estimated cost in USD for a single API call.

    Includes cache creation tokens (25% premium on first cache write)
    and cache read tokens (90% discount on subsequent reads).
    """
    rates = PRICING.get(model, _DEFAULT_PRICING)
    cost = (
        input_tokens * rates["input_per_mtok"]
        + output_tokens * rates["output_per_mtok"]
        + cache_read_tokens * rates["cache_read_per_mtok"]
        + cache_creation_tokens * rates["cache_write_per_mtok"]
    ) / 1_000_000
    return round(cost, 8)
record_ai_usage function · python · L94-L171 (78 LOC)
src/pinwheel/ai/usage.py
async def record_ai_usage(
    *,
    session: AsyncSession,
    call_type: str,
    model: str,
    input_tokens: int,
    output_tokens: int,
    cache_read_tokens: int = 0,
    cache_creation_tokens: int = 0,
    latency_ms: float = 0.0,
    season_id: str = "",
    round_number: int | None = None,
) -> AIUsageLogRow:
    """Record an AI API call to the usage log.

    Parameters
    ----------
    session : AsyncSession
        The SQLAlchemy async session to use for the insert.
    call_type : str
        Identifier for the call site, e.g. "report.simulation",
        "commentary.game", "interpreter.v2", "classifier".
    model : str
        The model name, e.g. "claude-sonnet-4-5-20250929".
    input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens : int
        Token counts from the API response.
    latency_ms : float
        Wall-clock time of the API call in milliseconds.
    season_id : str
        Current season ID (empty string if unavailable).
    round_num
track_latency function · python · L175-L189 (15 LOC)
src/pinwheel/ai/usage.py
async def track_latency() -> AsyncGenerator[dict[str, float], None]:
    """Context manager that yields a dict; after exit, 'latency_ms' is set.

    Usage::

        async with track_latency() as timing:
            response = await client.messages.create(...)
        latency = timing["latency_ms"]
    """
    timing: dict[str, float] = {"latency_ms": 0.0}
    start = time.monotonic()
    try:
        yield timing
    finally:
        timing["latency_ms"] = (time.monotonic() - start) * 1000
extract_usage function · python · L192-L210 (19 LOC)
src/pinwheel/ai/usage.py
def extract_usage(response: object) -> tuple[int, int, int, int]:
    """Extract token counts from an API response.

    Returns (input_tokens, output_tokens, cache_read_tokens,
    cache_creation_tokens). Works with the Anthropic SDK ``Message``
    objects.

    ``cache_creation_input_tokens`` is populated on the first request
    that creates a cache entry (charged at a 25% premium). Subsequent
    requests return ``cache_read_input_tokens`` instead (90% discount).
    """
    usage = getattr(response, "usage", None)
    if usage is None:
        return (0, 0, 0, 0)
    input_tokens = getattr(usage, "input_tokens", 0) or 0
    output_tokens = getattr(usage, "output_tokens", 0) or 0
    cache_read = getattr(usage, "cache_read_input_tokens", 0) or 0
    cache_creation = getattr(usage, "cache_creation_input_tokens", 0) or 0
    return (input_tokens, output_tokens, cache_read, cache_creation)
cacheable_system function · python · L218-L229 (12 LOC)
src/pinwheel/ai/usage.py
def cacheable_system(text: str) -> list[dict[str, object]]:
    """Wrap a system prompt string as a cacheable content block.

    The Messages API accepts ``system`` as either a string or a list of
    content blocks. To enable prompt caching, we use the block format
    with ``cache_control: {"type": "ephemeral"}``. Cached content costs
    90% less on subsequent reads within a 5-minute TTL.

    Prompts below the 1,024-token minimum cacheable size will simply
    not be cached (no error).
    """
    return [{"type": "text", "text": text, "cache_control": {"type": "ephemeral"}}]
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
pydantic_to_response_format function · python · L232-L252 (21 LOC)
src/pinwheel/ai/usage.py
def pydantic_to_response_format(
    model_class: type[BaseModel], name: str
) -> dict[str, object]:
    """Convert a Pydantic model to a Messages API ``output_config`` dict.

    Uses ``anthropic.transform_schema()`` to sanitize Pydantic's JSON schema
    (adds ``additionalProperties: false``, strips unsupported constraints like
    ``minimum``/``maximum``, etc.). The API guarantees the response conforms
    to the schema, eliminating JSON parsing failures.

    Returns a dict suitable for the ``output_config`` parameter
    (SDK v0.79+ uses ``output_config.format``).
    """
    from anthropic import transform_schema

    return {
        "format": {
            "type": "json_schema",
            "schema": transform_schema(model_class),
        },
    }
costs_dashboard function · python · L32-L166 (135 LOC)
src/pinwheel/api/admin_costs.py
async def costs_dashboard(
    request: Request, repo: RepoDep, current_user: OptionalUser
) -> HTMLResponse:
    """AI costs dashboard — token usage, cost breakdown, per-round trends.

    Auth-gated: redirects to login if OAuth is enabled and user is not
    authenticated. In dev mode without OAuth credentials the page is
    accessible to support local testing.
    """
    if denied := check_admin_access(current_user, request):
        return denied

    season_id = await _get_active_season_id(repo)
    session = repo.session

    # --- Summary totals ---
    total_calls = 0
    total_input_tokens = 0
    total_output_tokens = 0
    total_cache_read_tokens = 0
    total_cost = 0.0
    avg_latency = 0.0

    if season_id:
        stmt = select(
            func.count(AIUsageLogRow.id),
            func.coalesce(func.sum(AIUsageLogRow.input_tokens), 0),
            func.coalesce(func.sum(AIUsageLogRow.output_tokens), 0),
            func.coalesce(func.sum(AIUsageLogRow.cache_read_toke
_format_duration function · python · L42-L53 (12 LOC)
src/pinwheel/api/admin_perf.py
def _format_duration(seconds: float) -> str:
    """Format seconds into a human-readable duration string."""
    if seconds < 60:
        return f"{seconds:.0f}s"
    if seconds < 3600:
        minutes = seconds / 60
        return f"{minutes:.0f}m"
    hours = seconds / 3600
    if hours < 24:
        return f"{hours:.1f}h"
    days = hours / 24
    return f"{days:.1f}d"
_compute_percentiles function · python · L56-L76 (21 LOC)
src/pinwheel/api/admin_perf.py
def _compute_percentiles(values: list[float]) -> dict[str, float]:
    """Compute P50, P95, P99 from a sorted list of values.

    Returns a dict with keys 'p50', 'p95', 'p99'.  Returns zeros if the
    list is empty.
    """
    if not values:
        return {"p50": 0.0, "p95": 0.0, "p99": 0.0}
    values_sorted = sorted(values)
    n = len(values_sorted)

    def _pct(p: float) -> float:
        idx = int(p / 100.0 * (n - 1))
        idx = max(0, min(idx, n - 1))
        return values_sorted[idx]

    return {
        "p50": round(_pct(50), 1),
        "p95": round(_pct(95), 1),
        "p99": round(_pct(99), 1),
    }
_build_review_queue function · python · L29-L103 (75 LOC)
src/pinwheel/api/admin_review.py
async def _build_review_queue(
    repo: RepoDep,
    season_id: str,
) -> list[dict]:
    """Build the proposal review queue from governance events.

    Finds proposals flagged for review that haven't been cleared or vetoed.
    Returns a list of proposal dicts with all relevant metadata.
    """
    # Get all flagged proposals
    flagged_events = await repo.get_events_by_type(
        season_id=season_id,
        event_types=["proposal.flagged_for_review"],
    )

    # Get cleared/vetoed proposals to filter them out
    resolved_events = await repo.get_events_by_type(
        season_id=season_id,
        event_types=["proposal.review_cleared", "proposal.vetoed"],
    )
    resolved_ids = {e.aggregate_id for e in resolved_events}

    # Get proposal outcomes (passed/failed) -- these are also resolved
    outcome_events = await repo.get_events_by_type(
        season_id=season_id,
        event_types=["proposal.passed", "proposal.failed"],
    )
    outcome_map: dict[str, str] = {}
admin_review function · python · L107-L155 (49 LOC)
src/pinwheel/api/admin_review.py
async def admin_review(request: Request, repo: RepoDep, current_user: OptionalUser) -> HTMLResponse:
    """Admin proposal review queue.

    Shows proposals flagged for admin review. In dev mode without OAuth,
    the page is accessible to support local testing.
    """
    if denied := check_admin_access(current_user, request):
        return denied

    season_id = await _get_active_season_id(repo)
    queue: list[dict] = []
    pending_count = 0
    total_flagged = 0

    if season_id:
        queue = await _build_review_queue(repo, season_id)
        total_flagged = len(queue)
        pending_count = sum(1 for p in queue if p["status"] == "pending")

    # Also get injection classifications for context
    from pinwheel.evals.injection import get_injection_classifications

    injection_classifications: list[dict] = []
    if season_id:
        raw_classifications = await get_injection_classifications(repo, season_id, limit=10)
        for c in raw_classifications:
            if 
admin_roster function · python · L29-L147 (119 LOC)
src/pinwheel/api/admin_roster.py
async def admin_roster(request: Request, repo: RepoDep, current_user: OptionalUser) -> HTMLResponse:
    """Admin roster -- table of all enrolled governors.

    Auth-gated: requires admin Discord ID match when OAuth is enabled.
    In dev mode without OAuth credentials the page is accessible to support
    local testing.
    """
    if denied := check_admin_access(current_user, request):
        return denied

    # Show ALL players, regardless of season enrollment
    all_players = await repo.get_all_players()
    governors: list[dict] = []

    # Token balances are scoped to the active season (current state).
    # Proposals and votes aggregate across ALL seasons (lifetime record).
    active_season = await repo.get_active_season()
    active_season_id = active_season.id if active_season else None
    all_seasons = await repo.get_all_seasons()

    for player in all_players:
        team = await repo.get_team(player.team_id) if player.team_id else None
        team_name = team.name 
admin_season function · python · L26-L130 (105 LOC)
src/pinwheel/api/admin_season.py
async def admin_season(request: Request, repo: RepoDep, current_user: OptionalUser) -> HTMLResponse:
    """Admin season dashboard -- current config, past seasons, new season form."""
    if denied := check_admin_access(current_user, request):
        return denied

    settings = request.app.state.settings

    # Current season
    active_season = await repo.get_active_season()
    current_season_data: dict | None = None
    current_round = 0
    team_count = 0
    governor_count = 0
    games_played = 0

    if active_season:
        teams = await repo.get_teams_for_season(active_season.id)
        team_count = len(teams)

        players = await repo.get_players_for_season(active_season.id)
        governor_count = len(players)

        # Count completed games
        game_count_result = await repo.session.execute(
            select(sa_func.count(GameResultRow.id)).where(
                GameResultRow.season_id == active_season.id,
            )
        )
        games_played = gam
Source: Repobility analyzer · https://repobility.com
admin_workbench function · python · L76-L156 (81 LOC)
src/pinwheel/api/admin_workbench.py
async def admin_workbench(request: Request, current_user: OptionalUser) -> HTMLResponse:
    """Admin safety workbench -- injection classifier test bench and config.

    Auth-gated: requires admin Discord ID match when OAuth is enabled.
    In dev mode without OAuth credentials the page is accessible.
    """
    if denied := check_admin_access(current_user, request):
        return denied

    settings = request.app.state.settings

    # Classifier config info
    from pinwheel.ai.classifier import CLASSIFIER_MODEL, CLASSIFIER_PROMPT

    classifier_config = {
        "model": CLASSIFIER_MODEL,
        "prompt_preview": (
            CLASSIFIER_PROMPT[:300] + "..."
            if len(CLASSIFIER_PROMPT) > 300
            else CLASSIFIER_PROMPT
        ),
        "api_key_set": bool(settings.anthropic_api_key),
    }

    # Defense stack summary
    defense_layers = [
        {
            "name": "Input Sanitization",
            "description": "Strips invisible Unicode, HTML, prompt 
test_classifier function · python · L160-L258 (99 LOC)
src/pinwheel/api/admin_workbench.py
async def test_classifier(
    request: Request,
    current_user: OptionalUser,
    body: ClassifierTestRequest | None = None,
) -> HTMLResponse:
    """Test the injection classifier with arbitrary text.

    Returns an HTML fragment (HTMX partial) showing the classification
    result. Accepts JSON body with ``{"text": "..."}`` or form-encoded
    data. Runs sanitize_text first, then the classifier.
    """
    if denied := check_admin_access(current_user, request):
        return denied

    settings = request.app.state.settings

    # Accept either JSON body or form-encoded data
    raw_text = ""
    if body is not None:
        raw_text = body.text.strip()
    else:
        # Fallback: try to read JSON from request body
        try:
            data = await request.json()
            raw_text = str(data.get("text", "")).strip()
        except (ValueError, AttributeError):
            raw_text = ""

    if not raw_text:
        return HTMLResponse(
            '<div class="workbenc
_escape_html function · python · L261-L269 (9 LOC)
src/pinwheel/api/admin_workbench.py
def _escape_html(text: str) -> str:
    """Escape HTML special characters in text for safe rendering."""
    return (
        text.replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
        .replace("'", "&#x27;")
    )
_point function · python · L42-L48 (7 LOC)
src/pinwheel/api/charts.py
def _point(center: float, radius: float, index: int) -> tuple[float, float]:
    """Compute (x, y) for the given axis index at the given radius."""
    angle_deg = index * ANGLE_STEP - 90  # start from top
    angle_rad = math.radians(angle_deg)
    x = center + radius * math.cos(angle_rad)
    y = center + radius * math.sin(angle_rad)
    return (round(x, 2), round(y, 2))
spider_chart_data function · python · L51-L85 (35 LOC)
src/pinwheel/api/charts.py
def spider_chart_data(
    attributes: dict[str, float],
    center: float = 150,
    max_radius: float = 120,
) -> list[dict]:
    """Compute SVG coordinates for each attribute vertex.

    Returns a list of 9 dicts with keys:
        x, y       — vertex position
        lx, ly     — label position (slightly beyond vertex)
        attr       — attribute name
        label      — 3-char abbreviation
        value      — numeric value (0–100)
        color      — hex color for this attribute
    """
    points = []
    for i, attr in enumerate(ATTRIBUTE_ORDER):
        value = max(0, min(100, float(attributes.get(attr, 0))))
        r = (value / 100) * max_radius
        x, y = _point(center, r, i)
        # Label position: push out beyond the max radius
        lx, ly = _point(center, max_radius + 18, i)
        points.append(
            {
                "x": x,
                "y": y,
                "lx": lx,
                "ly": ly,
                "attr": attr,
                "
compute_grid_rings function · python · L88-L101 (14 LOC)
src/pinwheel/api/charts.py
def compute_grid_rings(
    center: float = 150,
    max_radius: float = 120,
) -> list[str]:
    """Compute SVG polygon point-strings for 4 concentric grid rings (25/50/75/100%)."""
    rings = []
    for pct in (0.25, 0.50, 0.75, 1.0):
        r = max_radius * pct
        coords = []
        for i in range(NUM_AXES):
            x, y = _point(center, r, i)
            coords.append(f"{x},{y}")
        rings.append(" ".join(coords))
    return rings
axis_lines function · python · L109-L118 (10 LOC)
src/pinwheel/api/charts.py
def axis_lines(
    center: float = 150,
    max_radius: float = 120,
) -> list[dict]:
    """Compute axis line endpoints from center to outer ring."""
    lines = []
    for i in range(NUM_AXES):
        x, y = _point(center, max_radius, i)
        lines.append({"x1": center, "y1": center, "x2": x, "y2": y})
    return lines
compute_season_averages function · python · L121-L160 (40 LOC)
src/pinwheel/api/charts.py
def compute_season_averages(
    box_scores: list[dict],
) -> dict[str, float | int]:
    """Compute season averages from a list of box score dicts.

    Each dict should have keys matching BoxScoreRow fields:
        points, assists, steals, turnovers,
        field_goals_made, field_goals_attempted,
        three_pointers_made, three_pointers_attempted,
        free_throws_made, free_throws_attempted

    Returns dict with ppg, apg, spg, topg, fg_pct, three_pct, ft_pct, games_played.
    Returns empty dict if no box scores.
    """
    if not box_scores:
        return {}

    games = len(box_scores)
    total_pts = sum(bs.get("points", 0) for bs in box_scores)
    total_ast = sum(bs.get("assists", 0) for bs in box_scores)
    total_stl = sum(bs.get("steals", 0) for bs in box_scores)
    total_to = sum(bs.get("turnovers", 0) for bs in box_scores)

    total_fgm = sum(bs.get("field_goals_made", 0) for bs in box_scores)
    total_fga = sum(bs.get("field_goals_attempted", 0) for bs in b
Same scanner, your repo: https://repobility.com — Repobility
get_session function · python · L20-L31 (12 LOC)
src/pinwheel/api/deps.py
async def get_session(
    engine: Annotated[AsyncEngine, Depends(get_engine)],
) -> AsyncGenerator[AsyncSession, None]:
    """Yield an async session."""
    factory = create_session_factory(engine)
    async with factory() as session:
        try:
            yield session
            await session.commit()
        except Exception:  # Re-raise pattern — must catch all to ensure rollback on any error
            await session.rollback()
            raise
compute_safety_summary function · python · L26-L116 (91 LOC)
src/pinwheel/api/eval_dashboard.py
def compute_safety_summary(
    *,
    grounding_rate: float,
    grounding_total: int,
    prescriptive_flagged: int,
    injection_attempts: int,
    active_flags: list[dict],
    gqi_trend: list[dict],
    golden_pass_rate: float,
) -> dict:
    """Compute the traffic-light safety summary from eval metrics.

    Returns a dict with:
      - status: "green" | "yellow" | "red"
      - label: human-readable status label
      - total_evaluated: total reports/proposals evaluated
      - injection_attempts: number of injection attempts detected
      - eval_coverage_pct: percentage of eval types that have data
      - gqi_score: latest GQI composite score (0.0 if unavailable)
      - concerns: list of short concern strings
    """
    concerns: list[str] = []

    # Count critical and warning flags
    critical_flags = sum(1 for f in active_flags if f.get("severity") == "critical")
    warning_flags = sum(1 for f in active_flags if f.get("severity") == "warning")

    if critical_flags >
_get_available_rounds function · python · L125-L138 (14 LOC)
src/pinwheel/api/eval_dashboard.py
async def _get_available_rounds(repo: RepoDep, season_id: str) -> list[int]:
    """Return sorted list of distinct round numbers that have eval results."""
    from sqlalchemy import select

    from pinwheel.db.models import EvalResultRow

    stmt = (
        select(EvalResultRow.round_number)
        .where(EvalResultRow.season_id == season_id)
        .distinct()
        .order_by(EvalResultRow.round_number)
    )
    result = await repo.session.execute(stmt)
    return [row[0] for row in result.all() if row[0] is not None]
_parse_round_param function · python · L141-L152 (12 LOC)
src/pinwheel/api/eval_dashboard.py
def _parse_round_param(request: Request) -> int | None:
    """Extract and validate the ``round`` query parameter."""
    raw = request.query_params.get("round")
    if raw is None:
        return None
    try:
        value = int(raw)
    except (ValueError, TypeError):
        return None
    if value < 0:
        return None
    return value
sse_stream function · python · L74-L142 (69 LOC)
src/pinwheel/api/events.py
async def sse_stream(
    request: Request,
    event_type: str | None = None,
) -> StreamingResponse:
    """Server-Sent Events stream.

    Query params:
        event_type: optional filter — must be one of the known event types
                    (e.g. "game.completed", "report.generated").
                    If omitted, receives all events.

    Returns an SSE stream that stays open until the client disconnects.
    Sends an initial comment to flush proxy buffers and periodic heartbeats
    to keep the connection alive through reverse proxies.

    Errors:
        400 — unknown event_type value
        429 — global connection limit reached
    """
    # Validate event_type against the allowlist
    if event_type is not None and event_type not in ALLOWED_EVENT_TYPES:
        raise HTTPException(
            status_code=400,
            detail=(
                f"Unknown event_type {event_type!r}. "
                f"Valid values: {sorted(ALLOWED_EVENT_TYPES)}"
            ),
     
events_health function · python · L146-L154 (9 LOC)
src/pinwheel/api/events.py
async def events_health(request: Request) -> dict:
    """Check EventBus health, subscriber count, and SSE connection stats."""
    bus = _get_bus(request)
    return {
        "status": "ok",
        "subscribers": bus.subscriber_count,
        "active_sse_connections": _MAX_SSE_CONNECTIONS - _connection_semaphore._value,  # noqa: SLF001
        "max_sse_connections": _MAX_SSE_CONNECTIONS,
    }
get_followed_team_id function · python · L23-L28 (6 LOC)
src/pinwheel/api/follow.py
def get_followed_team_id(request: Request) -> str | None:
    """Read the followed team ID from the cookie. Returns None if unset."""
    value = request.cookies.get(FOLLOW_COOKIE_NAME)
    if value and len(value) == 36:
        return value
    return None
follow_team function · python · L32-L63 (32 LOC)
src/pinwheel/api/follow.py
async def follow_team(
    request: Request,
    team_id: str,
    repo: RepoDep,
    current_user: OptionalUser,
) -> HTMLResponse | JSONResponse:
    """Set the followed team cookie. Returns an HTMX partial or JSON.

    Works for both logged-in and anonymous users — the cookie is
    independent of the session. This keeps the MVP simple.
    """
    # Validate team exists
    team = await repo.get_team(team_id)
    if team is None:
        return JSONResponse({"error": "Team not found"}, status_code=404)

    is_htmx = request.headers.get("HX-Request") == "true"

    if is_htmx:
        html = _follow_button_html(team_id, team.name, following=True)
        response = HTMLResponse(html)
    else:
        response = JSONResponse({"status": "following", "team_id": team_id})  # type: ignore[assignment]

    response.set_cookie(
        FOLLOW_COOKIE_NAME,
        team_id,
        max_age=FOLLOW_COOKIE_MAX_AGE,
        httponly=True,
        samesite="lax",
    )
    return response
Repobility analyzer · published findings · https://repobility.com
unfollow_team function · python · L67-L86 (20 LOC)
src/pinwheel/api/follow.py
async def unfollow_team(
    request: Request,
    team_id: str,
    repo: RepoDep,
    current_user: OptionalUser,
) -> HTMLResponse | JSONResponse:
    """Clear the followed team cookie. Returns an HTMX partial or JSON."""
    team = await repo.get_team(team_id)
    team_name = team.name if team else "this team"

    is_htmx = request.headers.get("HX-Request") == "true"

    if is_htmx:
        html = _follow_button_html(team_id, team_name, following=False)
        response = HTMLResponse(html)
    else:
        response = JSONResponse({"status": "unfollowed", "team_id": team_id})  # type: ignore[assignment]

    response.delete_cookie(FOLLOW_COOKIE_NAME)
    return response
_follow_button_html function · python · L89-L111 (23 LOC)
src/pinwheel/api/follow.py
def _follow_button_html(team_id: str, team_name: str, *, following: bool) -> str:
    """Render the follow/unfollow button as an HTMX-swappable fragment."""
    if following:
        return (
            f'<div id="follow-btn-container">'
            f'<button class="follow-btn follow-btn--following"'
            f' hx-delete="/api/teams/{team_id}/follow"'
            f' hx-target="#follow-btn-container"'
            f' hx-swap="outerHTML"'
            f' title="Unfollow {team_name}">'
            f"Following</button>"
            f"</div>"
        )
    return (
        f'<div id="follow-btn-container">'
        f'<button class="follow-btn"'
        f' hx-post="/api/teams/{team_id}/follow"'
        f' hx-target="#follow-btn-container"'
        f' hx-swap="outerHTML"'
        f' title="Follow {team_name} to prioritize their games">'
        f"Follow</button>"
        f"</div>"
    )
get_playoff_bracket function · python · L223-L229 (7 LOC)
src/pinwheel/api/games.py
async def get_playoff_bracket(repo: RepoDep) -> dict:
    """Get structured playoff bracket data.

    Returns bracket with semifinals, finals, series records, and champion.
    """
    bracket = await _build_bracket_data(repo)
    return {"data": bracket}
get_game function · python · L233-L251 (19 LOC)
src/pinwheel/api/games.py
async def get_game(game_id: str, repo: RepoDep) -> dict:
    """Get a game result by ID."""
    game = await repo.get_game_result(game_id)
    if not game:
        raise HTTPException(404, "Game not found")
    return {
        "data": {
            "id": game.id,
            "home_team_id": game.home_team_id,
            "away_team_id": game.away_team_id,
            "home_score": game.home_score,
            "away_score": game.away_score,
            "winner_team_id": game.winner_team_id,
            "total_possessions": game.total_possessions,
            "elam_target": game.elam_target,
            "quarter_scores": game.quarter_scores,
            "seed": game.seed,
        },
    }
get_boxscore function · python · L255-L276 (22 LOC)
src/pinwheel/api/games.py
async def get_boxscore(game_id: str, repo: RepoDep) -> dict:
    """Get box scores for a game."""
    game = await repo.get_game_result(game_id)
    if not game:
        raise HTTPException(404, "Game not found")
    return {
        "data": [
            {
                "hooper_id": bs.hooper_id,
                "team_id": bs.team_id,
                "points": bs.points,
                "field_goals_made": bs.field_goals_made,
                "field_goals_attempted": bs.field_goals_attempted,
                "three_pointers_made": bs.three_pointers_made,
                "three_pointers_attempted": bs.three_pointers_attempted,
                "assists": bs.assists,
                "steals": bs.steals,
                "turnovers": bs.turnovers,
            }
            for bs in game.box_scores
        ],
    }
api_list_proposals function · python · L18-L29 (12 LOC)
src/pinwheel/api/governance.py
async def api_list_proposals(season_id: str, repo: RepoDep) -> dict:
    """List all proposals for a season."""
    events = await repo.get_events_by_type(
        season_id=season_id,
        event_types=["proposal.submitted"],
    )
    proposals = []
    for e in events:
        proposal_data = e.payload
        if "id" in proposal_data and "raw_text" in proposal_data:
            proposals.append(Proposal(**proposal_data))
    return {"data": [p.model_dump(mode="json") for p in proposals]}
api_current_rules function · python · L33-L55 (23 LOC)
src/pinwheel/api/governance.py
async def api_current_rules(season_id: str, repo: RepoDep) -> dict:
    """Get the current ruleset for a season."""
    season = await repo.get_season(season_id)
    if not season:
        raise HTTPException(status_code=404, detail="Season not found")

    ruleset = RuleSet(**(season.current_ruleset or {}))
    defaults = RuleSet()

    # Highlight non-default values
    changes = {}
    for param in RuleSet.model_fields:
        current = getattr(ruleset, param)
        default = getattr(defaults, param)
        if current != default:
            changes[param] = {"current": current, "default": default}

    return {
        "data": {
            "ruleset": ruleset.model_dump(),
            "changes_from_default": changes,
        }
    }
api_rule_history function · python · L59-L65 (7 LOC)
src/pinwheel/api/governance.py
async def api_rule_history(season_id: str, repo: RepoDep) -> dict:
    """Get all rule changes for a season."""
    events = await repo.get_events_by_type(
        season_id=season_id,
        event_types=["rule.enacted"],
    )
    return {"data": [e.payload for e in events]}
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
get_pace function · python · L51-L59 (9 LOC)
src/pinwheel/api/pace.py
async def get_pace(request: Request) -> PaceResponse:
    """Return the current presentation pace and derived cron expression."""
    settings = _get_settings(request)
    cron = settings.effective_game_cron()
    return PaceResponse(
        pace=settings.pinwheel_presentation_pace,
        cron=cron,
        auto_advance=cron is not None,
    )
set_pace function · python · L63-L87 (25 LOC)
src/pinwheel/api/pace.py
async def set_pace(
    body: PaceRequest,
    request: Request,
    _: Annotated[None, Depends(require_api_admin)],
) -> PaceResponse:
    """Change the presentation pace in memory (not persisted to env).

    Admin-only: requires authenticated admin in production/staging.
    This is a demo convenience endpoint — not meant for production use.
    """
    if body.pace not in VALID_PACES:
        raise HTTPException(
            status_code=422,
            detail=f"Invalid pace '{body.pace}'. Must be one of: {sorted(VALID_PACES)}",
        )

    settings = _get_settings(request)
    settings.pinwheel_presentation_pace = body.pace

    cron = PACE_CRON_MAP[body.pace]
    return PaceResponse(
        pace=body.pace,
        cron=cron,
        auto_advance=cron is not None,
    )
advance_round function · python · L91-L129 (39 LOC)
src/pinwheel/api/pace.py
async def advance_round(
    request: Request,
    _: Annotated[None, Depends(require_api_admin)],
    quarter_seconds: int = 300,
    game_gap_seconds: int = 0,
) -> AdvanceResponse:
    """Trigger a round advance within the server process.

    Admin-only: requires authenticated admin in production/staging.
    Forces presentation_mode="replay" with demo-friendly timing defaults.
    Returns 409 if a presentation is already active.
    """
    from pinwheel.core.presenter import PresentationState
    from pinwheel.core.scheduler_runner import tick_round

    presentation_state: PresentationState = request.app.state.presentation_state
    if presentation_state.is_active:
        raise HTTPException(
            status_code=409,
            detail="A presentation is already active. Wait for it to finish.",
        )

    engine = request.app.state.engine
    event_bus = request.app.state.event_bus
    settings = _get_settings(request)

    asyncio.create_task(
        tick_round(
     
pace_status function · python · L133-L142 (10 LOC)
src/pinwheel/api/pace.py
async def pace_status(request: Request) -> PaceStatusResponse:
    """Return current presentation state."""
    from pinwheel.core.presenter import PresentationState

    presentation_state: PresentationState = request.app.state.presentation_state
    return PaceStatusResponse(
        is_active=presentation_state.is_active,
        current_round=presentation_state.current_round,
        current_game_index=presentation_state.current_game_index,
    )
_get_slot_start_times function · python · L39-L59 (21 LOC)
src/pinwheel/api/pages.py
def _get_slot_start_times(
    request: Request,
    slot_count: int,
) -> list[str]:
    """Compute formatted start times for the next *slot_count* time slots.

    Each slot fires on the cron cadence (e.g. every 30 min).  A slot is
    a set of games where no team plays twice — they tip off simultaneously.
    Returns an empty list if the cron is unavailable.
    """
    if slot_count <= 0:
        return []
    settings: Settings = request.app.state.settings
    cron_expr = settings.effective_game_cron()
    if not cron_expr:
        return []
    try:
        times = compute_round_start_times(cron_expr, slot_count)
        return [format_game_time(t) for t in times]
    except (ValueError, TypeError):
        return []
_light_safe function · python · L65-L86 (22 LOC)
src/pinwheel/api/pages.py
def _light_safe(hex_color: str) -> str:
    """Darken high-luminance hex colors for readability on light backgrounds.

    Colors with relative luminance > 0.5 (e.g. gold #FFD700, light blue
    #88BBDD) get darkened ~40% and saturation-boosted.  All other colors
    pass through unchanged.
    """
    c = hex_color.lstrip("#")
    if len(c) != 6:
        return hex_color
    try:
        r, g, b = int(c[0:2], 16), int(c[2:4], 16), int(c[4:6], 16)
    except ValueError:
        return hex_color
    # Relative luminance (sRGB)
    luminance = 0.2126 * (r / 255) + 0.7152 * (g / 255) + 0.0722 * (b / 255)
    if luminance <= 0.5:
        return hex_color
    # Darken by 40%
    factor = 0.6
    rd, gd, bd = int(r * factor), int(g * factor), int(b * factor)
    return f"#{rd:02x}{gd:02x}{bd:02x}"
_prose_to_html function · python · L92-L104 (13 LOC)
src/pinwheel/api/pages.py
def _prose_to_html(text: str) -> str:
    """Convert markdown/prose text to HTML.

    Uses the markdown library to render headings, bold, italic, lists, etc.
    Sanitizes the resulting HTML with nh3 to prevent XSS from AI-generated
    content that may include raw HTML or script tags.
    """
    import markdown
    import nh3

    text = text.strip()
    raw_html = markdown.markdown(text, extensions=["nl2br", "smarty"])
    return nh3.clean(raw_html)
_auth_context function · python · L110-L125 (16 LOC)
src/pinwheel/api/pages.py
def _auth_context(request: Request, current_user: SessionUser | None) -> dict:
    """Build auth-related template context available on every page."""
    settings = request.app.state.settings
    oauth_enabled = bool(settings.discord_client_id and settings.discord_client_secret)
    admin_id = settings.pinwheel_admin_discord_id
    is_admin = current_user is not None and bool(admin_id) and current_user.discord_id == admin_id
    followed_team_id = request.cookies.get("pinwheel_followed_team")
    return {
        "current_user": current_user,
        "oauth_enabled": oauth_enabled,
        "pinwheel_env": settings.pinwheel_env,
        "app_version": APP_VERSION,
        "discord_invite_url": settings.discord_invite_url,
        "is_admin": is_admin,
        "followed_team_id": followed_team_id,
    }
Source: Repobility analyzer · https://repobility.com
_get_active_season function · python · L134-L139 (6 LOC)
src/pinwheel/api/pages.py
async def _get_active_season(repo: RepoDep) -> tuple[str | None, str | None]:
    """Get (season_id, season_name) for the active season."""
    row = await repo.get_active_season()
    if row:
        return row.id, row.name
    return None, None
_get_standings function · python · L142-L181 (40 LOC)
src/pinwheel/api/pages.py
async def _get_standings(
    repo: RepoDep,
    season_id: str,
    phase_filter: str | None = None,
) -> list[dict]:
    """Compute standings for a season.

    Args:
        phase_filter: When ``"regular"``, include only regular-season games
            (phase is ``None`` or ``"regular"``).  When ``"playoff"``, include
            only post-season games (phase in ``"playoff"``, ``"semifinal"``,
            ``"finals"``).  ``None`` includes all games (original behaviour).
    """
    games = await repo.get_all_games(season_id)

    if phase_filter == "regular":
        games = [g for g in games if getattr(g, "phase", None) in (None, "regular")]
    elif phase_filter == "playoff":
        games = [
            g for g in games if getattr(g, "phase", None) in ("playoff", "semifinal", "finals")
        ]

    all_results: list[dict] = [
        {
            "home_team_id": g.home_team_id,
            "away_team_id": g.away_team_id,
            "home_score": g.home_score,
            "a
_get_season_phase function · python · L184-L205 (22 LOC)
src/pinwheel/api/pages.py
async def _get_season_phase(repo: RepoDep, season_id: str) -> str:
    """Get the current season phase as a display label.

    Returns one of: 'regular', 'tiebreakers', 'playoffs', 'championship',
    'offseason'. Used to drive phase-aware badges in templates.
    """
    season = await repo.get_season(season_id)
    if not season:
        return "regular"
    phase_map: dict[str, str] = {
        "setup": "regular",
        "active": "regular",
        "tiebreaker_check": "regular",
        "tiebreakers": "tiebreakers",
        "regular_season_complete": "regular",
        "playoffs": "playoffs",
        "championship": "championship",
        "offseason": "offseason",
        "completed": "offseason",
        "complete": "offseason",
    }
    return phase_map.get(season.status or "", "regular")
‹ prevpage 3 / 14next ›