Phase 2: Multi-Department + RBAC + Resident Access

Overview

Duration: ~3.5 weeks (120-140 hours) Goal: Multiple departments with proper access control. PDs see their department, residents see only themselves. Demo: PD logs in → sees OB-GYN dashboard with form type toggle. Resident logs in → sees only their own data.

Prerequisites: Phase 1 complete (OB-GYN data imported, schema in place)


Timeline

Week 2D4      Week 3                        Week 4                        Week 5
├─────────────┼─────────────────────────────┼─────────────────────────────┼─────────────────────────────┤
│ Day 4-5     │ Day 1-2  │ Day 3-4 │ Day 5 │ Day 1-2  │ Day 3-4 │ Day 5 │ Day 1-2  │ Day 3-4 │ Day 5  │
│ Users +     │ RBAC     │ Auth    │ Data  │ Dynamic  │ Form    │ EM    │ Resident │ Resident│ Test   │
│ Mappings    │ Design   │ Middle  │ Filter│ UI       │ Selector│ Migr  │ Dashboard│ Polish  │ Demo   │
├─────────────┼──────────┼─────────┼───────┼──────────┼─────────┼───────┼──────────┼─────────┼────────┤
│    12h      │    8h    │   12h   │  8h   │   16h    │   8h    │  12h  │   16h    │   8h    │  12h   │
└─────────────┴──────────┴─────────┴───────┴──────────┴─────────┴───────┴──────────┴─────────┴────────┘

                                                                                          DEMO 2

User Stories

IDStoryAcceptance Criteria
US-2.1As an admin, I can upload a CSV mapping learner names to user emailsMappings appear in learner_mappings table
US-2.2As a program director, I can log in and see all residents in MY department onlyPD sees full OB-GYN data, cannot see EM data
US-2.3As a resident, I can log in and see only MY OWN evaluation dataResident sees personal evaluations, not others
US-2.4As a super admin, I can see data across all departmentsSuper admin has no department filter
US-2.5As a dept admin, I can manage users and learner mappings for my departmentCan create users, upload mappings for own dept
US-2.6As a PD viewing OB-GYN, I can toggle between Milestones and Procedures viewsDropdown/toggle switches displayed data

Epic 2.0: User & Identity Foundation

Estimated Time: 12 hours

Task IDTaskDescriptionHoursDependencies
2.0.1Users TableCreate users table (email, name, role, department_id)3hPhase 1
2.0.2Learner Mappings TableCreate learner_mappings table (learner_name → user_id)3h2.0.1
2.0.3CSV Mapping ImporterParse learner-to-email CSV and load into learner_mappings3h2.0.2
2.0.4CSV Upload EndpointPOST /admin/upload/mappings - accepts CSV3h2.0.3

Schema Additions

-- Users (for authentication and RBAC)
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(200) NOT NULL UNIQUE,
    name VARCHAR(200),
    role VARCHAR(50) NOT NULL,           -- super_admin, dept_admin, program_director, resident
    department_id INTEGER REFERENCES departments(id),
    created_at TIMESTAMP DEFAULT NOW()
);

-- Learner Mappings (links learner names from data to user accounts)
CREATE TABLE learner_mappings (
    id SERIAL PRIMARY KEY,
    department_id INTEGER REFERENCES departments(id),
    learner_name VARCHAR(200) NOT NULL,  -- From source data (Excel)
    user_id INTEGER REFERENCES users(id),
    grad_year INTEGER,
    created_at TIMESTAMP DEFAULT NOW(),
    UNIQUE(department_id, learner_name)
);

-- Add optional user references to evaluation_responses
ALTER TABLE evaluation_responses
    ADD COLUMN evaluator_user_id INTEGER REFERENCES users(id),
    ADD COLUMN learner_user_id INTEGER REFERENCES users(id);

CSV Mapping Format

learner_name,email,grad_year

Epic 2.1: RBAC Foundation

Estimated Time: 28 hours

Task IDTaskDescriptionHoursDependencies
2.1.1Role Permissions MatrixDocument what each role can do4hNone
2.1.2Roles ImplementationAdd role validation logic4h2.1.1, 2.0.1
2.1.3Auth Middleware RefactorReplace hardcoded email list with DB lookup8h2.0.1
2.1.4Role DecoratorCreate @require_role('program_director') decorator4h2.1.3
2.1.5Data Filtering LayerQueries auto-filter by user’s role + department8h2.1.3, 2.1.4, 2.0.2

Role Permissions Matrix

Permissionsuper_admindept_adminprogram_directorresident
View all departments
View own department (all learners)
View own data only
Upload data
Manage users (own dept)
Manage users (all depts)
Upload learner mappings
Download ILP✅ (own)

Auth Flow

Request with x-goog-authenticated-user-email header


        ┌───────────────────┐
        │ Lookup user by    │
        │ email in DB       │
        └─────────┬─────────┘

        ┌─────────┴─────────┐
        │                   │
        ▼                   ▼
   User Found          User Not Found
        │                   │
        ▼                   ▼
   Set request.user    Return 403
   with role, dept     (not authorized)


   Route handler with
   @require_role checks

Epic 2.2: Multi-Department Support

Estimated Time: 20 hours

Task IDTaskDescriptionHoursDependencies
2.2.1Department ParameterUpdate endpoints to accept/filter by dept_id6hPhase 1
2.2.2Department SwitcherUI dropdown for users with multi-dept access4h2.1.3
2.2.3EM Data MigrationMigrate medhub_eval_response → unified schema8hPhase 1 schema
2.2.4Seed OB-GYN DepartmentCreate OB-GYN department record, link forms2hPhase 1

EM Migration Strategy

Existing: medhub_eval_response (EM-specific)

              │ Migration script

New: evaluation_responses (unified)

              │ Keep old table until verified

Cutover: Update EM routes to use new table

              │ After validation period

Cleanup: Drop old table (future)

Epic 2.3: Dynamic UI

Estimated Time: 36 hours

Task IDTaskDescriptionHoursDependencies
2.3.1Dynamic Form Definitions APIEndpoint returns form structure for department4hPhase 1
2.3.2Frontend Dynamic ColumnsUpdate JS to render columns from API, not hardcoded12h2.3.1
2.3.3Dynamic ReportsUpdate histogram, trending for dynamic data8h2.3.2
2.3.4Answer Type HandlingHandle numeric, enum, text, comments in UI4h2.3.2
2.3.5Form Type SelectorDropdown to switch between Milestones/Procedures6h2.3.2
2.3.6Comments DisplayHandle comments filtering and display2h2.3.4

Form Type Selector UI

┌─────────────────────────────────────────────────────────────────┐
│  OB-GYN Department Dashboard                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  View: [ Milestones ▼ ]    Date Range: [2024-01-01] - [2025-01] │
│        ┌─────────────┐                                          │
│        │ Milestones  │  ← Currently viewing                     │
│        │ Procedures  │                                          │
│        └─────────────┘                                          │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Resident  │ PROF-3 │ ICS-3 │ PC-13 │ PC-1 │ ... │ Avg  │    │
│  ├────────────┼────────┼───────┼───────┼──────┼─────┼──────┤    │
│  │  Resident  │  4.2   │  4.5  │  3.8  │ 4.0  │ ... │ 4.1  │    │
│  │  Resident  │  3.9   │  4.1  │  4.2  │ 3.7  │ ... │ 4.0  │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

Epic 2.4: Resident Self-Service (AIM 1 - Basic)

Estimated Time: 28 hours

Task IDTaskDescriptionHoursDependencies
2.4.1Resident Login FlowDetect role, redirect to appropriate dashboard4h2.1.3
2.4.2Personal Data APIGET /api/me/evaluations filtered to logged-in learner8h2.1.5, 2.0.2
2.4.3Resident Dashboard PagePersonal view template and styling12h2.4.2
2.4.4No-Data HandlingGraceful handling when learner has no mapping4h2.4.2

