xiaohongshu
xiaohongshu
fm-residency-scheduling
Family Medicine Residency Excel-based scheduling. Use when working with academic year block schedule spreadsheets to assign faculty half-days (C, GME, DFM) and resident half-days, validate constraints, process FMIT/SAFP blocks, apply post-call rules, and meet AT coverage. Triggers: Block schedule...
Full skill instructions
Automates faculty and resident half-day assignments directly in Excel for Family Medicine residency block schedules.
Excel is the communication layer to code. The Excel workbook serves as configuration input - the scheduling engine reads from it and has more detailed logic internally. Think of Excel as the "what" and the code as the "how."
See: docs/architecture/HALF_DAY_ASSIGNMENT_MODEL.md
The scheduling system uses persisted half-day assignments with actual dates (not block references). This enables:
Source Priority (when multiple sources want same slot):
preload - FMIT, call, absences - NEVER overwrittenmanual - Explicit human overridesolver - Computed by CP-SATtemplate - Default from WeeklyPatternThis table is the foundation of all scheduling logic.
| Activity | Definition | Physical Capacity | AT Coverage |
|---|---|---|---|
| Resident C | Resident seeing patients | Counts (max 6) | Creates demand |
| Faculty C | Faculty seeing OWN patients | Counts (max 6) | None |
| Faculty AT | Faculty supervising residents | Does NOT count | Provides |
| PCAT | Post Call Attending Time | Does NOT count | Provides (= AT) |
| DO | Direct Observation | - | Auto-assigned after call |
Key Rules:
Terminology (CRITICAL - do not confuse):
Physical Capacity: Max 6 people doing clinical work (C) per half-day. AT does NOT count toward this limit because AT faculty are supervising, not generating their own patient load.
Phase 1: PRELOAD (locked before solver)
Phase 2: SOLVER (computed)
All blocks start Thursday, end Wednesday (except Block 0 and 13).
Academic Year: July 1 - June 30
Block 0: July 1 → First Thursday - 1 day
Variable length (1-6 days)
Purpose: Orientation, onboarding, calendar fudge factor
Blocks 1-12: Thursday → Wednesday
Fixed 28 days each (56 half-day assignments per person)
Block 13: Thursday → June 30
Variable length (28+ days)
Purpose: Absorb end-of-year remainder
Example AY25-26:
Block 0 Logic:
Block 13 Logic:
Priority Order: FMIT > AT (clinic) > Admin (GME/DFM)
Key Constraints:
references/excel-structure.md - Complete workbook layoutreferences/faculty-roster.md - Faculty caps and admin typesreferences/residents-rotations.md - Resident rotations and codesreferences/block10-context.md - Block N specific contextThis is the working document for scheduling.
| Row Range | Content |
|---|---|
| 1 | Block number, Day names (THURS, FRI, SAT...) |
| 2 | Day abbreviations (THU, FRI, SAT...) |
| 3 | Date row with actual dates (2025-03-12, etc.) |
| 4 | Staff Call (faculty name when on call) |
| 5 | Resident Call |
| 6 | Headers: TEMPLATE, ROLE, PROVIDER |
| 9-13 | PGY-3 Residents (R3) |
| 14-19 | PGY-2 Residents (R2) |
| 20-25 | PGY-1 Residents (R1) |
| 31-43 | Faculty (C19/FAC, ADJ/FAC, SPEC/PSY) |
| 49-53 | TY/Float/SM |
| 55-65 | Medical Students (USU, IPAP, MS) |
| 67-68 | CP/BHC (Pharmacist, Behavioral Health) |
| 71-82 | Metrics (Appointments, AT Needed, etc.) |
| Column | Content |
|---|---|
| 1 | First-half rotation (e.g., "FMC", "FMIT 2", "Remote_Site") |
| 2 | Second-half rotation if different (e.g., "Peds NF") |
| 3 | Template code (R1, R2, R3, C19, ADJ, etc.) |
| 4 | Role (PGY 1, PGY 2, PGY 3, FAC, etc.) |
| 5 | Provider name |
| 6+ | Half-day slots (AM/PM pairs per day) |
Each date uses 2 columns: AM (even col), PM (odd col+1)
| Columns | Date | Day | Week |
|---|---|---|---|
| 6-7 | [Block Start] | Thu | 1 |
| 8-9 | Mar 13 | Fri | 1 |
| 10-11 | Mar 14 | Sat | 1 (W) |
| 12-13 | Mar 15 | Sun | 1 (W) |
| 14-15 | Mar 16 | Mon | 2 |
| 16-17 | Mar 17 | Tue | 2 |
| 18-19 | Mar 18 | Wed | 2 (LEC col 19) |
| 20-21 | Mar 19 | Thu | 2 |
| 22-23 | Mar 20 | Fri | 2 |
| 24-25 | Mar 21 | Sat | 2 (W) |
| 26-27 | Mar 22 | Sun | 2 (W) |
| 28-29 | Mar 23 | Mon | 3 ← MID-BLOCK TRANSITION |
| 30-31 | Mar 24 | Tue | 3 |
| 32-33 | Mar 25 | Wed | 3 (LEC col 33) |
| 34-35 | Mar 26 | Thu | 3 |
| 36-37 | Mar 27 | Fri | 3 |
| 38-39 | Mar 28 | Sat | 3 (W) |
| 40-41 | Mar 29 | Sun | 3 (W) |
| 42-43 | Mar 30 | Mon | 4 |
| 44-45 | Mar 31 | Tue | 4 |
| 46-47 | Apr 01 | Wed | 4 (LEC col 47) |
| 48-49 | Apr 02 | Thu | 4 |
| 50-51 | Apr 03 | Fri | 4 |
| 52-53 | Apr 04 | Sat | 4 (W) |
| 54-55 | Apr 05 | Sun | 4 (W) |
| 56-57 | Apr 06 | Mon | 5 |
| 58-59 | Apr 07 | Tue | 5 |
| 60-61 | Apr 08 | Wed | 5 (LEC col 61) |
Special Columns:
See "C vs AT Distinction" table above for foundational rules.
Weekly caps apply to C only (personal clinic), NOT to AT (supervision). If staffing is critical, faculty can do AT all day every day.
| Row | Template | Name | Min C/wk | Max C/wk | Admin | Notes |
|---|---|---|---|---|---|---|
| 31 | C19/FAC | [APD] | 0 | 0 | GME | APD, 100% admin |
| 32 | C19/FAC | [Faculty 1] | 2 | 4 | GME | |
| 33 | C19/FAC | [Faculty 2] | 2 | 4 | GME | |
| 34 | C19/FAC | [DFM Admin] | 1 | 1 | DFM | 90% DFM admin |
| 35 | C19/FAC | [Faculty 3] | 0 | 0 | GME | OUT Dec-Jun |
| 36 | C19/FAC | [Faculty 4] | 2 | 4 | GME | |
| 37 | C19/FAC | SM_FACULTY, Chelsea | 0 | 0 | SM/AT | Sports Med faculty, can also do AT |
| 38 | C19/FAC | [Faculty 5] | 2 | 2 | GME | |
| 39 | C19/FAC | [Faculty 6] | 0 | 0 | GME | DEP (deployed) |
| 40 | C19/FAC | [Faculty 7] | 0 | 0 | GME | FMIT weeks |
| 41 | ADJ/FAC | [Adjunct 1] | 0 | 0 | GME | FMIT/Call only |
| 42 | ADJ/FAC | [Adjunct 2] | 0 | 0 | GME | FMIT/Call only |
| 43 | SPEC/PSY | [Spec Faculty] | 2 | 2 | GME |
If MIN cannot be met: WARN - flag for PD review (e.g., conference week, faculty leave)
Target half-days per week (10 total):
| Activity | Half-Days | Notes |
|---|---|---|
| C (Clinic) | 3 | Personal patients, has MIN/MAX caps |
| GME/DFM | 2-3 | Admin time |
| AT | 3-4 | Supervising residents (no cap) |
| PCAT/DO | 1 | If took call that week |
If no call that week: Extra AT slot available
Full-day preference: Faculty prefer full-day C (AM+PM same day) when possible - increases chances of full-day GME and better work-life balance.
| Row | Name | Block N Rotation |
|---|---|---|
| 9 | [R3-1] | Remote_Site (TDY) |
| 10 | [R3-2] | NF (Night Float) |
| 11 | [R3-3] | FMC |
| 12 | [R3-4] | FMIT 2 |
| 13 | [R3-5] | NEURO → NF |
| Row | Name | Block N Rotation |
|---|---|---|
| 14 | [R2-1] | FMIT 2 |
| 15 | [R2-2] | SM (Sports Med) |
| 16 | [R2-3] | POCUS |
| 17 | [R2-4] | L&D Night Float |
| 18 | [R2-5] | Surg Exp |
| 19 | [R2-6] | Gyn Clinic |
| Row | Name | Block N Rotation |
|---|---|---|
| 20 | [R1-1] | FMC |
| 21 | [R1-2] | Peds Ward → Peds NF |
| 22 | [R1-3] | Offsite_Hospital L&D |
| 23 | [R1-4] | Peds NF → Peds Ward |
| 24 | [R1-5] | PROC |
| 25 | [R1-6] | IM |
| Rotation Name | Primary Code | Pattern | Notes |
|---|---|---|---|
| Remote_Site | TDY | TDY all day | Off-site, entire block |
| NF (Night Float) | NF | OFF (AM) / NF (PM) | Works nights, minimal day |
| FMC | C/CV | C (AM) / CV or C (PM) | High clinic load |
| FMIT / FMIT 2 | FMIT | FMIT all day | Inpatient team |
| NEURO | NEURO | NEURO (AM) / C (PM) | Elective + clinic |
| SM (Sports Med) | SM | SM (AM) / C (PM) | Sports Med + clinic |
| POCUS | US | US (AM) / C (PM) | Ultrasound + clinic |
| L&D Night Float | L&D | L&D all day | Labor & Delivery nights |
| Surg Exp | SURG | SURG (AM) / C (PM) | Surgery + clinic |
| Gyn Clinic | GYN | GYN (AM) / C (PM) | Gynecology + clinic |
| Peds Ward | PedW | PedW all day | Pediatrics inpatient |
| Peds NF | PedNF | OFF (AM) / PedNF (PM) | Peds night float |
| Offsite_Hospital L&D | KAP | KAP all day | Off-site L&D |
| PROC | PR | PR (AM) / C (PM) | Procedures + clinic |
| IM | IM | IM all day | Internal Medicine ward |
| Code | Full Name | Usage |
|---|---|---|
| C | Clinic | FM Clinic precepting |
| C30 | Clinic 30-min | 30-min appointments |
| C40 | Clinic 40-min | 40-min appointments (intern) |
| C-I | Clinic-FMIT | FMIT resident clinic day |
| C-N | Night Float Clinic | Thursday PM for oncoming NF |
| CC | Continuity Clinic | Panel patients |
| CV | Virtual Clinic | Telehealth |
| V1, V2 | Virtual 1/2 | Virtual clinic blocks |
| PR | Procedures | Procedure clinic |
| VAS, VasC | Vascular | Vascular procedures |
| ADM | Admin | Administrative time |
| FLX | Flex | Flexible/catch-up time |
| FMIT | FM Inpatient Team | Inpatient rotation |
| NF | Night Float | Night shift |
| OFF | Day Off | Post-call or scheduled off |
| TDY | Temporary Duty | Off-site rotation (Remote_Site, etc.) |
| LV | Leave | Vacation/sick |
| HOL | Holiday | Federal holiday |
| HC | Holiday Call | On-call on holiday |
| W | Weekend | Saturday/Sunday |
| LEC | Lecture | Protected didactics (Wed PM) |
| SIM | Simulation | Sim lab |
| MM | M&M | Morbidity & Mortality conf |
| PI | Process Improvement | QI time |
| EPIC | EPIC Training | EHR training |
| Orient | Orientation | New rotation orientation |
| Coding | Coding | Billing/coding education |
| SM | Sports Medicine | SM rotation |
| aSM | Academic Sports Med | Wednesday AM for sports rotation |
| HLC | Houseless Clinic | Monday PM for R2/R3 |
| CLC | Continuity Learning | Thursday PM, weeks 2 and 4 |
| GYN | Gynecology | GYN clinic |
| NEURO | Neurology | Neuro elective |
| OPTH | Ophthalmology | Ophth elective |
| ENT | ENT | ENT elective |
| URO | Urology | Urology elective |
| PAL | Palliative | Palliative care |
| ENDO | Endocrinology | Endo elective |
| VA | VA Clinic | VA rotation |
| NBN | Newborn Nursery | NBN rotation |
| NICU | NICU | Neonatal ICU |
| KAP | Offsite_Hospital L&D | Off-site L&D (intern) |
| L&D | Labor & Delivery | Program L&D |
| LDNF | L&D Night Float | R2 L&D nights |
| IM | Internal Medicine | IM ward |
| PedW | Peds Ward | Pediatrics inpatient |
| PedNF | Peds Night Float | Peds nights |
| PedSP | Peds Subspecialty | Peds specialty |
| SURG | Surgery | Surgery rotation |
| PARTNER_CLINIC | Partner_Clinic | Partner_Clinic clinic |
Off-island rotation to Remote_Site, remote location.
Pre-departure (Week 1):
During Remote_Site (Weeks 2-4):
Return (Week 4/5):
Key Points:
Off-site rotation at Offsite_Hospital Medical Center.
| Day | AM | PM | Notes |
|---|---|---|---|
| Mon | KAP | OFF | Travel back from Offsite_Hospital |
| Tue | OFF | OFF | Recovery day |
| Wed | C | LEC | Continuity clinic! |
| Thu | KAP | KAP | On-site |
| Fri | KAP | KAP | On-site |
| Sat | KAP | KAP | On-site |
| Sun | KAP | KAP | On-site |
CRITICAL Pattern Summary:
def get_offsite_hospital(day_of_week, is_am, is_last_wed):
if is_last_wed:
return "LEC" if is_am else "ADV"
if day_of_week == 1: # Monday
return "KAP" if is_am else "OFF"
elif day_of_week == 2: # Tuesday
return "OFF" # Both AM and PM
elif day_of_week == 3: # Wednesday
return "C" if is_am else "LEC"
else: # Thu-Sun
return "KAP"
Key Points:
Program Labor & Delivery night shift rotation.
| Day | AM | PM | Notes |
|---|---|---|---|
| Mon | OFF | LDNF | Sleeping days, working nights |
| Tue | OFF | LDNF | |
| Wed | OFF | LDNF | NO Wed AM clinic! |
| Thu | OFF | LDNF | |
| Fri | C | OFF | FRIDAY morning clinic! |
| Sat | W | W | Weekend |
| Sun | W | W | Weekend |
CRITICAL: Friday clinic, NOT Wednesday!
def get_ldnf(day_of_week, is_am, is_last_wed):
if is_last_wed:
return "LEC" if is_am else "ADV"
if day_of_week == 5: # Friday
return "C" if is_am else "OFF" # FRIDAY clinic!
elif day_of_week in (6, 7): # Weekend
return "W"
else: # Mon-Thu
return "OFF" if is_am else "LDNF"
Key Points:
Night Float Schedule Structure:
C-N Code (Night Float Clinic):
Night Float Week Example:
Thu (start): C-N in PM (oncoming clinic)
Fri-Wed: NF (working nights)
Thu (end): OFF (post-call inter-block)
Schedule:
Procedures Orientation (Faculty_4):
MedsTo Orientation (Faculty_5):
Schedule:
Block Example:
Week 1 Thu PM: NOT CLC (too early)
Week 2 Thu PM: CLC ← First session
Week 3 Thu PM: NOT CLC (gap week)
Week 4 Thu PM: CLC ← Second session
Some residents switch rotations mid-block (at column 28 / Mar 23):
# Check column 1 and 2 for rotation info
first_half = sheet.cell(row=row, column=1).value # e.g., "Peds Ward"
second_half = sheet.cell(row=row, column=2).value # e.g., "Peds NF"
MID_BLOCK_COL = 28 # Start of second half
def get_rotation(col, first_rot, second_rot):
if second_rot and col >= MID_BLOCK_COL:
return second_rot
return first_rot
PGY-1 interns have protected Continuity Clinic on Wednesday mornings.
This is a HARD CONSTRAINT - interns must see their panel patients weekly regardless of rotation.
Wednesday AM for PGY-1:
- Most rotations → C (Continuity Clinic)
- FMC Block 1-6 → C60 (60-min appointments, new intern)
- FMC Block 7-13 → C40 (40-min appointments, experienced intern)
- ER → C30 (30-min appointments)
Exceptions (no Wed AM clinic):
- Night Float schedules (PedW night, NF) → PedW or OFF
- Off-site (Remote_Site, Offsite_Hospital) → TDY or KAP
- OB Intern → OB Cl
- Transitional → ADM
Senior residents (PGY-2/3) do NOT have this constraint - their Wed AM depends on rotation:
Clinic Half-Days per Week by PGY Level:
| Level | Clinic Half-Days/Week | Notes |
|---|---|---|
| PGY-1 | 1 (ideally) | Protected for learning |
| PGY-2 | 2-3 | No more than 3 |
| PGY-3 | 3-4 | Most clinic time |
R2 on Sports Med: Maximum 3 clinics (not 4)
Flex Time Requirements:
| Level | FLX Half-Days/Week |
|---|---|
| PGY-1 | 2 half-days |
| PGY-2 | 1 half-day |
| PGY-3 (FMC) | At least 1 half-day |
def fill_rotation(sheet, row, rotation, col):
"""Fill a single half-day slot based on rotation type"""
is_am = (col % 2 == 0) # Even columns are AM
if rotation == 'Remote_Site':
return 'TDY'
elif rotation == 'NF':
return 'OFF' if is_am else 'NF'
elif rotation == 'FMC':
return 'C' if is_am else ('CV' if col % 4 == 1 else 'C')
elif rotation in ['FMIT', 'FMIT 2']:
return 'FMIT'
elif rotation == 'NEURO':
return 'NEURO' if is_am else 'C'
elif rotation == 'SM':
return 'SM' if is_am else 'C'
elif rotation == 'POCUS':
return 'US' if is_am else 'C'
elif rotation == 'L and D night float':
return 'L&D'
elif rotation == 'Surg Exp':
return 'SURG' if is_am else 'C'
elif rotation == 'Gyn Clinic':
return 'GYN' if is_am else 'C'
elif rotation == 'Peds Ward':
return 'PedW'
elif rotation == 'Peds NF':
return 'OFF' if is_am else 'PedNF'
elif rotation == 'Offsite_Hospital L and D':
return 'KAP'
elif rotation == 'PROC':
return 'PR' if is_am else 'C'
elif rotation == 'IM':
return 'IM'
else:
return rotation # Use as-is
from openpyxl import load_workbook
wb = load_workbook('schedule.xlsx')
sheet = wb['Block Template2']
Never overwrite these codes:
| Code | Meaning | Action |
|---|---|---|
| FMIT | FM Inpatient Team | Skip |
| LV | Leave | Skip |
| W | Weekend | Skip |
| PC | Post-call marker | Keep |
| PCAT | Post-Call Admin | Keep |
| DO | Day Off | Keep |
| LEC | Lecture | Skip |
| SIM | Simulation | Skip |
| SAFP | State AFP conf | Skip |
| BLS | BLS training | Skip |
| USAFP | USAFP conference | Skip |
| DEP | Deployed | Skip |
| aSM | Sports Med assist | Skip |
| PI | Process Improvement | Skip |
| MM? / MM | M&M conference | Skip |
Rule: Don't give faculty call on consecutive days - need gap days between call assignments.
BAD: Faculty on call Mar 15 AND Mar 17 (only 1 day gap)
GOOD: Faculty on call Mar 15 AND Mar 19 (3 day gap)
Faculty with "W" (weekend) in their schedule row should NOT be assigned call that day.
FMIT Week Structure:
During FMIT week, faculty covers:
FMIT faculty CANNOT be placed on call:
Example: Faculty on FMIT Fri Mar 13 - Thu Mar 19:
- Mar 13 (Fri) ← FMIT starts, covers Fri call
- Mar 14 (Sat) ← FMIT covers Sat call
- Mar 15-19 (Sun-Thu) ← CANNOT be on call (on FMIT service)
- Mar 20 (Fri) ← PC (day off after FMIT)
- Mar 21 (Sat) ← CANNOT be placed on call (recovery)
FMIT week = Friday through Thursday
FMIT faculty covers:
- Friday night call (FMIT start)
- Saturday night call (FMIT weekend)
- CANNOT be on call Sun-Thu (on service)
- PC Friday after (day off)
- CANNOT be on call Sat after (recovery)
Only applies to non-FMIT faculty:
If faculty on call Night N AND NOT on FMIT:
Day N+1 AM = PCAT (Post-Call Admin Time)
Day N+1 PM = DO (Day Off)
FMIT faculty do NOT get PCAT/DO - they continue FMIT coverage.
After completing FMIT week, faculty gets Friday off:
FMIT ends Thursday -> Friday = PC (both AM and PM)
This Friday CANNOT have this faculty on call.
AUTO-ASSIGN Pool (Mon-Thu, Sun):
MANUAL-ONLY Pool (assigned by hand, typically Sundays):
SPECIAL RULES Faculty:
| Faculty | Rule |
|---|---|
| FACULTY_7 | Week-on/week-off FMIT: No Sun-Thu call during "off" weeks; available after FMIT but not immediately |
| APD | Available for call starting next week after post-FMIT (not same week) |
| FACULTY_2 | Only weeks 1-2 before FMIT week; no call in week immediately preceding FMIT |
Sundays are AUTO-ASSIGNABLE but tracked separately for equity. No single faculty member should be assigned every Sunday in a block.
Sunday equity tracking:
- Maintain separate sunday_counts vs weekday_counts
- When assigning Sunday, sort by sunday_counts (lowest first)
- Distribute evenly: aim for max 1 Sunday per faculty per block
- W (weekend) in faculty row does NOT block call assignment
Important: The "W" code in a faculty's schedule row indicates weekend/off, but call assignments (row 4) are separate. Faculty can be ON CALL on a day they have "W" in their schedule.
SM_FACULTY is the Sports Medicine (SM) faculty. She does NOT do regular clinic (C).
CRITICAL: SM_FACULTY does SM REGARDLESS of whether residents are on SM rotation.
Key Rules:
SM_FACULTY Schedule Codes:
Workflow:
This is a HARD CONSTRAINT - minimum supervision ratios required by ACGME.
Resident AT Demand:
| PGY Level | AT Demand |
|---|---|
| PGY-1 (Intern) | 0.5 AT each |
| PGY-2 | 0.25 AT each |
| PGY-3 | 0.25 AT each |
Non-Clinic Activities = +1 AT each:
Calculation:
Total AT Needed = (PGY-1 count × 0.5) + (PGY-2 count × 0.25) + (PGY-3 count × 0.25) + (PROC/VAS count × 1.0)
ALWAYS ROUND UP - cannot have half a faculty member
Example:
2 PGY-1 in clinic = 1.0 AT
2 PGY-2 in clinic = 0.5 AT
1 PROC resident = 1.0 AT
--------------------------
Total = 2.5 AT → Round up to 3 AT minimum
Coverage codes that count as AT:
Verification: Check rows 91-92 in Block Template2:
Row 92 must be >= Row 91 for EVERY half-day slot.
AT compliance is a two-way equation:
AT Coverage >= AT Demand
If coverage is short, you can EITHER:
Reducing demand options:
Priority: AT compliance > weekly clinic caps > GME preferences
Faculty caps are preferences, not hard constraints. ACGME supervision IS a hard constraint. However, before going over caps, first check if reducing resident demand is feasible.
Example - Mar 13 PM with most faculty at USAFP:
Rotation templates (column 1-2) are GUIDELINES, not strict requirements. The actual schedule may vary based on:
Check "FMIT Attending (2025-2026)" sheet for weekly assignments:
Example Block N:
| Week | Dates | FMIT Attending |
|---|---|---|
| 1 | Mar 13-19 | Faculty_7 |
| 2 | Mar 20-26 | APD |
| 3 | Mar 27-Apr 2 | Faculty_7 |
| 4 | Apr 3-9 | Faculty_2 |
For each weekday half-day:
For remaining empty faculty cells:
For each resident:
Final Wednesday of block (Week 5) has special rules:
All Residents:
⚠️ Common Error: Scheduling morning clinic on last Wednesday - this is WRONG.
Last Wednesday Example ([Block End]):
AM: LEC (Lecture) - protected
PM: ADV (Advising)
Special Rule for IM rotation:
IM Resident Last Week Example:
Tue PM: C (moved from Wed)
Wed AM: LEC
Wed PM: ADV
from openpyxl import load_workbook
# Faculty configuration (min_c = MIN clinic/wk, max_c = MAX clinic/wk)
# Weekly caps apply to C only, NOT AT (AT is unlimited)
FACULTY = {
'APD': {'row': 31, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Faculty_1': {'row': 32, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
'Faculty_2': {'row': 33, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
'DFM_Admin': {'row': 34, 'min_c': 1, 'max_c': 1, 'admin': 'DFM'},
'Faculty_3': {'row': 35, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Faculty_4': {'row': 36, 'min_c': 2, 'max_c': 4, 'admin': 'GME'},
'SM_FACULTY': {'row': 37, 'min_c': 0, 'max_c': 0, 'admin': 'GME'}, # SM only, no personal C
'Faculty_5': {'row': 38, 'min_c': 2, 'max_c': 2, 'admin': 'GME'},
'Faculty_6': {'row': 39, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Faculty_7': {'row': 40, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Adjunct_1': {'row': 41, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Adjunct_2': {'row': 42, 'min_c': 0, 'max_c': 0, 'admin': 'GME'},
'Spec_Faculty': {'row': 43, 'min_c': 2, 'max_c': 2, 'admin': 'GME'},
}
# Resident configuration
RESIDENTS = {
9: {'name': 'R3_1', 'pgy': 3},
10: {'name': 'R3_2', 'pgy': 3},
11: {'name': 'R3_3', 'pgy': 3},
12: {'name': 'R3_4', 'pgy': 3},
13: {'name': 'You', 'pgy': 3},
14: {'name': 'R2_1', 'pgy': 2},
15: {'name': 'R2_2', 'pgy': 2},
16: {'name': 'R2_3', 'pgy': 2},
17: {'name': 'R2_4', 'pgy': 2},
18: {'name': 'R2_5', 'pgy': 2},
19: {'name': 'R2_6', 'pgy': 2},
20: {'name': 'R1_1', 'pgy': 1},
21: {'name': 'R1_2', 'pgy': 1},
22: {'name': 'R1_3', 'pgy': 1},
23: {'name': 'R1_4', 'pgy': 1},
24: {'name': 'R1_5', 'pgy': 1},
25: {'name': 'R1_6', 'pgy': 1},
}
# Rotation to code mapping (AM, PM)
# NOTE: These are DEFAULT patterns - special days override (Wed AM intern continuity, etc.)
ROTATION_CODES = {
'Remote_Site': ('TDY', 'TDY'), # Off-island, see Remote_Site TDY Schedule for details
'NF': ('OFF', 'NF'), # Night Float - starts Thu, ends Wed
'FMC': ('C', 'C'), # Family Medicine Clinic
'FMIT': ('FMIT', 'FMIT'), # FM Inpatient Team
'FMIT 2': ('FMIT', 'FMIT'), # FM Inpatient Team (2nd team)
'NEURO': ('NEURO', 'C'), # Neurology elective + clinic
'SM': ('SM', 'C'), # Sports Medicine + clinic
'POCUS': ('US', 'C'), # Point-of-care ultrasound + clinic
'L and D night float': ('OFF', 'LDNF'), # R2 L&D nights - Fri AM clinic!
'Surg Exp': ('SURG', 'C'), # Surgery experience + clinic
'Gyn Clinic': ('GYN', 'C'), # Gynecology + clinic
'Peds Ward': ('PedW', 'PedW'), # Pediatrics inpatient
'Peds NF': ('OFF', 'PedNF'), # Peds Night Float
'Offsite_Hospital L and D': ('KAP', 'KAP'), # Off-site L&D - see KAP schedule
'PROC': ('PR', 'C'), # Procedures + clinic
'IM': ('IM', 'IM'), # Internal Medicine ward
'VA': ('VA', 'VA'), # VA clinic rotation
'ENDO': ('ENDO', 'C'), # Endocrinology elective
'Derm': ('DERM', 'C'), # Dermatology elective
'MSK': ('MSK', 'C'), # Musculoskeletal elective
}
# Special columns
WEEKEND_COLS = {10, 11, 12, 13, 24, 25, 26, 27, 38, 39, 40, 41, 52, 53, 54, 55}
LEC_COLS = {19, 33, 47, 61}
MID_BLOCK_COL = 28
# Pre-blocked codes (never overwrite)
BLOCKED_CODES = {'FMIT', 'LV', 'W', 'PC', 'LEC', 'SIM', 'SAFP', 'BLS',
'PCAT', 'DO', 'USAFP', 'DEP', 'aSM', 'PI', 'MM?', 'MM',
'HOL', 'HC', 'TDY'}
def fill_resident_schedule(sheet, row):
"""Fill a single resident's schedule based on their rotation"""
rot1 = sheet.cell(row=row, column=1).value
rot2 = sheet.cell(row=row, column=2).value
for col in range(6, 62):
current = sheet.cell(row=row, column=col).value
if current and str(current).strip().upper() in BLOCKED_CODES:
continue
if col in WEEKEND_COLS:
sheet.cell(row=row, column=col).value = 'W'
continue
if col in LEC_COLS:
sheet.cell(row=row, column=col).value = 'LEC'
continue
# Get rotation (handle mid-block switch)
rotation = rot2 if (rot2 and col >= MID_BLOCK_COL) else rot1
# Get AM/PM codes
if rotation in ROTATION_CODES:
am_code, pm_code = ROTATION_CODES[rotation]
is_am = (col % 2 == 0)
sheet.cell(row=row, column=col).value = am_code if is_am else pm_code
else:
sheet.cell(row=row, column=col).value = rotation
This is the typical workflow when scheduling by hand. The automated system should follow a similar approach:
See "C vs AT Distinction" table for authoritative rules.
Max 6 people doing clinical work (C) per half-day slot. AT does NOT count toward this limit.
Why this matters: Staffing constraints limit how many patients can be seen. More than 6 physicians overwhelms support staff.
After completing assignments:
"Not enough coverage": Check if faculty are on leave (LV), USAFP, or DEP.
"Weekly cap exceeded": Recalculate weekly totals. Caps are PER WEEK, not total.
"Post-call conflict": If faculty on call Sunday, Monday AM/PM both blocked.
"Wrong rotation code": Check column 1 for rotation name, verify mapping exists.
"Mid-block not switching": Ensure column 2 has second rotation and col >= 28.
Ground truth file: docs/scheduling/Block10_ROSETTA_CORRECT.xlsx
This file contains the CORRECT Block N schedule with all patterns applied correctly. Use it for TDD:
Test files:
backend/tests/scheduling/test_expansion_vs_rosetta.py - 24 parameterized testsbackend/app/utils/rosetta_parser.py - Parses ROSETTA xlsxRun tests:
cd backend
pytest tests/scheduling/test_expansion_vs_rosetta.py -v
Key patterns verified by ROSETTA:
| Resident | Rotation | Key Pattern |
|---|---|---|
| [R1-3] | KAP | Mon PM=OFF, Tue=OFF/OFF, Wed AM=C |
| [R2-4] | LDNF | Fri AM=C (not Wed!), Mon-Thu=OFF/LDNF |
| [R1-5] | PROC | Wed AM=C (intern continuity) |
| [R1-6] | IM | Wed AM=C, works weekends |
| You, Jae | NEURO→NF | Mid-block transition at col 28 |
| [R1-2] | PedW→PedNF | Mid-block + intern continuity |
| [R1-4] | PedNF→PedW | Reverse mid-block |
Priority rules (highest first):
| Edge Case | Resolution |
|---|---|
| Call before FMIT | Min 3-day buffer; prefer no call week before FMIT |
| PCAT/DO inter-block | Carries over via actual dates (Wednesday call → Thursday PCAT in next block) |
| Call on last day of block | PCAT/DO applies to first day of next block automatically |
| Edge Case | Resolution |
|---|---|
| FMIT spanning blocks | Date-based, not block-tied; solver handles naturally |
| Post-FMIT PC in next block | Actual dates handle this (e.g., FMIT ends Thu → PC Fri may be in next block) |
| FMIT + Wednesday call prohibition | Cannot take Sun-Thu call during FMIT week |
| Edge Case | Resolution |
|---|---|
| Multiple SM residents same slot | Max 2 residents; SM_FACULTY supervises both; must match half-day |
| SM_FACULTY post-call + SM scheduled | Medium constraint - avoid if possible, not a hard fail |
| SM_FACULTY on FMIT + SM residents | No SM that week; convert resident SM → C |
| PGY Level | C-I Day | Notes |
|---|---|---|
| PGY-1 | Wednesday AM | Hard constraint - intern continuity preserved |
| PGY-2 | Tuesday PM | FMIT resident clinic day |
| PGY-3 | Monday PM | FMIT resident clinic day |
C-I is PRELOADED, not solved. These slots are locked before solver runs.
| Scenario | Handling |
|---|---|
| NF post-call inter-block | NF ends Wednesday → post-call Thursday = next block start |
| PCAT/DO from Wed call | Thursday AM/PM may be in next block |
| Rotation ending mid-week | Date arithmetic handles naturally |
| Constraint | Limit |
|---|---|
| Clinical work per half-day | Max 6 people (residents + faculty in C/CV/PR/VAS) |
| AT supervision | Does NOT count toward physical limit |
| Scenario | Resolution |
|---|---|
| CLC on holiday | Skip; does NOT reschedule to another day |
| HLC on holiday | Skip; does NOT reschedule |
| Intern continuity on holiday | Skip; continuity does NOT reschedule |
Resident call is Chief-assigned and follows different rules:
Note: Resident call is tracked in resident_call_preloads table, separate from faculty call.
docs/architecture/HALF_DAY_ASSIGNMENT_MODEL.md - Data model specification.claude/Scratchpad/session-104-half-day-model.md - Design session notesreferences/faculty-roster.md - Faculty caps and constraintsreferences/residents-rotations.md - Resident rotation patternsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience