diff --git a/codebenders-dashboard/app/glossary/page.tsx b/codebenders-dashboard/app/glossary/page.tsx new file mode 100644 index 0000000..2ce15c5 --- /dev/null +++ b/codebenders-dashboard/app/glossary/page.tsx @@ -0,0 +1,117 @@ +import Link from "next/link" +import { + buildGlossaryTopicBlocks, + formatGlossarySlugForDisplay, + parseGlossaryEntries, + readMetricGlossaryMarkdown, +} from "@/lib/metric-glossary" + +const BOLD_LEAD_PARAGRAPH = /^\*\*([^*]+)\*\*:\s*([\s\S]*)$/ + +function GlossaryBody({ text }: { text: string }) { + const paragraphs = text.split(/\n\n+/).filter(Boolean) + return ( +
+ {boldLead[1]}: {boldLead[2]} +
+ ) + } + return{p}
+ })} +
+ In-context definitions for dashboard KPIs, with PDP field notes and high-level IPEDS / state
+ cross-walks. Source:{" "}
+ content/metric-glossary.md
+
+ + Back to dashboard + + {" · "} + + Methodology + +
+What it shows: Percentage of students retained year-to-year based on historical data.
Data source: Retention field from student cohort records (0=Not Retained, 1=Retained).
Use for: Baseline institutional performance metric.
++ + Full glossary entry (PDP / IPEDS cross-walk) → + +
> } /> @@ -295,6 +301,11 @@ export default function DashboardPage() {Use for: Early identification of at-risk students for proactive intervention.
++ + Full glossary entry (PDP / IPEDS cross-walk) → + +
> } /> @@ -319,6 +330,11 @@ export default function DashboardPage() {Recommended actions: Immediate advisor outreach, financial aid review, tutoring referrals.
++ + Full glossary entry (PDP / IPEDS cross-walk) → + +
> } /> @@ -339,6 +355,11 @@ export default function DashboardPage() {Why it matters: Strong predictor of retention and credential completion.
++ + Full glossary entry (PDP / IPEDS cross-walk) → + +
> } /> diff --git a/codebenders-dashboard/components/nav-header.tsx b/codebenders-dashboard/components/nav-header.tsx index d995b48..19c0e5d 100644 --- a/codebenders-dashboard/components/nav-header.tsx +++ b/codebenders-dashboard/components/nav-header.tsx @@ -6,6 +6,7 @@ import { GraduationCap, LogOut } from "lucide-react" import { Button } from "@/components/ui/button" import { signOut } from "@/app/actions/auth" import { AI_TRANSPARENCY_HREF } from "@/content/ai-transparency" +import { GLOSSARY_HREF } from "@/lib/glossary-constants" import { ROLE_COLORS, ROLE_LABELS, type Role } from "@/lib/roles" interface NavHeaderProps { @@ -15,6 +16,7 @@ interface NavHeaderProps { const NAV_LINKS: Array<{ href: string; label: string; roles?: Role[] }> = [ { href: "/", label: "Dashboard" }, + { href: GLOSSARY_HREF, label: "Glossary" }, { href: "/courses", label: "Courses" }, { href: "/students", label: "Students" }, { href: "/query", label: "Query" }, diff --git a/codebenders-dashboard/content/metric-glossary.md b/codebenders-dashboard/content/metric-glossary.md new file mode 100644 index 0000000..2c733ec --- /dev/null +++ b/codebenders-dashboard/content/metric-glossary.md @@ -0,0 +1,51 @@ +# Metric glossary + +Single source of truth for dashboard KPI definitions. Each section is keyed by its URL anchor (e.g. `/glossary#overall-retention-rate`). Cross-walks are indicative — institutions should confirm against their PDP documentation, IPEDS submission manuals, and state reporting rules. + +--- + +## overall-retention-rate + +**Plain English:** Share of students in the selected cohort who are still enrolled (or completed) one year later — a common “year-to-year” retention view for the students you are filtering. + +**PDP / analysis-ready:** Uses the cohort retention indicator on `student_level_with_predictions` (historical `Retention` field: 0 = not retained to the next year, 1 = retained) after filters (cohort, enrollment intensity, credential goal) are applied. + +**IPEDS:** Closest published analog is *Fall cohort retention* for first-time full-time students; PDP cohorts may include part-time or mixed populations, so percentages will not match IPEDS one-to-one without aligning cohort definitions. + +**State compliance:** Many state accountability dashboards publish “first-year retention” or “persistence”; map this metric to the state field that uses the same cohort and time window. + +--- + +## avg-predicted-retention + +**Plain English:** Average of the model’s estimated probability (0–100%) that each student in the filtered set will retain — not the same as historical retention above. + +**PDP / analysis-ready:** Mean of `retention_probability` from the deployed XGBoost retention model on `student_level_with_predictions`. + +**IPEDS:** No direct IPEDS submission — this is an institutional analytics prediction, not an audited outcome count. + +**State compliance:** Treat as early-warning / planning metric unless your state explicitly allows predictive indicators in reporting. + +--- + +## students-at-high-critical-risk + +**Plain English:** Count of students whose composite risk score places them in the **HIGH** or **URGENT** alert bands used for intervention triage. + +**PDP / analysis-ready:** Derived from `at_risk_alert` and related thresholds on `student_level_with_predictions` (see dashboard methodology for the composite formula). + +**IPEDS:** Not an IPEDS field; comparable to internal early-alert counts only. + +**State compliance:** Use for operations; confirm before exporting small subgroup counts externally (FERPA / small-N policies). + +--- + +## avg-course-completion + +**Plain English:** Credits successfully completed divided by credits attempted, expressed as a percentage, aggregated across students in the filter. + +**PDP / analysis-ready:** Computed from course completion fields on `student_level_with_predictions` (credits attempted vs. earned in the modeled year window). + +**IPEDS:** Conceptually related to success rates and progression, but IPEDS collects many distinct measures (e.g., completions by award) — do not assume identity without a written cross-walk. + +**State compliance:** Often parallels “success rate” or “credit completion ratio” in performance funding models; verify denominator rules match your state formula. diff --git a/codebenders-dashboard/lib/__tests__/metric-glossary-coverage.test.ts b/codebenders-dashboard/lib/__tests__/metric-glossary-coverage.test.ts new file mode 100644 index 0000000..debf689 --- /dev/null +++ b/codebenders-dashboard/lib/__tests__/metric-glossary-coverage.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest" +import { + DASHBOARD_KPI_GLOSSARY_SLUGS, + GLOSSARY_TOPIC_SECTIONS, +} from "@/lib/glossary-constants" +import { parseGlossaryEntries, readMetricGlossaryMarkdown } from "@/lib/metric-glossary" + +describe("metric glossary coverage (#105)", () => { + it("includes every dashboard KPI slug in metric-glossary.md", () => { + const md = readMetricGlossaryMarkdown() + const entries = parseGlossaryEntries(md) + for (const slug of DASHBOARD_KPI_GLOSSARY_SLUGS) { + expect(entries[slug], `missing ## ${slug} in content/metric-glossary.md`).toBeTruthy() + expect(entries[slug]!.length).toBeGreaterThan(20) + } + }) + + it("topic sections list each KPI slug exactly once", () => { + const listed = GLOSSARY_TOPIC_SECTIONS.flatMap((t) => [...t.slugOrder]) + expect(listed.length).toBe(DASHBOARD_KPI_GLOSSARY_SLUGS.length) + for (const slug of DASHBOARD_KPI_GLOSSARY_SLUGS) { + const n = listed.filter((s) => s === slug).length + expect(n, `slug ${slug} should appear once in GLOSSARY_TOPIC_SECTIONS`).toBe(1) + } + }) +}) diff --git a/codebenders-dashboard/lib/glossary-constants.ts b/codebenders-dashboard/lib/glossary-constants.ts new file mode 100644 index 0000000..2a86003 --- /dev/null +++ b/codebenders-dashboard/lib/glossary-constants.ts @@ -0,0 +1,42 @@ +/** Nav and deep links — safe to import from client components. */ + +export const GLOSSARY_HREF = "/glossary" as const + +/** + * Source of truth for topic groupings and KPI slug order on the glossary page. + * `DASHBOARD_KPI_GLOSSARY_SLUGS` is derived from this list; every `##` section in + * `content/metric-glossary.md` must stay in sync (see + * `lib/__tests__/metric-glossary-coverage.test.ts`). + */ +const GLOSSARY_TOPIC_SECTIONS_RAW = [ + { + id: "retention", + label: "Retention, risk & predictions", + slugOrder: [ + "overall-retention-rate", + "avg-predicted-retention", + "students-at-high-critical-risk", + ] as const, + }, + { + id: "completion", + label: "Completion & course success", + slugOrder: ["avg-course-completion"] as const, + }, +] as const + +export type DashboardKpiGlossarySlug = + (typeof GLOSSARY_TOPIC_SECTIONS_RAW)[number]["slugOrder"][number] + +export const DASHBOARD_KPI_GLOSSARY_SLUGS: readonly DashboardKpiGlossarySlug[] = + GLOSSARY_TOPIC_SECTIONS_RAW.flatMap((topic) => [...topic.slugOrder]) + +export const GLOSSARY_TOPIC_SECTIONS: { + id: string + label: string + slugOrder: readonly DashboardKpiGlossarySlug[] +}[] = GLOSSARY_TOPIC_SECTIONS_RAW.map((topic) => ({ + id: topic.id, + label: topic.label, + slugOrder: topic.slugOrder, +})) diff --git a/codebenders-dashboard/lib/metric-glossary.ts b/codebenders-dashboard/lib/metric-glossary.ts new file mode 100644 index 0000000..270be93 --- /dev/null +++ b/codebenders-dashboard/lib/metric-glossary.ts @@ -0,0 +1,49 @@ +import fs from "fs" +import path from "path" + +import { GLOSSARY_TOPIC_SECTIONS } from "@/lib/glossary-constants" + +export function readMetricGlossaryMarkdown(): string { + const file = path.join(process.cwd(), "content", "metric-glossary.md") + return fs.readFileSync(file, "utf8") +} + +/** Map slug → markdown body (text below `## slug`). */ +export function parseGlossaryEntries(md: string): Record