Skip to content

feat: course sequencing insights and DFWI analysis (#85)#87

Merged
William-Hill merged 11 commits intorebranding/bishop-statefrom
stretch/task-85-course-dfwi
Feb 23, 2026
Merged

feat: course sequencing insights and DFWI analysis (#85)#87
William-Hill merged 11 commits intorebranding/bishop-statefrom
stretch/task-85-course-dfwi

Conversation

@William-Hill
Copy link
Collaborator

Summary

  • Adds course_enrollments table (migration + indexes) and streams bishop_state_courses.csv (~99,559 rows) into it via an idempotent, transaction-wrapped ingestion script
  • Three new API routes under /api/courses/: dfwi (per-course DFWI rates with filters), gateway-funnel (Math/English gateway pass/DFWI breakdown by cohort), sequences (top 20 course prefix+number co-enrollment pairs with pass rate overlay)
  • New /courses page with sortable high-risk course table, Recharts gateway funnel charts, and top co-enrollment pairs table
  • "Courses" nav link added; role guard (admin/advisor/ir/faculty) on all new routes and page

Test Plan

  • Run ingestion script and verify SELECT COUNT(*) FROM course_enrollments = 99,559
  • /courses page loads; DFWI table shows courses sorted by DFWI rate descending
  • "Gateway courses only" toggle narrows table to Math/English gateway courses
  • Math and English funnel charts render with bars per cohort (Attempted / Passed / DFWI)
  • Top course pairings table shows prefix+number pairs (e.g. ENG 101 / MAT 100) with both-pass rate
  • Leadership role is redirected away from /courses (middleware enforces this)
  • "Courses" nav link appears and highlights when on /courses

- Redesign /courses page with 3 tabs: DFWI Rates, Gateway Funnel, Co-enrollment Insights
- Add POST /api/courses/explain-pairing route: queries per-pair stats (individual
  DFWI/pass rates, breakdown by delivery method and instructor type) then calls
  gpt-4o-mini to generate an advisor-friendly narrative
- Co-enrollment Insights tab shows sortable pairings table with per-row Explain
  button that fetches and renders stats chips + LLM analysis inline
- Tab state is client-side (no Radix Tabs dependency needed)
@William-Hill
Copy link
Collaborator Author

Workflow fix pushed to base branch — re-triggering CI by pushing a no-op update.

- Replace next lint with direct eslint . in package.json lint script
- Rewrite eslint.config.mjs to use eslint-config-next flat config exports
  directly instead of deprecated FlatCompat bridge
- Add eslint and eslint-config-next as devDependencies
- Suppress pre-existing rule violations (no-explicit-any, no-unescaped-entities,
  set-state-in-effect) to avoid CI failures on legacy code
@William-Hill William-Hill merged commit 65c5d21 into rebranding/bishop-state Feb 23, 2026
2 checks passed
William-Hill added a commit that referenced this pull request Feb 24, 2026
* feat: add cohort and enrollment intensity filters to student roster (#81) (#82)

* docs: add demo script with 6-minute talk track and screenshot guide

* docs: move DEMO.md into docs/ directory

* docs: move demo script and screenshots into docs/demo/ subdirectory

* chore: untrack large presentation files, add *.pptx and docs PDFs to .gitignore

* feat: student roster page with drill-down, filtering, sorting, and CSV export (#65)

* feat: student roster with info popovers; fix gateway models using 'Y' not 'C' for completion target

* fix: credential model — add sought-credential fallback and class_weight=balanced; add sorting for enrollment and credential type columns

* fix: update credential type popover to reflect sought-credential fallback logic

* feat: dashboard filtering by cohort, enrollment type, and credential type (#66) (#72)

- Add filter bar above KPI tiles with Cohort, Enrollment Type, and Credential Type
  dropdowns (shadcn Select) and a Clear button with filtered-student count
- All 4 dashboard API routes now accept cohort, enrollmentType, credentialType
  query params and apply parameterized WHERE clauses
- Risk alerts and retention-risk routes use a CTE so percentage denominators
  are relative to the filtered set (not the full table)
- Readiness route conditionally JOINs student_level_with_predictions when
  enrollment or credential filters are active; existing institution/cohort/level
  params unchanged
- All fetch calls on the page are re-triggered when filter state changes

* feat: audit log export endpoint with CSV download button (#67) (#73)

- Add GET /api/query-history/export that reads logs/query-history.jsonl and
  streams a CSV with headers: timestamp, institution, prompt, vizType, rowCount
- Accepts optional ?from=ISO_DATE&to=ISO_DATE query params for date-range
  filtering; returns 404 with clear message if the log file does not exist yet
- Sets Content-Disposition: attachment; filename="query-audit-log.csv"
- Add "Export" button with download icon in the QueryHistoryPanel header that
  triggers a direct browser download via <a href download>

* feat: student detail view with personalized recommendations (#77) (#79)

- Add GET /api/students/[guid] joining student_level_with_predictions +
  llm_recommendations; returns 404 for unknown GUIDs
- Add /students/[guid] page with:
    - Student header: GUID, cohort, enrollment, credential, at-risk + readiness badges
    - FERPA disclaimer (de-identified GUID only, no PII stored)
    - Six prediction score cards (retention, readiness, gateway math/English,
      GPA risk, time-to-credential) color-coded green/yellow/red
    - AI Readiness Assessment card: rationale, risk factors (orange dot list),
      and recommended actions (checkbox-style checklist)
    - Graceful fallback when no assessment has been generated yet
    - Back button uses router.back() to preserve roster filter state
- Student roster rows are now fully clickable (onClick → router.push)
  with the GUID cell retaining its Link for ctrl/cmd+click support

* feat: Supabase Auth + role-based access control (FR6, #75) (#80)

* feat: Supabase Auth + role-based access control (FR6, #75)

Auth layer
- Install @supabase/supabase-js + @supabase/ssr
- lib/supabase/client.ts  — browser client (createBrowserClient)
- lib/supabase/server.ts  — server client (createServerClient + cookies)
- lib/supabase/middleware-client.ts — session refresh helper for middleware

Roles
- lib/roles.ts — Role type, ROUTE_PERMISSIONS map, canAccess() helper,
  ROLE_LABELS and ROLE_COLORS per role
- Five roles: admin | advisor | ir | faculty | leadership
  /students/**            → admin, advisor, ir
  /query                  → admin, advisor, ir, faculty
  /api/students/**        → admin, advisor, ir
  /api/query-history/export → admin, ir
  / and /methodology      → all roles (public within auth)

Middleware
- middleware.ts — unauthenticated → redirect /login; role resolved from
  user_roles table; canAccess() enforced; role + user-id + email forwarded
  as request headers (x-user-role, x-user-id, x-user-email) for API routes

Login page
- app/login/page.tsx — email/password form using createBrowserClient
- app/auth/callback/route.ts — PKCE code exchange handler

Navigation
- components/nav-header.tsx — sticky top bar: role badge, email, sign-out
- app/layout.tsx — server component reads session + role, renders NavHeader
  when authenticated

API guards
- /api/students: 403 for faculty + leadership
- /api/students/[guid]: 403 for faculty + leadership
- /api/query-history/export: 403 for non-admin/ir

Database & seed
- migrations/001_user_roles.sql — user_roles table + RLS policy
- scripts/seed-demo-users.ts — creates 5 demo users via service role key
  (admin/advisor/ir/faculty/leadership @bscc.edu, pw: BishopState2025!)

* fix: seed script accepts NEXT_PUBLIC_ env var names; install tsx dev dep

* feat: add cohort and enrollment intensity filters to student roster (#81)

* fix: use correct DB enrollment intensity values (Full-Time/Part-Time with hyphens)

* chore: add GitHub Actions CI/CD workflows (#83) (#84)

* fix: drop npm lockfile cache since package-lock.json is gitignored

npm ci requires a lockfile; switch to npm install in ci-dashboard and
security-audit workflows to avoid cache resolution failures.

* feat: course sequencing insights and DFWI analysis (#85) (#87)

* feat: add course_enrollments migration and data ingestion script (#85)

* fix: transaction safety and validation in course enrollment ingestion (#85)

* feat: add course DFWI, gateway funnel, and sequence API routes (#85)

* fix: sequence join granularity, gateway funnel clarity, DFWI result cap (#85)

* feat: add /courses page with DFWI table, gateway funnel, and co-enrollment pairs (#85)

* fix: percentage display and component cleanup in /courses page (#85)

* fix: gateway type label values (M/E) and add RBAC to gateway-funnel route (#85)

* feat: sortable column headers, info popovers for DFWI/pass rate, pairings table sort (#85)

* feat: tabbed courses page with AI-powered co-enrollment explainability

- Redesign /courses page with 3 tabs: DFWI Rates, Gateway Funnel, Co-enrollment Insights
- Add POST /api/courses/explain-pairing route: queries per-pair stats (individual
  DFWI/pass rates, breakdown by delivery method and instructor type) then calls
  gpt-4o-mini to generate an advisor-friendly narrative
- Co-enrollment Insights tab shows sortable pairings table with per-row Explain
  button that fetches and renders stats chips + LLM analysis inline
- Tab state is client-side (no Radix Tabs dependency needed)

* ci: trigger re-run after workflow fix

* fix: update ESLint for Next.js 16 (next lint removed)

- Replace next lint with direct eslint . in package.json lint script
- Rewrite eslint.config.mjs to use eslint-config-next flat config exports
  directly instead of deprecated FlatCompat bridge
- Add eslint and eslint-config-next as devDependencies
- Suppress pre-existing rule violations (no-explicit-any, no-unescaped-entities,
  set-state-in-effect) to avoid CI failures on legacy code

* feat: NQL interface redesign — nav link, sidebar history, LLM Summarize (#88) (#89)

* docs: NQL interface redesign design doc (#88)

* docs: NQL redesign implementation plan (#88)

* feat: add Query link to global nav header (#88)

* feat: add POST /api/query-summary LLM result narration (#88)

* fix: harden query-summary route input validation (#88)

* refactor: adapt QueryHistoryPanel for sidebar layout (#88)

* fix: restore institution label, fix text size, restore truncation threshold (#88)

* feat: sidebar layout + LLM Summarize button on query page (#88)

* fix: NQL rate formatting and course_enrollments routing (#90)

- Add isRateColumn helper and formatCellValue to render 0–1 probabilities
  as percentages in table cells, chart axes, tooltips, and KPI display
- Fix KPI suffix to only append % when value is actually in 0–1 range
- Add course_enrollments table to LLM prompt with schema, routing rules,
  DFWI/pass rate SQL patterns, FERPA guardrails, and worked example
- Add SchemaEntry interface; remove as-any cast on courseColumns
- SQL patterns return 0–1 scale; display layer handles multiplication

* fix: prevent LLM from adding institution_id filter to course_enrollments

course_enrollments has no institution_id column; the INSTITUTION context
in the prompt was causing the LLM to borrow the Institution_ID filter
from the student table and apply it to course queries, producing:
  Error: column "institution_id" does not exist

* feat: add viz type tab strip for user override of LLM chart choice

Renders Table/Bar/Line/Pie/KPI buttons above query results. Defaults to
the LLM's chosen vizType and resets on each new query, so users can
switch views without re-running the query.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant