All files / src/app/api/analytics route.ts

66.66% Statements 6/9
75% Branches 3/4
100% Functions 1/1
75% Lines 6/8

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97        1x     2x 2x 2x               2x                                                                                                                     2x                                        
import { NextResponse } from "next/server";
import { runQuery } from "@/lib/neo4j";
import { requireAuth, isAuthError } from "@/lib/auth-guard";
 
export const dynamic = "force-dynamic";
 
export async function GET() {
  const auth = await requireAuth();
  Iif (isAuthError(auth)) return auth;
  try {
    const [
      summary,
      topConditions,
      topDrugs,
      topMeasurements,
      topProcedures,
      genderBreakdown,
    ] = await Promise.all([
      runQuery<{
        persons: number;
        conditions: number;
        drugs: number;
        measurements: number;
        procedures: number;
        visits: number;
      }>(
        `MATCH (op:OMOPPerson)
         WITH count(op) AS persons
         MATCH (oc:OMOPConditionOccurrence)
         WITH persons, count(oc) AS conditions
         MATCH (od:OMOPDrugExposure)
         WITH persons, conditions, count(od) AS drugs
         MATCH (om:OMOPMeasurement)
         WITH persons, conditions, drugs, count(om) AS measurements
         OPTIONAL MATCH (opo:OMOPProcedureOccurrence)
         WITH persons, conditions, drugs, measurements, count(opo) AS procedures
         MATCH (ov:OMOPVisitOccurrence)
         RETURN persons, conditions, drugs, measurements, procedures, count(ov) AS visits`,
      ),
 
      runQuery<{ label: string; count: number }>(
        `MATCH (oc:OMOPConditionOccurrence)
         RETURN oc.name AS label, count(oc) AS count
         ORDER BY count DESC LIMIT 15`,
      ),
 
      runQuery<{ label: string; count: number }>(
        `MATCH (od:OMOPDrugExposure)
         RETURN od.name AS label, count(od) AS count
         ORDER BY count DESC LIMIT 15`,
      ),
 
      runQuery<{ label: string; count: number }>(
        `MATCH (om:OMOPMeasurement)
         RETURN om.name AS label, count(om) AS count
         ORDER BY count DESC LIMIT 15`,
      ),
 
      runQuery<{ label: string; count: number }>(
        `MATCH (opo:OMOPProcedureOccurrence)
         RETURN opo.name AS label, count(opo) AS count
         ORDER BY count DESC LIMIT 15`,
      ),
 
      runQuery<{ gender: string; count: number }>(
        `MATCH (op:OMOPPerson)
         RETURN CASE op.genderConceptId
                  WHEN 8507 THEN 'Male'
                  WHEN 8532 THEN 'Female'
                  ELSE 'Unknown'
                END AS gender,
                count(op) AS count
         ORDER BY count DESC`,
      ),
    ]);
 
    return NextResponse.json({
      summary: summary[0] ?? {
        persons: 0,
        conditions: 0,
        drugs: 0,
        measurements: 0,
        procedures: 0,
        visits: 0,
      },
      topConditions,
      topDrugs,
      topMeasurements,
      topProcedures,
      genderBreakdown,
    });
  } catch (err) {
    console.error("GET /api/analytics error:", err);
    return NextResponse.json({ error: "Neo4j unavailable" }, { status: 502 });
  }
}