# WIN Student Goal Tracker – Developer Documentation
Developer documentation for the WIN Student Goal Tracker project.

Developer Documentation

# WIN Student Goal Tracker

April 2026

## 1. Project Description

The WIN Student Goal Tracker is a web-based case management system designed to support organizations working with adults with special needs. The platform enables staff to track student goals, document services, and log critical incidents while maintaining strong privacy, auditability, and compliance with FERPA, IDEA, and FAPE.

The system is designed with sustainability in mind, ensuring that future developers can easily maintain, extend, and redeploy the application.

## 2. Architecture

### Technology Stack & Infrastructure

Frontend Angular 20
Backend .NET 9.0 (C#) with Dapper ORM
Authentication JWT + Refresh Tokens
Database MySQL
Infrastructure Docker + VPS + Traefik

### Architecture Description

1. Presentation Layer (Angular)
2. Application Layer (.NET API)
3. Data Layer (MySQL)

Traefik manages routing and HTTPS traffic.

## 3. Data Flow

Each scenario lists the UI file that initiates the action, the Angular service method called, the API endpoint hit, the .NET controller and repository method invoked, and the stored procedure that reads or writes the database.

### 1 — User Login

login.html (submit) → Auth.login() [auth.ts] → POST /api/Auth/Login → AuthController.Login() → UserRepository.GetByEmailAsync() + GetProgramsForUserIdAsync() → sp_User_GetByEmail, sp_UserPrograms_GetByUserId → session token + program list → login.html renders program selector

### 2 — View My Students (Home Dashboard)

home.ts (constructor) → StudentService.getMyStudents() [student.service.ts] → GET /api/Student/my → StudentController.GetMyStudents() → StudentRepository.GetMyStudentsAsync() → sp_Student_GetWithAssignments → student card list → home.html renders student cards

### 3 — Open Student Workspace (Full Profile)

home.html (card click → router) → workspace.ts (route param change) → StudentService.getFullProfile() [student.service.ts] → GET /api/Student/{id}/full → StudentController.GetFullProfile() → StudentRepository.GetFullProfileAsync() → sp_Student_GetFullProfile → goals + benchmarks + progress events → workspace.html renders detail view

### 4 — Create Goal

workspace.html (Add Goal button) → goal-modal.ts (onSubmit) → StudentService.createGoal() [student.service.ts] → POST /api/Student/{id}/goals → StudentController.CreateGoal() → PermissionService.IsAllowed() → StudentRepository.InsertGoalAsync() → sp_Goal_Insert → created goal returned → goal-modal closes, workspace.html refreshes

### 5 — Log Progress Event

workspace.html (Add Event) → edit-event-modal.ts (onSave) → StudentService.addProgressEvent() [student.service.ts] → POST /api/Student/{id}/progress-event → StudentController.AddProgressEvent() → StudentRepository.SaveProgressEventAsync() → sp_ProgressEvent_Save → new event ID returned → workspace.html progress list refreshes

## 4. Recommended Hosting

- The entire application is currently hosted online with Hetzner (www.hetzner.com), on a plan donated by team members. The team has committed to hosting for at least the next two years, and if/when that changes, will help the partner transition to the hosting platform of their choice.

## 5. Application Installation

### Prerequisites

Node.js, .NET 9 SDK, MySQL, Docker

### Frontend

    cd frontend
    npm install
    npm start

### Backend

    cd backend
    dotnet restore
    dotnet run

### Database

Create DB, import schema, update `.env`.

### Docker

    docker-compose up --build

## 6. Authentication & Authorization

JWT-based authentication with refresh tokens.

Key files: AuthController.cs, JwtService.cs, middleware

## 7. Database Backup

### Backup

    mysqldump -u username -p dbname > backup.sql

### Restore

    mysql -u username -p dbname < backup.sql

### From the UI

Administrators may back up the database via the "Backup" button in the Administrator panel

## 8. Environment Variables

    DB_CONNECTION=...
    JWT_SECRET=...
    JWT_EXPIRATION=3600

Ensure `.env` is in `.gitignore`.

## 9. Partner Statement

The partner representatives Polly Balsillie and Fred Winter have reviewed the developer documentation and understands its scope and purpose.

Date: April 16, 2026

## 10. Installation Walkthrough Statement

N/A. An installation walkthrough was not appropriate for the partner, as they are non-technical. They are primarily users of the application, and at least two of our team members will continue to make ourselves available to support the application.

Date: April 16, 2026

## 11. Performance & UX Analysis

### Lighthouse Results

Desktop
Mobile

### Form Factor Analysis

- Mobile Portrait: Fully responsive and functional
- Mobile Landscape: Improved readability and layout
- Desktop: Optimal user experience

### UX Observations

Clean navigation with consistent user workflows across devices.

Responsive Accessible High Performance SEO Ready

## 12. Known Liabilities & Improvements

Issues: Potential performance scaling and mobile optimization opportunities

Improvements: Lazy loading, bundle optimization, responsive enhancements

## 13. Sustainability Considerations

Docker deployment, free-tier hosting, and modular design support long-term maintainability.

## Appendix A – ERD

The system includes a structured relational database supporting users, roles, permissions, programs, students, goals, progress tracking, and incident logging. The interactive diagram below shows all 17 tables and their relationships. Drag tables to rearrange, scroll to zoom, and hover a table to highlight its connections.

Open full-page ERD

# ⬡ ERD

Tables 17
Relations 0
Primary Key
Foreign Key
Relationship

## Appendix B – Repository Structure

    WinStudentGoalTracker/
    ├── api/                    .NET 9 backend — controllers, services, repositories, Dockerfile
    ├── db/                     MySQL schema and stored procedures; docker-init scripts for container startup
    ├── docs/                   HTML documentation (technical spec, user manual, ERD, architecture, API)
    ├── ui/                     Angular 20 frontend SPA
    ├── prototype/              Early role-assignment prototypes (not production code)
    ├── docker-compose.yml      Orchestrates API, Angular/Nginx, MySQL, and Traefik containers
    └── WinStudentGoalTracker.sln  Visual Studio solution file

## Appendix C – Architecture Overview

High-level diagrams of the system tiers, two-phase auth flow, data model, role hierarchy, deployment, and key design decisions.

Open full-page Architecture Overview
🏗 System Architecture
Client
Angular SPA v20

- TypeScript 5.8 + SCSS
- Angular Signals (state)
- Lazy-loaded routes
- JWT auth interceptor
- Proactive token refresh

Desktop Layout

- Home / Workspace
- Student detail view
- Goal & Benchmark modals
- Progress event modals
- Report generation UI
- Admin panel

Mobile Layout

- Student card list
- Add progress event
- Toggle benchmark

Shared Services

- AuthService (signals)
- ApiService (HTTP)
- StudentService
- AdminService
- ReportPromptService
- PlatformService

HTTPS / REST + JWT Bearer — win.opelly.me → winapi.opelly.me
Proxy
Traefik Reverse Proxy

- win.opelly.me → Angular (Nginx :8006)
- winapi.opelly.me → API (:8005)
- Let's Encrypt TLS
- Gzip compression
- Security headers

HTTP (internal Docker network)
API
ASP.NET Core 9 C#

- JWT authentication middleware
- Swagger / OpenAPI
- CORS policy
- Dependency injection

Controllers

- /api/Auth — login & tokens
- /api/Student — CRUD
- /api/Admin — district/users
- /api/ReportPrompt — prompts

Services

- TokenService (JWT)
- PermissionService
- PasswordHasher (PBKDF2)
- RecommendationService
- TranscriptionService
- ProgressReportBuilder

Repositories Dapper

- StudentRepository
- UserRepository
- AuthRepository
- AdminRepository
- ReportPromptRepository

Stored procedures via Dapper — TCP :3309 (Docker internal)
Data
MySQL 8 Docker

- winstudentgoaltracker DB
- 17 tables
- 51 stored procedures
- Initialised from db/docker-init/

Core Tables

- user, school_district, program
- user_program (roles junction)
- student, user_student
- goal (recursive), benchmark
- progress_event + junction
- refresh_token

Procedure Groups

- sp_Student_* (CRUD)
- sp_Goal_* / sp_Benchmark_*
- sp_ProgressEvent_*
- sp_RefreshToken_*
- sp_Program_* / sp_User_*
- sp_ProgressReport_*

Outbound HTTP from API tier only
External
Ollama LLM AI

- llm.opelly.me
- Model: gemma4:e2b
- Benchmark recommendations
- 5-min timeout

STT Service AI

- stt.opelly.me
- Speech-to-text transcription
- Progress event dictation
- 5-min timeout

🔐 Two-Phase Authentication Flow
1
User Submits Credentials email + password
POST /api/Auth/Login
2
Phase 1 Response Session token (5 min)
+ program list
Stored in localStorage
3
User Selects Program session token + program ID
POST /api/Auth/SelectProgram
4
Phase 2 Response JWT (1 min) +
Refresh token (30 days)
JWT includes role + program_id
5
Authenticated Requests Bearer JWT on all API calls
Auth interceptor injects token
6
Proactive Refresh 10 s before JWT expiry:
POST /api/Auth/RefreshToken
Rotated refresh token returned
Token storage: `auth_jwt`, `auth_refresh_token`, `auth_session_token` — all in `localStorage`. | 401 interceptor: attempts one silent refresh; if that fails, redirects to `/login`. | Refresh token rotation: each use replaces the old token (tracked via `replaced_by_token_id`).
🗄 Core Data Model
school_district
PK id_school_district
name
contact_email
Top-level tenant
program
PK id_program
FK id_school_district
name, description
Scope boundary for data
user
PK id_user
email, name
password_hash, password_salt
locked_until
user_program (junction)
PK id_user_program
FK id_user
FK id_program
FK id_role
is_primary, status
student
PK id_student
FK id_program
identifier, program_year
enrollment_date
next_iep_date
user_student (junction)
PK id_user_student
FK id_user
FK id_student
is_primary
goal
PK id_goal
FK id_goal_parent (self-ref)
FK id_student
description, category
baseline, target_completion_date
close_date, achieved
benchmark
PK id_benchmark
FK id_goal
benchmark (description)
short_name
progress_event
PK id_progress_event
FK id_goal
FK id_user_created
content (rich text)
is_sensitive
progress_event_benchmark (junction)
FK id_progress_event
FK id_benchmark
Links events to benchmarks
refresh_token
PK id_refresh_token
FK id_user, id_program
token_hash, token_salt
expires_at, revoked_at
replaced_by_token_id
report_prompt
PK id_report_prompt
FK program_id
report_name
prompt_template (LLM)
Key relationships: school_district 1→N program · program 1→N student · student 1→N goal (recursive parent/child) · goal 1→N benchmark · goal 1→N progress_event · progress_event M↔N benchmark (junction) · user M↔N program (via user_program + role) · user M↔N student (via user_student)
👥 Role Hierarchy & Permissions
Role Tiers (highest → lowest)
super_admin · full platform access
district_admin · manages programs
program_admin · manages users
teacher · full student CRUD
paraeducator · log events
Sample Permission Matrix

Entity / Action | super_admin | district_admin | program_admin | teacher | paraeducator
--- | --- | --- | --- | --- | ---
Student — Create | Allow | Allow | Allow | Allow | Deny
Student — Update | Allow | Allow | Allow | Mine | Deny
Goal — Create | Allow | Allow | Allow | Mine | Deny
Goal — Update | Allow | Allow | Allow | Mine | Deny
ProgressEvent — Create | Allow | Allow | Allow | Allow | Mine
ProgressEvent — Delete | Allow | Allow | Allow | Mine | Deny
Program — Create | Allow | Mine | Deny | Deny | Deny
User — Create | Allow | Mine | Mine | Deny | Deny

Allow = full access Mine = own records only Deny = not permitted
🐳 Deployment & Infrastructure

### Docker Containers

- mysql:8 (port 3309)
- .NET API (port 8005)
- Angular / Nginx (port 8006)
- Traefik reverse proxy

### Docker Networks

- web — external HTTPS routing
- backend — internal service mesh
- API depends on MySQL healthcheck
- UI depends on API (build time)

### Environment Config

- MYSQL_ROOT_PASSWORD
- MYSQL_DATABASE / USER / PASSWORD
- JWT_KEY (signing secret)
- MYSQL_HOST / PORT

### Frontend Environments

- Dev: localhost:5000 (API)
- Prod: winapi.opelly.me (API)
- Served by Nginx in production
- Angular CLI dev server locally

### DB Initialisation

- Schemas from db/docker-init/
- 51 stored procedures
- Health check gates API startup
- No ORM migrations — raw SQL

### AI / External Services

- Ollama LLM — llm.opelly.me
- STT service — stt.opelly.me
- Both called from API tier only
- 5-minute timeout on both

⚙️ Key Design Decisions

### Two-Phase JWT Auth

- Phase 1: credentials → session token
- Phase 2: program select → scoped JWT
- Prevents data leakage before program chosen
- JWT scoped to one program at a time

### Refresh Token Rotation

- New token issued on each refresh
- Old token replaced (not deleted)
- Genealogy tracked via replaced_by
- Prevents replay attacks

### Program-Scoped Multi-Tenancy

- JWT contains program_id claim
- All queries filtered by program
- district_admin scoped to district
- super_admin bypasses scope

### Dapper + Stored Procedures

- No ORM (Entity Framework)
- Business logic close to data
- Dapper maps rows to C# objects
- 51 procedures for full coverage

### Angular Signals (not RxJS)

- Auth state as reactive signals
- Computed signals derive user context
- Simpler than Observable chains
- Auto-update dependent UI

### Centralised Permission Matrix

- Declarative rules per entity + action
- Allow / MineOnly / Deny granularity
- Evaluated at request time in API
- Single source of truth for authz