This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
AnalyticsK12 is a K-12 school district analytics platform. It manages student demographics, attendance (absences), discipline (incidents), staffing (teachers, courses), and family contacts (parents) across multiple school sites. It includes role-based access control, bulk CSV import, encrypted credentials, and session-based global filters (school year, site, snap date, student status).
# Run dev server
uv run flask --app main.py run
# Or activate the venv and use flask directly
source .venv/bin/activate
flask --app main.py runDependencies are managed with uv. To add or sync packages:
uv sync
uv add <package>MySQL via PyMySQL. Connection string is in .env as DATABASE_URL.
# Apply all pending migrations
flask --app main.py db upgrade
# Generate a new migration after model changes
flask --app main.py db migrate -m "description"
# Check current migration version
flask --app main.py db currentMigration files live in migrations/versions/. After adding or changing a model, always run migrate then upgrade.
# 1. Create MySQL DB and write .env (interactive)
python installation/create_env.py
# 2. Seed roles, default site, admin user (interactive)
python installation/seed_data.py
# 3. Seed academic demo data (sites, students, teachers, courses, parents, absences, incidents)
python installation/seed_academic_data.pymain.py contains create_app(config_name). It initializes Flask extensions (SQLAlchemy, Flask-Login, CSRF, Flask-Mail, Flask-Limiter, APScheduler), registers the single blueprint, applies security headers, and wires database-stored SMTP config from the Organization model.
All routes live in application/routes.py under one blueprint (routes_blueprint). The file is large (~2700+ lines) and organized into labeled sections:
- Auth — login, logout, set-password, account lockout
- Users / Roles / Sites / Notifications / Organization — admin management
- Global session filters —
/set_school_year,/set_site_filter,/set_snap_date,/set_status_filter(store to session, redirect back) - Context processor — injects
active_schoolyr,active_site_filter,active_snap_date,active_status_filter,global_sites,global_school_yearsinto every template - Students — list (paginated, multi-filter), detail, edit, export CSV (
/students/export/csv) - Demographics dashboard —
/demographics(charts + tables) - SWD dashboard —
/swd - Absences —
/absences/dashboard,/absences(list) - Discipline dashboard —
/discipline - Incidents —
/incidents(list) - Teachers / Courses / Parents — list + detail + edit
Key models and their notable fields:
| Model | Notes |
|---|---|
Student |
status is a computed @property from enter_date/exit_date — there is no status column in DB |
User |
Email stored encrypted (cryptography.fernet); password hashed with scrypt |
Organization |
Stores SMTP and FTP config with encrypted passwords; config overrides app.config at startup |
Absence |
Linked to student via ssid string (not FK), site via site_id FK |
Incident |
Linked to student via sisid string (not FK); site is stored as the site acronym string |
Site |
Has both site_name and site_acronyms — dashboards use acronyms for compact display |
The context processor reads these four session keys and injects them into every template:
active_schoolyr— school year string (e.g."2025-2026")active_site_filter— site ID as stringactive_snap_date— ISO date string for enrollment-as-of queriesactive_status_filter—"active"|"inactive"|"all"
Routes that support site filtering from the URL (e.g. dashboard table links) check request.args.get('site_filter') first, then fall back to session.
/students supports multi-select subgroup filtering via repeated ?subgroup= URL params (getlist). The route applies AND logic across all selected subgroups. Supported values: homeless, frm, swd, foster, migrant, sed504, no_ssid.
English status and gender use single-value URL params (english_status, gender).
application/templates/base.html— main layout; includesincludes/nav.htmlapplication/templates/includes/nav.html— sidebar nav + top navbar with global filter barapplication/templates/dashboard/— analytics dashboards (demographics, swd, discipline, absenteeism)- Chart rendering uses Chart.js loaded from
static/js/plugins/chartjs.min.js - Print/PDF export uses a
printDashboard()JS function that swaps canvas elements for images and applies a zoom factor
application/static/css/dashboards.css contains shared dashboard styles including .demo-kpi, .chart-card, .chart-wrap and @media print rules for all dashboards.
site_filterparameter: In the students route, URL paramsite_filteroverrides session. In all other routes it comes from session only.- Ethnicity codes: Stored as 3-digit strings (
'500'= Hispanic/Latino,'600'= African American, etc.). Theethnicity_table_datavariable is always a list of(code, label, count)tuples. - Add pages removed:
add_student,add_teacher,add_course,add_parentroutes redirect to their list pages — the templates were intentionally deleted. student.status: Never filter withStudent.status == 'Active'in SQL — it's a Python property. Useenter_date/exit_dateconditions instead.