EA_strat_optimizer / backend /api /routes_graph.py
TheQuantEd's picture
deploy: AMD EA Strategy Optimizer — Neo4j + FastAPI + Streamlit
6252f54
from typing import Annotated
from fastapi import APIRouter, Depends, Query
from backend.dependencies import get_neo4j_client
from backend.graph.neo4j_client import Neo4jClient
from backend.graph.cypher_queries import (
GET_ALL_DOMAINS,
GET_CAPABILITIES_FOR_DOMAIN,
GET_DOMAIN_STATS,
GET_DOMAIN_FULL_DETAIL,
)
router = APIRouter()
@router.get("/domains")
async def list_domains(neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)]):
rows = neo4j.run_query(GET_ALL_DOMAINS)
return [
{
"id": r["id"],
"name": r["name"],
"capability_count": r.get("capability_count", 0),
}
for r in rows
]
@router.get("/capabilities")
async def list_capabilities(
neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)],
domain_id: str | None = Query(default=None),
limit: int = Query(default=50, le=200),
):
rows = neo4j.run_query(GET_CAPABILITIES_FOR_DOMAIN, domain_id=domain_id, limit=limit)
return [
{
"id": r["id"],
"name": r["name"],
"domain": r.get("domain_name"),
"subdomain": r.get("subdomain_name"),
"description": r.get("description"),
}
for r in rows
]
@router.get("/stats")
async def graph_stats(neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)]):
rows = neo4j.run_query(GET_DOMAIN_STATS)
return {"node_counts": {r["label"]: r["count"] for r in rows}}
@router.get("/graph/network")
async def graph_network(neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)]):
from backend.graph.cypher_queries import GET_NETWORK_GRAPH
rows = neo4j.run_query(GET_NETWORK_GRAPH)
if not rows:
return {"nodes": [], "edges": []}
row = rows[0]
return {
"nodes": row.get("nodes") or [],
"edges": [e for e in (row.get("edges") or []) if e],
}
@router.get("/subdomains")
async def list_subdomains(
neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)],
domain_names: list[str] = Query(default=[]),
):
from backend.graph.cypher_queries import GET_SUBDOMAINS_FOR_DOMAINS
rows = neo4j.run_query(GET_SUBDOMAINS_FOR_DOMAINS, domain_names=domain_names)
return [
{
"id": r["id"],
"name": r["name"],
"domain_name": r.get("domain_name", ""),
"functional_scope": r.get("functional_scope", ""),
}
for r in rows
]
@router.get("/graph/domain-detail")
async def domain_detail(
domain_name: str,
neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)],
):
rows = neo4j.run_query(GET_DOMAIN_FULL_DETAIL, domain_name=domain_name)
if not rows:
return {"domain": {}, "standard": None, "trend": None, "subdomain_groups": []}
domain = rows[0].get("domain") or {}
standard = rows[0].get("standard")
trend = rows[0].get("trend")
subdomain_map: dict = {}
for r in rows:
sd = r.get("subdomain") or {}
sd_id = sd.get("id")
if not sd_id:
continue
if sd_id not in subdomain_map:
subdomain_map[sd_id] = {"subdomain": sd, "capabilities": []}
cap = r.get("capability") or {}
if cap.get("id"):
cap["subcapability_names"] = r.get("subcapability_names") or []
subdomain_map[sd_id]["capabilities"].append(cap)
# Sort subdomain groups and capabilities within each group alphabetically
groups = sorted(
subdomain_map.values(),
key=lambda g: (g["subdomain"].get("name") or "").lower(),
)
for g in groups:
g["capabilities"].sort(key=lambda c: (c.get("name") or "").lower())
return {
"domain": domain,
"standard": standard,
"trend": trend,
"subdomain_groups": groups,
}
@router.get("/subdomain-capabilities")
async def list_subdomain_capabilities(
neo4j: Annotated[Neo4jClient, Depends(get_neo4j_client)],
subdomain_ids: list[str] = Query(default=[]),
):
from backend.graph.cypher_queries import GET_CAPABILITIES_FOR_SUBDOMAINS
rows = neo4j.run_query(GET_CAPABILITIES_FOR_SUBDOMAINS, subdomain_ids=subdomain_ids)
return [
{
"id": r["id"],
"name": r["name"],
"subdomain": r.get("subdomain_name", ""),
"subdomain_id": r.get("subdomain_id", ""),
"description": r.get("description", ""),
"complexity": r.get("complexity", ""),
"duration_weeks": r.get("duration_weeks"),
}
for r in rows
]