How Resident Data Filtering Works

1. Resident logs in with U-M L2


2. Auth middleware looks up user in `users` table
   → user.id = 42, user.role = 'resident', user.department_id = 1


3. Request hits /api/me/evaluations


4. Look up learner_mapping where user_id = 42
   → learner_name = "Name"


5. Query evaluation_responses WHERE learner_name = "Name"
   AND department_id = 1


6. Return only this resident's evaluations

Resident Dashboard Wireframe

┌─────────────────────────────────────────────────────────────────┐
│  Mi-TRAC - Resident name (PGY-3, OB-GYN)              [Logout]  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  My Evaluations                                                 │
│  ───────────────                                                │
│  View: [ Milestones ▼ ]                                         │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  Date       │ Evaluator    │ Form    │ Score │ Details  │   │
│  ├─────────────┼──────────────┼─────────┼───────┼──────────┤   │
│  │  2025-01-05 │ Dr. One      │ PROF-3  │  4.5  │  [View]  │   │
│  │  2025-01-03 │ Dr. Two      │ PC-13   │  4.0  │  [View]  │   │
│  │  2024-12-28 │ Dr. Threee   │ C-Sect  │  4.2  │  [View]  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  Total Evaluations: 47  │  Average Score: 4.1                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Epic 2.5: Future/Budget Items

Task IDTaskDescriptionPriority
2.5.1Cohort ComparisonAnonymized comparison to PGY cohortFuture
2.5.2TRUE SCORE CalculationTrue score for dynamic formsTBD
2.5.3Additional OB-GYN FormsTeaching, M&M, Journal Club, etc.Stretch
2.5.4User Self-RegistrationNew users request access, admin approvesFuture
2.5.5Individual Mapping EditorUI for editing learner mappings one-by-oneFuture

Deliverables

DeliverableDescriptionFormat
Users & Mappings TablesUser accounts and learner identity linkingAlembic migrations
CSV Mapping UploadEndpoint for bulk learner-email mappingFlask route
RBAC SystemRole-based access control with decoratorsPython middleware
Multi-Dept SupportDepartment filtering on all queriesPython/SQL
Dynamic UIFrontend handles any form typeJavaScript
Form Type SelectorDropdown for Milestones vs ProceduresUI component
Resident DashboardPersonal data view for residentsHTML/JS template
EM MigrationExisting data moved to unified schemaMigration script

Demo Script (End of Week 5)

Demo 2A: Admin - User & Mapping Setup

  1. Upload learner mappings CSV: POST to /admin/upload/mappings
  2. Show mappings created: Query learner_mappings table
  3. Create PD user: Add program director to users table
  4. Create resident user: Add resident to users table

Demo 2B: Program Director View

  1. Login as OB-GYN PD: Navigate to app, authenticate
  2. Show department dashboard: All OB-GYN residents visible
  3. Toggle form type: Switch from Milestones to Procedures
  4. Show data changes: Different columns, different scores
  5. Attempt to view EM: Should be blocked or not visible

Demo 2C: Resident View

  1. Login as OB-GYN resident: Different user, authenticate
  2. Show personal dashboard: Only their evaluations visible
  3. Toggle form type: See personal Milestones vs Procedures
  4. Show no access to others: Cannot see other residents

Demo 2D: Admin View

  1. Login as super admin: Authenticate
  2. Show department switcher: Can select OB-GYN or EM
  3. View both departments: Full access to all data

Definition of Done

  • Users table created with role field
  • Learner mappings table links names to user accounts
  • CSV upload for learner mappings works
  • RBAC middleware correctly identifies user role from DB
  • Program directors see only their department’s data
  • Residents see only their own data
  • Form type selector works for OB-GYN (Milestones/Procedures)
  • Dynamic UI renders columns from form definitions
  • EM data migrated to unified schema (or abstraction works)
  • No regressions to existing EM functionality
  • Code reviewed and merged to main