diff --git a/backend/app/models/course.py b/backend/app/models/course.py index 984f25b..32587cb 100644 --- a/backend/app/models/course.py +++ b/backend/app/models/course.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import TYPE_CHECKING -from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String +from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func @@ -16,6 +16,7 @@ class Course(Base): """Represents a course offered by the college.""" __tablename__ = "course" + __table_args__ = (UniqueConstraint("subject", "code", name="uq_course_subject_code"),) course_id: Mapped[int] = mapped_column(Integer, primary_key=True) subject: Mapped[str] = mapped_column(String(10)) diff --git a/backend/app/routers/course.py b/backend/app/routers/course.py index 8d00c42..0133e61 100644 --- a/backend/app/routers/course.py +++ b/backend/app/routers/course.py @@ -1,6 +1,7 @@ """Course router.""" from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from app.core.database import get_db @@ -45,6 +46,9 @@ def create_course(course: CourseCreate, db: Session = Depends(get_db)): return course_service.create_course(db, course) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) from e + except IntegrityError as e: + db.rollback() + raise HTTPException(status_code=400, detail="A course with this subject and code already exists") from e @router.patch("/{course_id}", response_model=CourseResponse) diff --git a/backend/app/routers/upload.py b/backend/app/routers/upload.py index f941520..61a7ef2 100644 --- a/backend/app/routers/upload.py +++ b/backend/app/routers/upload.py @@ -90,7 +90,7 @@ def upload_faculty_preferences(file: UploadFile = File(...), db: Session = Depen records_successful=len(to_insert) + len(to_update), records_failed=len(skipped), available_faculty=result.get("available_faculty"), - errors=[f"Skipped {len(skipped)} unrecognized courses: {', '.join(skipped)}"] if skipped else None, + errors=[f"Skipped {len(skipped)} unrecognized courses: {', '.join(skipped[:5])}{'...' if len(skipped) > 5 else ''}"] if skipped else None, ) diff --git a/backend/app/schemas/section.py b/backend/app/schemas/section.py index 5dcb5c8..5731838 100644 --- a/backend/app/schemas/section.py +++ b/backend/app/schemas/section.py @@ -38,6 +38,8 @@ class SectionUpdate(BaseModel): class CourseInfo(BaseModel): course_id: int + subject: str + code: int name: str description: str credits: int diff --git a/backend/app/services/section.py b/backend/app/services/section.py index 8f49a4f..edea5e3 100644 --- a/backend/app/services/section.py +++ b/backend/app/services/section.py @@ -70,6 +70,8 @@ def get_rich_sections(db: Session, schedule_id: int) -> list[SectionRichResponse crosslisted_section_id=s.crosslisted_section_id, course=CourseInfo( course_id=s.course.course_id, + subject=s.course.subject, + code=s.course.code, name=s.course.name, description=s.course.description, credits=s.course.credits, diff --git a/backend/tests/test_schedule.py b/backend/tests/test_schedule.py index a27598b..56af2c4 100644 --- a/backend/tests/test_schedule.py +++ b/backend/tests/test_schedule.py @@ -316,7 +316,7 @@ def test_create_schedule_new_courses_field_is_ignored(client, db_session): campus = _make_campus(db_session) semester = _make_semester(db_session, season="Fall", year=2024) _make_historical_context(db_session, campus, season="Fall", current_year=2024) - new_course = _make_course(db_session, name="CS 3800", description="Theory of Computation", credits=4) + new_course = _make_course(db_session, code=3800, name="CS 3800", description="Theory of Computation", credits=4) db_session.commit() response = client.post( "/schedules", diff --git a/backend/tests/test_section_lock.py b/backend/tests/test_section_lock.py index 8e36028..4526ac4 100644 --- a/backend/tests/test_section_lock.py +++ b/backend/tests/test_section_lock.py @@ -43,11 +43,11 @@ def _make_user(db, nuid=1, role="ADMIN"): return user -def _make_section(db, season="Fall"): +def _make_section(db, season="Fall", course_code=2500): campus = _make_campus(db) semester = _make_semester(db, season=season) schedule = Schedule(name="F24", semester_id=semester.semester_id, campus=campus.campus_id) - course = Course(subject="CS", code=2500, name="CS 2500", description="Fundamentals", credits=4) + course = Course(subject="CS", code=course_code, name=f"CS {course_code}", description="Fundamentals", credits=4) db.add_all([schedule, course]) db.flush() time_block = TimeBlock( @@ -121,7 +121,7 @@ def test_lock_different_user(client: TestClient, db_session: Session) -> None: def test_previous_lock_releases(client: TestClient, db_session: Session) -> None: user = _make_user(db_session) section1 = _make_section(db_session) - section2 = _make_section(db_session, season="Spring") + section2 = _make_section(db_session, season="Spring", course_code=3500) app.dependency_overrides[get_db_user] = lambda: user client.post(f"/sections/{section1.section_id}/lock") diff --git a/backend/tests/test_section_service.py b/backend/tests/test_section_service.py index 5e1cd6f..7002690 100644 --- a/backend/tests/test_section_service.py +++ b/backend/tests/test_section_service.py @@ -261,8 +261,8 @@ def test_error_check_unpreferenced_course_warning(db_session): campus = _make_campus(db_session) semester = _make_semester(db_session) schedule = _make_schedule(db_session, campus, semester) - course_a = _make_course(db_session, name="CS 2500") - course_b = _make_course(db_session, name="CS 3500") + course_a = _make_course(db_session, name="CS 2500", code=2500) + course_b = _make_course(db_session, name="CS 3500", code=3500) tb = _make_time_block(db_session, campus) faculty = _make_faculty(db_session, campus, nuid=1001, email="f1@test.edu") section = _make_section(db_session, schedule, course_a, tb) @@ -283,8 +283,8 @@ def test_error_check_faculty_has_course_preference_no_warning(db_session): campus = _make_campus(db_session) semester = _make_semester(db_session) schedule = _make_schedule(db_session, campus, semester) - course_a = _make_course(db_session, name="CS 2500") - course_b = _make_course(db_session, name="CS 3500") + course_a = _make_course(db_session, name="CS 2500", code=2500) + course_b = _make_course(db_session, name="CS 3500", code=3500) tb = _make_time_block(db_session, campus) faculty = _make_faculty(db_session, campus, nuid=1001, email="f1@test.edu") section = _make_section(db_session, schedule, course_a, tb) @@ -311,8 +311,8 @@ def test_error_check_not_interested_course_preference_counts_as_warning(db_sessi campus = _make_campus(db_session) semester = _make_semester(db_session) schedule = _make_schedule(db_session, campus, semester) - course_a = _make_course(db_session, name="CS 2500") - course_b = _make_course(db_session, name="CS 3500") + course_a = _make_course(db_session, name="CS 2500", code=2500) + course_b = _make_course(db_session, name="CS 3500", code=3500) tb = _make_time_block(db_session, campus) faculty = _make_faculty(db_session, campus, nuid=1001, email="f1@test.edu") section = _make_section(db_session, schedule, course_a, tb) @@ -540,8 +540,8 @@ def test_update_section_returns_unpreferenced_course_warning(db_session): campus = _make_campus(db_session) semester = _make_semester(db_session) schedule = _make_schedule(db_session, campus, semester) - course_a = _make_course(db_session, name="CS 2500") - course_b = _make_course(db_session, name="CS 3500") + course_a = _make_course(db_session, name="CS 2500", code=2500) + course_b = _make_course(db_session, name="CS 3500", code=3500) tb = _make_time_block(db_session, campus) faculty = _make_faculty(db_session, campus, nuid=1001, email="f1@test.edu") section = _make_section(db_session, schedule, course_a, tb) @@ -559,8 +559,8 @@ def test_update_section_no_warnings_when_faculty_prefers_new_course(db_session): campus = _make_campus(db_session) semester = _make_semester(db_session) schedule = _make_schedule(db_session, campus, semester) - course_a = _make_course(db_session, name="CS 2500") - course_b = _make_course(db_session, name="CS 3500") + course_a = _make_course(db_session, name="CS 2500", code=2500) + course_b = _make_course(db_session, name="CS 3500", code=3500) tb = _make_time_block(db_session, campus) faculty = _make_faculty(db_session, campus, nuid=1001, email="f1@test.edu") section = _make_section(db_session, schedule, course_a, tb) diff --git a/frontend/openapi.json b/frontend/openapi.json index 650ba5b..3c23173 100644 --- a/frontend/openapi.json +++ b/frontend/openapi.json @@ -1 +1 @@ -{"openapi":"3.1.0","info":{"title":"Automated Course Scheduler API","description":"API for the Automated Course Scheduler system","version":"1.0.0"},"paths":{"/sections":{"post":{"tags":["sections"],"summary":"Create Section","description":"Create a new section in a schedule.","operationId":"create_section_sections_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}":{"patch":{"tags":["sections"],"summary":"Update Section","description":"Acquire (or refresh) the lock on this section, then apply the update.\n\nFails with 423 if another user currently holds the lock.\nBroadcasts section_updated to connected clients on success.","operationId":"update_section_sections__section_id__patch","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["sections"],"summary":"Delete Section","description":"Delete a section from a schedule.","operationId":"delete_section_sections__section_id__delete","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules":{"get":{"tags":["schedules"],"summary":"Get Schedules","operationId":"get_schedules_schedules_get","parameters":[{"name":"campus_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus Id"}},{"name":"semester_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Semester Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleResponse"},"title":"Response Get Schedules Schedules Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["schedules"],"summary":"Create Schedule","description":"Create a new schedule draft.","operationId":"create_schedule_schedules_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}":{"get":{"tags":["schedules"],"summary":"Get Schedule","description":"Retrieve a specific schedule.","operationId":"get_schedule_schedules__schedule_id__get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["schedules"],"summary":"Update Schedule","description":"Update schedule metadata (name, complete status, etc.).","operationId":"update_schedule_schedules__schedule_id__put","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["schedules"],"summary":"Delete Schedule","description":"Delete a schedule and all its sections.","operationId":"delete_schedule_schedules__schedule_id__delete","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/sections":{"get":{"tags":["schedules"],"summary":"Get Schedule Sections","description":"Get all sections for a specific schedule.","operationId":"get_schedule_sections_schedules__schedule_id__sections_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SectionResponse"},"title":"Response Get Schedule Sections Schedules Schedule Id Sections Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/sections/rich":{"get":{"tags":["schedules"],"summary":"Get Schedule Sections Rich","description":"Get all sections with denormalized course, time block, and instructor data.","operationId":"get_schedule_sections_rich_schedules__schedule_id__sections_rich_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SectionRichResponse"},"title":"Response Get Schedule Sections Rich Schedules Schedule Id Sections Rich Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/export/csv":{"get":{"tags":["schedules"],"summary":"Export Schedule Csv","description":"Export a finalized schedule as a downloadable CSV.","operationId":"export_schedule_csv_schedules__schedule_id__export_csv_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/locks":{"get":{"tags":["schedules"],"summary":"Get Schedule Locks","description":"Get all active locks for a schedule.\n\nArgs:\n schedule_id: ID of the schedule to query locks for.\n db: Database session.\n\nReturns:\n List of active locks with user display name included.","operationId":"get_schedule_locks_schedules__schedule_id__locks_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleActiveLockResponse"},"title":"Response Get Schedule Locks Schedules Schedule Id Locks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/semesters":{"get":{"tags":["semesters"],"summary":"Get All Semesters","description":"Get all semesters, with optional filtering by campus, semester name, or year.","operationId":"get_all_semesters_semesters_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SemesterResponse"},"type":"array","title":"Response Get All Semesters Semesters Get"}}}}}},"post":{"tags":["semesters"],"summary":"Create Semester","description":"Create a new semester.","operationId":"create_semester_semesters_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterCreate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/semesters/{semester_id}":{"get":{"tags":["semesters"],"summary":"Get Semester","description":"Retrieve a specific semester.","operationId":"get_semester_semesters__semester_id__get","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["semesters"],"summary":"Update Semester","description":"Update semester metadata (name, complete status, etc.).","operationId":"update_semester_semesters__semester_id__put","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["semesters"],"summary":"Delete Semester","description":"Delete a semester and all its sections.","operationId":"delete_semester_semesters__semester_id__delete","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses":{"get":{"tags":["courses"],"summary":"Get Courses","description":"Retrieve all courses, optionally filtered by schedule","operationId":"get_courses_courses_get","parameters":[{"name":"schedule_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter to courses with sections in this schedule","title":"Schedule Id"},"description":"Filter to courses with sections in this schedule"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CourseResponse"},"title":"Response Get Courses Courses Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["courses"],"summary":"Create Course","description":"Create a new course.","operationId":"create_course_courses_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses/{course_id}":{"get":{"tags":["courses"],"summary":"Get Course","description":"Retrieve a course by ID with section count.","operationId":"get_course_courses__course_id__get","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}},{"name":"schedule_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Section count for this schedule only","title":"Schedule Id"},"description":"Section count for this schedule only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["courses"],"summary":"Update Course","description":"Partially update a course.","operationId":"update_course_courses__course_id__patch","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["courses"],"summary":"Delete Course","description":"Delete a course with no sections.","operationId":"delete_course_courses__course_id__delete","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty":{"get":{"tags":["faculty"],"summary":"Get Faculty","description":"Retrieve all faculty members.","operationId":"get_faculty_faculty_get","parameters":[{"name":"campus","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by campus name","title":"Campus"},"description":"Filter by campus name"},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Filter to only active faculty","default":false,"title":"Active Only"},"description":"Filter to only active faculty"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FacultyResponse"},"title":"Response Get Faculty Faculty Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["faculty"],"summary":"Create Faculty","description":"Create a new faculty member.","operationId":"create_faculty_faculty_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty/{nuid}":{"get":{"tags":["faculty"],"summary":"Get Faculty Profile","description":"Retrieve faculty profile with course and time preferences.","operationId":"get_faculty_profile_faculty__nuid__get","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["faculty"],"summary":"Update Faculty","description":"Partially update faculty demographics and status.","operationId":"update_faculty_faculty__nuid__patch","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["faculty"],"summary":"Delete Faculty","description":"Delete a faculty member and their preferences and assignments.","operationId":"delete_faculty_faculty__nuid__delete","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty/build_profiles":{"post":{"tags":["faculty"],"summary":"Build Profiles","operationId":"build_profiles_faculty_build_profiles_post","requestBody":{"content":{"application/json":{"schema":{"items":{"type":"integer"},"type":"array","title":"Available Faculty"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FacultyProfileResponse"},"type":"array","title":"Response Build Profiles Faculty Build Profiles Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/time-blocks":{"get":{"tags":["time-blocks"],"summary":"Get Time Blocks","description":"Retrieve all time blocks, optionally filtered by campus.","operationId":"get_time_blocks_time_blocks_get","parameters":[{"name":"campus_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by campus ID","title":"Campus Id"},"description":"Filter by campus ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimeBlockResponse"},"title":"Response Get Time Blocks Time Blocks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["time-blocks"],"summary":"Create Time Block","description":"Create a new time block.\n\n`meeting_days` should be a compact uppercase day string (e.g. \"MWF\", \"TR\").\n`start_time` and `end_time` must be in HH:MM format.\nSet `block_group` to the same 8-character hex string on two rows to mark\nthem as a split block pair — split blocks are excluded from auto-assignment.\nReturns 409 if the block_group already has a complete pair on this campus.","operationId":"create_time_block_time_blocks_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/time-blocks/{time_block_id}":{"patch":{"tags":["time-blocks"],"summary":"Update Time Block","description":"Partially update a time block. Only fields present in the request body are changed.","operationId":"update_time_block_time_blocks__time_block_id__patch","parameters":[{"name":"time_block_id","in":"path","required":true,"schema":{"type":"integer","title":"Time Block Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["time-blocks"],"summary":"Delete Time Block","description":"Delete a time block.\n\nReturns 400 if any sections are currently assigned to this block —\nthose sections must be reassigned or removed first.","operationId":"delete_time_block_time_blocks__time_block_id__delete","parameters":[{"name":"time_block_id","in":"path","required":true,"schema":{"type":"integer","title":"Time Block Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/campuses":{"get":{"tags":["campuses"],"summary":"Get All Campuses","description":"Retrieve all campuses, optionally filtered by name.","operationId":"get_all_campuses_campuses_get","parameters":[{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CampusResponse"},"title":"Response Get All Campuses Campuses Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["campuses"],"summary":"Create Campus","description":"Create a new campus.","operationId":"create_campus_campuses_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/campuses/{campus_id}":{"get":{"tags":["campuses"],"summary":"Get Campus","description":"Retrieve a specific campus by ID.","operationId":"get_campus_campuses__campus_id__get","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["campuses"],"summary":"Update Campus","description":"Update campus metadata (name, etc.).","operationId":"update_campus_campuses__campus_id__put","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["campuses"],"summary":"Delete Campus","description":"Delete a campus.","operationId":"delete_campus_campuses__campus_id__delete","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/courses":{"post":{"tags":["upload"],"summary":"Upload Courses","description":"Upload a CSV file containing course offering data.","operationId":"upload_courses_upload_courses_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_courses_upload_courses_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/faculty-preferences":{"post":{"tags":["upload"],"summary":"Upload Faculty Preferences","description":"Upload a CSV file containing faculty preference data.","operationId":"upload_faculty_preferences_upload_faculty_preferences_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_faculty_preferences_upload_faculty_preferences_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/time-preferences":{"post":{"tags":["upload"],"summary":"Upload Time Preferences","description":"Upload a CSV file containing faculty time preference data.","operationId":"upload_time_preferences_upload_time_preferences_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_time_preferences_upload_time_preferences_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments":{"post":{"tags":["comments"],"summary":"Post Comment","operationId":"post_comment_comments_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentSchema"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{parent_id}":{"post":{"tags":["comments"],"summary":"Post Reply","operationId":"post_reply_comments__parent_id__post","parameters":[{"name":"parent_id","in":"path","required":true,"schema":{"type":"integer","title":"Parent Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentSchema"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{section_id}":{"get":{"tags":["comments"],"summary":"Get Comments","operationId":"get_comments_comments__section_id__get","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CommentResponse"},"title":"Response Get Comments Comments Section Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{comment_id}":{"delete":{"tags":["comments"],"summary":"Delete Comment","operationId":"delete_comment_comments__comment_id__delete","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["comments"],"summary":"Resolve Comment","operationId":"resolve_comment_comments__comment_id__put","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}/lock":{"post":{"tags":["section_locks"],"summary":"Acquire Lock","description":"Acquire a lock on a section for editing.\n\nRaises:\n HTTPException: 403 if the caller does not have the admin role.\n HTTPException: 423 if the section is locked by another user.","operationId":"acquire_lock_sections__section_id__lock_post","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionLockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}/unlock":{"post":{"tags":["section_locks"],"summary":"Release Lock","description":"Release a lock on a section.\n\nRaises:\n HTTPException: 403 if the caller does not own an active lock.","operationId":"release_lock_sections__section_id__unlock_post","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings":{"get":{"tags":["warnings"],"summary":"Get Schedule Warnings","operationId":"get_schedule_warnings_schedules__schedule_id__warnings_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"}},{"name":"include_dismissed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Dismissed"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WarningResponse"},"title":"Response Get Schedule Warnings Schedules Schedule Id Warnings Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["warnings"],"summary":"Create Warning","operationId":"create_warning_schedules__schedule_id__warnings_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Warning"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarningResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}/dismiss":{"patch":{"tags":["warnings"],"summary":"Dismiss Warning","operationId":"dismiss_warning_schedules__schedule_id__warnings__warning_id__dismiss_patch","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}/restore":{"patch":{"tags":["warnings"],"summary":"Restore Warning","operationId":"restore_warning_schedules__schedule_id__warnings__warning_id__restore_patch","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}":{"delete":{"tags":["warnings"],"summary":"Delete Warning","operationId":"delete_warning_schedules__schedule_id__warnings__warning_id__delete","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/generate":{"post":{"tags":["schedules"],"summary":"Run Algorithm","operationId":"run_algorithm_schedules__schedule_id__generate_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateScheduleRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/regenerate":{"post":{"tags":["schedules"],"summary":"Regenerate Algorithm","operationId":"regenerate_algorithm_schedules__schedule_id__regenerate_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateScheduleRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites":{"post":{"tags":["users"],"summary":"Create Invite","description":"Invite a faculty member. Requires admin role.\n\nLooks up the faculty record by NUID, creates a User, registers them in\nAuth0, generates a password-change ticket, and sends an invite email.","operationId":"create_invite_api_invites_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites/admin":{"post":{"tags":["users"],"summary":"Create Admin Invite","description":"Create a pending admin from NUID and name/email (no faculty record). Requires admin.","operationId":"create_admin_invite_api_invites_admin_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminInviteRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites/export":{"get":{"tags":["users"],"summary":"Export Invites","description":"Return invite links for all active faculty without a linked account.\n\nCreates pending User records for any who were not yet invited.\nRequires admin role.","operationId":"export_invites_api_invites_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InviteLinkResponse"},"type":"array","title":"Response Export Invites Api Invites Export Get"}}}}}}},"/api/users":{"get":{"tags":["users"],"summary":"List Users","description":"Return all users. Requires admin role.","operationId":"list_users_api_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserResponse"},"type":"array","title":"Response List Users Api Users Get"}}}}}}},"/api/users/me":{"get":{"tags":["users"],"summary":"Get Me","description":"Return the profile of the currently authenticated user.","operationId":"get_me_api_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}}}}},"/api/users/{user_id}":{"get":{"tags":["users"],"summary":"Get User","description":"Return a single user by ID. Requires admin role.","operationId":"get_user_api_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AdminInviteRequest":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"first_name":{"type":"string","maxLength":100,"minLength":1,"title":"First Name"},"last_name":{"type":"string","maxLength":100,"minLength":1,"title":"Last Name"},"email":{"type":"string","maxLength":100,"minLength":3,"title":"Email"}},"type":"object","required":["nuid","first_name","last_name","email"],"title":"AdminInviteRequest","description":"Pending admin user + Auth0 signup URL (no faculty row; same linking as bootstrap_admin)."},"AlgorithmParameters":{"properties":{"MaxTimeBlockCapacity":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Maxtimeblockcapacity","description":"Max department percentage per time block","default":0.15}},"type":"object","title":"AlgorithmParameters"},"Body_upload_courses_upload_courses_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_courses_upload_courses_post"},"Body_upload_faculty_preferences_upload_faculty_preferences_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_faculty_preferences_upload_faculty_preferences_post"},"Body_upload_time_preferences_upload_time_preferences_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_time_preferences_upload_time_preferences_post"},"CampusCreate":{"properties":{"name":{"type":"string","title":"Name"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["name"],"title":"CampusCreate"},"CampusResponse":{"properties":{"campus_id":{"type":"integer","title":"Campus Id"},"name":{"type":"string","title":"Name"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["campus_id","name","active"],"title":"CampusResponse"},"CampusUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"CampusUpdate"},"CommentResponse":{"properties":{"comment_id":{"type":"integer","title":"Comment Id"},"user_id":{"type":"integer","title":"User Id"},"section_id":{"type":"integer","title":"Section Id"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Id"},"content":{"type":"string","title":"Content"},"resolved":{"type":"boolean","title":"Resolved"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"user":{"$ref":"#/components/schemas/CommentUserInfo"}},"type":"object","required":["comment_id","user_id","section_id","parent_id","content","resolved","active","created_at","user"],"title":"CommentResponse"},"CommentSchema":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"user_id":{"type":"integer","title":"User Id"},"content":{"type":"string","title":"Content"}},"type":"object","required":["section_id","user_id","content"],"title":"CommentSchema"},"CommentUserInfo":{"properties":{"user_id":{"type":"integer","title":"User Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["user_id","first_name","last_name","email"],"title":"CommentUserInfo"},"CourseCreate":{"properties":{"subject":{"type":"string","maxLength":10,"minLength":1,"title":"Subject"},"code":{"type":"integer","exclusiveMinimum":0.0,"title":"Code"},"name":{"type":"string","minLength":1,"title":"Name"},"description":{"type":"string","minLength":1,"title":"Description"},"credits":{"type":"integer","minimum":0.0,"title":"Credits"},"priority":{"type":"boolean","title":"Priority","default":false}},"type":"object","required":["subject","code","name","description","credits"],"title":"CourseCreate"},"CourseInfo":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"credits":{"type":"integer","title":"Credits"}},"type":"object","required":["course_id","name","description","credits"],"title":"CourseInfo"},"CoursePreferenceInfo":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"course_name":{"type":"string","title":"Course Name"},"preference":{"type":"string","title":"Preference"}},"type":"object","required":["course_id","course_name","preference"],"title":"CoursePreferenceInfo"},"CourseResponse":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"subject":{"type":"string","title":"Subject"},"code":{"type":"integer","title":"Code"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"credits":{"type":"integer","title":"Credits"},"priority":{"type":"boolean","title":"Priority","default":false},"section_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Section Count"},"qualified_faculty":{"type":"integer","title":"Qualified Faculty","default":0}},"type":"object","required":["course_id","subject","code","name","credits"],"title":"CourseResponse"},"CourseUpdate":{"properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Code"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits"},"priority":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Priority"}},"type":"object","title":"CourseUpdate"},"FacultyCreate":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"first_name":{"type":"string","minLength":1,"title":"First Name"},"last_name":{"type":"string","minLength":1,"title":"Last Name"},"email":{"type":"string","minLength":1,"title":"Email"},"campus":{"type":"integer","exclusiveMinimum":0.0,"title":"Campus"},"active":{"type":"boolean","title":"Active","default":true},"max_load":{"type":"integer","minimum":1.0,"title":"Max Load","default":3}},"type":"object","required":["nuid","first_name","last_name","email","campus"],"title":"FacultyCreate"},"FacultyProfileResponse":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"campus":{"type":"integer","title":"Campus"},"active":{"type":"boolean","title":"Active"},"maxLoad":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxload"},"needsAdminReview":{"type":"boolean","title":"Needsadminreview","default":false},"course_preferences":{"items":{"$ref":"#/components/schemas/CoursePreferenceInfo"},"type":"array","title":"Course Preferences"},"meeting_preferences":{"items":{"$ref":"#/components/schemas/MeetingPreferenceInfo"},"type":"array","title":"Meeting Preferences"}},"type":"object","required":["nuid","first_name","last_name","email","campus","active","course_preferences","meeting_preferences"],"title":"FacultyProfileResponse"},"FacultyResponse":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"campus":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"maxLoad":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxload"}},"type":"object","required":["nuid"],"title":"FacultyResponse"},"FacultyUpdate":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"campus":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"max_load":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Load"}},"type":"object","title":"FacultyUpdate"},"GenerateScheduleRequest":{"properties":{"parameters":{"$ref":"#/components/schemas/AlgorithmParameters","default":{"MaxTimeBlockCapacity":0.15}}},"type":"object","title":"GenerateScheduleRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InstructorInfo":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"course_preferences":{"items":{"$ref":"#/components/schemas/CoursePreferenceInfo"},"type":"array","title":"Course Preferences"},"meeting_preferences":{"items":{"$ref":"#/components/schemas/MeetingPreferenceInfo"},"type":"array","title":"Meeting Preferences"}},"type":"object","required":["nuid","first_name","last_name","email","course_preferences","meeting_preferences"],"title":"InstructorInfo"},"InviteLinkResponse":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"invite_link":{"type":"string","title":"Invite Link"}},"type":"object","required":["first_name","last_name","email","invite_link"],"title":"InviteLinkResponse"},"InviteRequest":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"role":{"type":"string","title":"Role","default":"VIEWER"}},"type":"object","required":["nuid"],"title":"InviteRequest"},"InviteResponse":{"properties":{"user":{"$ref":"#/components/schemas/UserResponse"},"signup_url":{"type":"string","title":"Signup Url"}},"type":"object","required":["user","signup_url"],"title":"InviteResponse"},"MeetingPreferenceInfo":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"preference":{"type":"string","title":"Preference"}},"type":"object","required":["time_block_id","preference"],"title":"MeetingPreferenceInfo"},"RegenerateScheduleRequest":{"properties":{"parameters":{"$ref":"#/components/schemas/AlgorithmParameters","default":{"MaxTimeBlockCapacity":0.15}}},"type":"object","title":"RegenerateScheduleRequest"},"ScheduleActiveLockResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"locked_by":{"type":"integer","title":"Locked By"},"display_name":{"type":"string","title":"Display Name"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["section_id","locked_by","display_name","expires_at"],"title":"ScheduleActiveLockResponse","description":"Returned for each active lock when polling a schedule's lock state."},"ScheduleCreate":{"properties":{"name":{"type":"string","title":"Name"},"semester_id":{"type":"integer","title":"Semester Id"},"campus":{"type":"integer","title":"Campus"},"new_courses":{"items":{"type":"integer"},"type":"array","title":"New Courses","default":[]}},"type":"object","required":["name","semester_id","campus"],"title":"ScheduleCreate"},"ScheduleResponse":{"properties":{"schedule_id":{"type":"integer","title":"Schedule Id"},"name":{"type":"string","title":"Name"},"semester_id":{"type":"integer","title":"Semester Id"},"draft":{"type":"boolean","title":"Draft"},"campus":{"type":"integer","title":"Campus"},"active":{"type":"boolean","title":"Active"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":"idle"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"course_list":{"items":{"$ref":"#/components/schemas/CourseResponse"},"type":"array","title":"Course List","default":[]}},"type":"object","required":["schedule_id","name","semester_id","draft","campus","active"],"title":"ScheduleResponse"},"ScheduleUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"draft":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Draft"}},"type":"object","title":"ScheduleUpdate"},"SectionCreate":{"properties":{"schedule_id":{"type":"integer","title":"Schedule Id"},"course_id":{"type":"integer","title":"Course Id"},"time_block_id":{"type":"integer","title":"Time Block Id"},"capacity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Capacity"},"faculty_nuids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Faculty Nuids"}},"type":"object","required":["schedule_id","course_id","time_block_id"],"title":"SectionCreate"},"SectionLockResponse":{"properties":{"section_lock_id":{"type":"integer","title":"Section Lock Id"},"section_id":{"type":"integer","title":"Section Id"},"locked_by":{"type":"integer","title":"Locked By"},"locked_at":{"type":"string","format":"date-time","title":"Locked At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["section_lock_id","section_id","locked_by","locked_at","expires_at"],"title":"SectionLockResponse","description":"Returned on successful lock acquisition or refresh."},"SectionResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"schedule_id":{"type":"integer","title":"Schedule Id"},"time_block_id":{"type":"integer","title":"Time Block Id"},"course_id":{"type":"integer","title":"Course Id"},"capacity":{"type":"integer","title":"Capacity"},"section_number":{"type":"integer","title":"Section Number"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"assignment_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Assignment Score"}},"type":"object","required":["section_id","schedule_id","time_block_id","course_id","capacity","section_number"],"title":"SectionResponse"},"SectionRichResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"section_number":{"type":"integer","title":"Section Number"},"capacity":{"type":"integer","title":"Capacity"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"schedule_id":{"type":"integer","title":"Schedule Id"},"comment_count":{"type":"integer","title":"Comment Count","default":0},"crosslisted_section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Crosslisted Section Id"},"course":{"$ref":"#/components/schemas/CourseInfo"},"time_block":{"anyOf":[{"$ref":"#/components/schemas/TimeBlockInfo"},{"type":"null"}]},"instructors":{"items":{"$ref":"#/components/schemas/InstructorInfo"},"type":"array","title":"Instructors"}},"type":"object","required":["section_id","section_number","capacity","schedule_id","course","instructors"],"title":"SectionRichResponse"},"SectionUpdate":{"properties":{"time_block_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Time Block Id"},"course_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Course Id"},"capacity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Capacity"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"crosslisted_section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Crosslisted Section Id"},"faculty_nuids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Faculty Nuids"}},"type":"object","title":"SectionUpdate"},"SemesterCreate":{"properties":{"season":{"type":"string","title":"Season"},"year":{"type":"integer","title":"Year"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["season","year"],"title":"SemesterCreate"},"SemesterResponse":{"properties":{"semester_id":{"type":"integer","title":"Semester Id"},"season":{"type":"string","title":"Season"},"year":{"type":"integer","title":"Year"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["semester_id","season","year","active"],"title":"SemesterResponse"},"SemesterUpdate":{"properties":{"season":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Season"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"SemesterUpdate"},"Severity":{"type":"integer","enum":[1,2,3],"title":"Severity"},"TimeBlockCreate":{"properties":{"meeting_days":{"type":"string","minLength":1,"title":"Meeting Days","description":"Day letters, e.g. 'MWF' or 'TR'"},"start_time":{"type":"string","title":"Start Time","description":"Start time in HH:MM format"},"end_time":{"type":"string","title":"End Time","description":"End time in HH:MM format"},"campus_id":{"type":"integer","title":"Campus Id"},"block_group":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Block Group","description":"8-character hex string linking two rows of a split block pair"}},"type":"object","required":["meeting_days","start_time","end_time","campus_id"],"title":"TimeBlockCreate","description":"Payload for creating a new time block.\n\n`meeting_days` should be a compact string of uppercase day letters,\ne.g. \"MWF\" for Monday/Wednesday/Friday or \"TR\" for Tuesday/Thursday.\n\n`start_time` and `end_time` must be in \"HH:MM\" 24-hour format.\n\n`block_group` is optional. Set it to the same 8-character hex string on\ntwo sibling rows to mark them as a split block (e.g. \"T 9:50–11:30\" and\n\"R 1:30–2:50\" both with the same block_group). Split blocks are excluded\nfrom auto-assignment and must be assigned manually."},"TimeBlockInfo":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"days":{"type":"string","title":"Days"},"start_time":{"type":"string","title":"Start Time"},"end_time":{"type":"string","title":"End Time"}},"type":"object","required":["time_block_id","days","start_time","end_time"],"title":"TimeBlockInfo"},"TimeBlockResponse":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"meeting_days":{"type":"string","title":"Meeting Days"},"start_time":{"type":"string","title":"Start Time"},"end_time":{"type":"string","title":"End Time"},"campus_id":{"type":"integer","title":"Campus Id"},"block_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Block Group"}},"type":"object","required":["time_block_id","meeting_days","start_time","end_time","campus_id"],"title":"TimeBlockResponse","description":"Full representation of a time block returned by the API."},"TimeBlockUpdate":{"properties":{"meeting_days":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Days"},"start_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Time"},"end_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Time"},"campus_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus Id"},"block_group":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Block Group"}},"type":"object","title":"TimeBlockUpdate","description":"Partial update payload for a time block. All fields are optional —\nonly the fields included in the request body will be updated."},"UploadResponse":{"properties":{"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"},"records_processed":{"type":"integer","title":"Records Processed","default":0},"records_successful":{"type":"integer","title":"Records Successful","default":0},"records_failed":{"type":"integer","title":"Records Failed","default":0},"available_faculty":{"items":{"type":"integer"},"type":"array","title":"Available Faculty","default":[]},"errors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Errors"}},"type":"object","required":["status","message"],"title":"UploadResponse"},"UserResponse":{"properties":{"user_id":{"type":"integer","title":"User Id"},"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["user_id","nuid","first_name","last_name","email","role","active"],"title":"UserResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"Warning":{"properties":{"Type":{"anyOf":[{"$ref":"#/components/schemas/WarningType"},{"type":"null"}],"description":"Type of warning"},"SeverityRank":{"$ref":"#/components/schemas/Severity","description":"Severity of this warning"},"Message":{"type":"string","title":"Message","description":"Warning detail for the user"},"FacultyID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Facultyid","description":"Related faculty member"},"CourseID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Courseid","description":"Related course"},"BlockID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blockid","description":"Related time block"}},"type":"object","required":["SeverityRank","Message"],"title":"Warning"},"WarningResponse":{"properties":{"Type":{"anyOf":[{"$ref":"#/components/schemas/WarningType"},{"type":"null"}],"description":"Type of warning"},"SeverityRank":{"$ref":"#/components/schemas/Severity","description":"Severity of this warning"},"Message":{"type":"string","title":"Message","description":"Warning detail for the user"},"FacultyID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Facultyid","description":"Related faculty member"},"CourseID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Courseid","description":"Related course"},"BlockID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blockid","description":"Related time block"},"warning_id":{"type":"integer","title":"Warning Id","description":"Unique warning ID"},"dismissed":{"type":"boolean","title":"Dismissed","description":"Whether this warning was dismissed","default":false},"section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Section Id","description":"Directly linked section (manual-edit warnings only)"}},"type":"object","required":["SeverityRank","Message","warning_id"],"title":"WarningResponse","description":"Warning with persistence fields — returned by the API."},"WarningType":{"type":"string","enum":["Time block surpasses threshold","No valid time block for section-faculty pair","Faculty assigned unpreferenced course","Faculty assigned unpreferenced time","Conflict group courses overlap","Faculty overloaded with assignments","Insufficient faculty supply for section","Faculty double booked in time block"],"title":"WarningType","description":"Each member carries a human-readable message and a default severity.\n\nUsage:\n WarningType.FACULTY_OVERLOAD.value # → \"Faculty overloaded with assignments\"\n WarningType.FACULTY_OVERLOAD.severity # → Severity.HIGH"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"security":[{"BearerAuth":[]}]} \ No newline at end of file +{"openapi":"3.1.0","info":{"title":"Automated Course Scheduler API","description":"API for the Automated Course Scheduler system","version":"1.0.0"},"paths":{"/sections":{"post":{"tags":["sections"],"summary":"Create Section","description":"Create a new section in a schedule.","operationId":"create_section_sections_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}":{"patch":{"tags":["sections"],"summary":"Update Section","description":"Acquire (or refresh) the lock on this section, then apply the update.\n\nFails with 423 if another user currently holds the lock.\nBroadcasts section_updated to connected clients on success.","operationId":"update_section_sections__section_id__patch","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["sections"],"summary":"Delete Section","description":"Delete a section from a schedule.","operationId":"delete_section_sections__section_id__delete","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules":{"get":{"tags":["schedules"],"summary":"Get Schedules","operationId":"get_schedules_schedules_get","parameters":[{"name":"campus_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus Id"}},{"name":"semester_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Semester Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleResponse"},"title":"Response Get Schedules Schedules Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["schedules"],"summary":"Create Schedule","description":"Create a new schedule draft.","operationId":"create_schedule_schedules_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}":{"get":{"tags":["schedules"],"summary":"Get Schedule","description":"Retrieve a specific schedule.","operationId":"get_schedule_schedules__schedule_id__get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["schedules"],"summary":"Update Schedule","description":"Update schedule metadata (name, complete status, etc.).","operationId":"update_schedule_schedules__schedule_id__put","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["schedules"],"summary":"Delete Schedule","description":"Delete a schedule and all its sections.","operationId":"delete_schedule_schedules__schedule_id__delete","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/sections":{"get":{"tags":["schedules"],"summary":"Get Schedule Sections","description":"Get all sections for a specific schedule.","operationId":"get_schedule_sections_schedules__schedule_id__sections_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SectionResponse"},"title":"Response Get Schedule Sections Schedules Schedule Id Sections Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/sections/rich":{"get":{"tags":["schedules"],"summary":"Get Schedule Sections Rich","description":"Get all sections with denormalized course, time block, and instructor data.","operationId":"get_schedule_sections_rich_schedules__schedule_id__sections_rich_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SectionRichResponse"},"title":"Response Get Schedule Sections Rich Schedules Schedule Id Sections Rich Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/export/csv":{"get":{"tags":["schedules"],"summary":"Export Schedule Csv","description":"Export a finalized schedule as a downloadable CSV.","operationId":"export_schedule_csv_schedules__schedule_id__export_csv_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/locks":{"get":{"tags":["schedules"],"summary":"Get Schedule Locks","description":"Get all active locks for a schedule.\n\nArgs:\n schedule_id: ID of the schedule to query locks for.\n db: Database session.\n\nReturns:\n List of active locks with user display name included.","operationId":"get_schedule_locks_schedules__schedule_id__locks_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleActiveLockResponse"},"title":"Response Get Schedule Locks Schedules Schedule Id Locks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/semesters":{"get":{"tags":["semesters"],"summary":"Get All Semesters","description":"Get all semesters, with optional filtering by campus, semester name, or year.","operationId":"get_all_semesters_semesters_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SemesterResponse"},"type":"array","title":"Response Get All Semesters Semesters Get"}}}}}},"post":{"tags":["semesters"],"summary":"Create Semester","description":"Create a new semester.","operationId":"create_semester_semesters_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterCreate"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/semesters/{semester_id}":{"get":{"tags":["semesters"],"summary":"Get Semester","description":"Retrieve a specific semester.","operationId":"get_semester_semesters__semester_id__get","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["semesters"],"summary":"Update Semester","description":"Update semester metadata (name, complete status, etc.).","operationId":"update_semester_semesters__semester_id__put","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SemesterResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["semesters"],"summary":"Delete Semester","description":"Delete a semester and all its sections.","operationId":"delete_semester_semesters__semester_id__delete","parameters":[{"name":"semester_id","in":"path","required":true,"schema":{"type":"integer","title":"Semester Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses":{"get":{"tags":["courses"],"summary":"Get Courses","description":"Retrieve all courses, optionally filtered by schedule","operationId":"get_courses_courses_get","parameters":[{"name":"schedule_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter to courses with sections in this schedule","title":"Schedule Id"},"description":"Filter to courses with sections in this schedule"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CourseResponse"},"title":"Response Get Courses Courses Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["courses"],"summary":"Create Course","description":"Create a new course.","operationId":"create_course_courses_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/courses/{course_id}":{"get":{"tags":["courses"],"summary":"Get Course","description":"Retrieve a course by ID with section count.","operationId":"get_course_courses__course_id__get","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}},{"name":"schedule_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Section count for this schedule only","title":"Schedule Id"},"description":"Section count for this schedule only"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["courses"],"summary":"Update Course","description":"Partially update a course.","operationId":"update_course_courses__course_id__patch","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CourseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["courses"],"summary":"Delete Course","description":"Delete a course with no sections.","operationId":"delete_course_courses__course_id__delete","parameters":[{"name":"course_id","in":"path","required":true,"schema":{"type":"integer","title":"Course Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty":{"get":{"tags":["faculty"],"summary":"Get Faculty","description":"Retrieve all faculty members.","operationId":"get_faculty_faculty_get","parameters":[{"name":"campus","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by campus name","title":"Campus"},"description":"Filter by campus name"},{"name":"active_only","in":"query","required":false,"schema":{"type":"boolean","description":"Filter to only active faculty","default":false,"title":"Active Only"},"description":"Filter to only active faculty"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FacultyResponse"},"title":"Response Get Faculty Faculty Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["faculty"],"summary":"Create Faculty","description":"Create a new faculty member.","operationId":"create_faculty_faculty_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty/{nuid}":{"get":{"tags":["faculty"],"summary":"Get Faculty Profile","description":"Retrieve faculty profile with course and time preferences.","operationId":"get_faculty_profile_faculty__nuid__get","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["faculty"],"summary":"Update Faculty","description":"Partially update faculty demographics and status.","operationId":"update_faculty_faculty__nuid__patch","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacultyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["faculty"],"summary":"Delete Faculty","description":"Delete a faculty member and their preferences and assignments.","operationId":"delete_faculty_faculty__nuid__delete","parameters":[{"name":"nuid","in":"path","required":true,"schema":{"type":"integer","title":"Nuid"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/faculty/build_profiles":{"post":{"tags":["faculty"],"summary":"Build Profiles","operationId":"build_profiles_faculty_build_profiles_post","requestBody":{"content":{"application/json":{"schema":{"items":{"type":"integer"},"type":"array","title":"Available Faculty"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FacultyProfileResponse"},"type":"array","title":"Response Build Profiles Faculty Build Profiles Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/time-blocks":{"get":{"tags":["time-blocks"],"summary":"Get Time Blocks","description":"Retrieve all time blocks, optionally filtered by campus.","operationId":"get_time_blocks_time_blocks_get","parameters":[{"name":"campus_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by campus ID","title":"Campus Id"},"description":"Filter by campus ID"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TimeBlockResponse"},"title":"Response Get Time Blocks Time Blocks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["time-blocks"],"summary":"Create Time Block","description":"Create a new time block.\n\n`meeting_days` should be a compact uppercase day string (e.g. \"MWF\", \"TR\").\n`start_time` and `end_time` must be in HH:MM format.\nSet `block_group` to the same 8-character hex string on two rows to mark\nthem as a split block pair — split blocks are excluded from auto-assignment.\nReturns 409 if the block_group already has a complete pair on this campus.","operationId":"create_time_block_time_blocks_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/time-blocks/{time_block_id}":{"patch":{"tags":["time-blocks"],"summary":"Update Time Block","description":"Partially update a time block. Only fields present in the request body are changed.","operationId":"update_time_block_time_blocks__time_block_id__patch","parameters":[{"name":"time_block_id","in":"path","required":true,"schema":{"type":"integer","title":"Time Block Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeBlockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["time-blocks"],"summary":"Delete Time Block","description":"Delete a time block.\n\nReturns 400 if any sections are currently assigned to this block —\nthose sections must be reassigned or removed first.","operationId":"delete_time_block_time_blocks__time_block_id__delete","parameters":[{"name":"time_block_id","in":"path","required":true,"schema":{"type":"integer","title":"Time Block Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/campuses":{"get":{"tags":["campuses"],"summary":"Get All Campuses","description":"Retrieve all campuses, optionally filtered by name.","operationId":"get_all_campuses_campuses_get","parameters":[{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CampusResponse"},"title":"Response Get All Campuses Campuses Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["campuses"],"summary":"Create Campus","description":"Create a new campus.","operationId":"create_campus_campuses_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/campuses/{campus_id}":{"get":{"tags":["campuses"],"summary":"Get Campus","description":"Retrieve a specific campus by ID.","operationId":"get_campus_campuses__campus_id__get","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["campuses"],"summary":"Update Campus","description":"Update campus metadata (name, etc.).","operationId":"update_campus_campuses__campus_id__put","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["campuses"],"summary":"Delete Campus","description":"Delete a campus.","operationId":"delete_campus_campuses__campus_id__delete","parameters":[{"name":"campus_id","in":"path","required":true,"schema":{"type":"integer","title":"Campus Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/courses":{"post":{"tags":["upload"],"summary":"Upload Courses","description":"Upload a CSV file containing course offering data.","operationId":"upload_courses_upload_courses_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_courses_upload_courses_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/faculty-preferences":{"post":{"tags":["upload"],"summary":"Upload Faculty Preferences","description":"Upload a CSV file containing faculty preference data.","operationId":"upload_faculty_preferences_upload_faculty_preferences_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_faculty_preferences_upload_faculty_preferences_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/upload/time-preferences":{"post":{"tags":["upload"],"summary":"Upload Time Preferences","description":"Upload a CSV file containing faculty time preference data.","operationId":"upload_time_preferences_upload_time_preferences_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_time_preferences_upload_time_preferences_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments":{"post":{"tags":["comments"],"summary":"Post Comment","operationId":"post_comment_comments_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentSchema"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{parent_id}":{"post":{"tags":["comments"],"summary":"Post Reply","operationId":"post_reply_comments__parent_id__post","parameters":[{"name":"parent_id","in":"path","required":true,"schema":{"type":"integer","title":"Parent Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentSchema"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{section_id}":{"get":{"tags":["comments"],"summary":"Get Comments","operationId":"get_comments_comments__section_id__get","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CommentResponse"},"title":"Response Get Comments Comments Section Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/comments/{comment_id}":{"delete":{"tags":["comments"],"summary":"Delete Comment","operationId":"delete_comment_comments__comment_id__delete","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["comments"],"summary":"Resolve Comment","operationId":"resolve_comment_comments__comment_id__put","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}/lock":{"post":{"tags":["section_locks"],"summary":"Acquire Lock","description":"Acquire a lock on a section for editing.\n\nRaises:\n HTTPException: 403 if the caller does not have the admin role.\n HTTPException: 423 if the section is locked by another user.","operationId":"acquire_lock_sections__section_id__lock_post","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SectionLockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sections/{section_id}/unlock":{"post":{"tags":["section_locks"],"summary":"Release Lock","description":"Release a lock on a section.\n\nRaises:\n HTTPException: 403 if the caller does not own an active lock.","operationId":"release_lock_sections__section_id__unlock_post","parameters":[{"name":"section_id","in":"path","required":true,"schema":{"type":"integer","title":"Section Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings":{"get":{"tags":["warnings"],"summary":"Get Schedule Warnings","operationId":"get_schedule_warnings_schedules__schedule_id__warnings_get","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"}},{"name":"include_dismissed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Dismissed"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WarningResponse"},"title":"Response Get Schedule Warnings Schedules Schedule Id Warnings Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["warnings"],"summary":"Create Warning","operationId":"create_warning_schedules__schedule_id__warnings_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Warning"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WarningResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}/dismiss":{"patch":{"tags":["warnings"],"summary":"Dismiss Warning","operationId":"dismiss_warning_schedules__schedule_id__warnings__warning_id__dismiss_patch","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}/restore":{"patch":{"tags":["warnings"],"summary":"Restore Warning","operationId":"restore_warning_schedules__schedule_id__warnings__warning_id__restore_patch","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/warnings/{warning_id}":{"delete":{"tags":["warnings"],"summary":"Delete Warning","operationId":"delete_warning_schedules__schedule_id__warnings__warning_id__delete","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}},{"name":"warning_id","in":"path","required":true,"schema":{"type":"integer","title":"Warning Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/generate":{"post":{"tags":["schedules"],"summary":"Run Algorithm","operationId":"run_algorithm_schedules__schedule_id__generate_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateScheduleRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/schedules/{schedule_id}/regenerate":{"post":{"tags":["schedules"],"summary":"Regenerate Algorithm","operationId":"regenerate_algorithm_schedules__schedule_id__regenerate_post","parameters":[{"name":"schedule_id","in":"path","required":true,"schema":{"type":"integer","title":"Schedule Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateScheduleRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites":{"post":{"tags":["users"],"summary":"Create Invite","description":"Invite a faculty member. Requires admin role.\n\nLooks up the faculty record by NUID, creates a User, registers them in\nAuth0, generates a password-change ticket, and sends an invite email.","operationId":"create_invite_api_invites_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites/admin":{"post":{"tags":["users"],"summary":"Create Admin Invite","description":"Create a pending admin from NUID and name/email (no faculty record). Requires admin.","operationId":"create_admin_invite_api_invites_admin_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminInviteRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/invites/export":{"get":{"tags":["users"],"summary":"Export Invites","description":"Return invite links for all active faculty without a linked account.\n\nCreates pending User records for any who were not yet invited.\nRequires admin role.","operationId":"export_invites_api_invites_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/InviteLinkResponse"},"type":"array","title":"Response Export Invites Api Invites Export Get"}}}}}}},"/api/users":{"get":{"tags":["users"],"summary":"List Users","description":"Return all users. Requires admin role.","operationId":"list_users_api_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserResponse"},"type":"array","title":"Response List Users Api Users Get"}}}}}}},"/api/users/me":{"get":{"tags":["users"],"summary":"Get Me","description":"Return the profile of the currently authenticated user.","operationId":"get_me_api_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}}}}},"/api/users/{user_id}":{"get":{"tags":["users"],"summary":"Get User","description":"Return a single user by ID. Requires admin role.","operationId":"get_user_api_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AdminInviteRequest":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"first_name":{"type":"string","maxLength":100,"minLength":1,"title":"First Name"},"last_name":{"type":"string","maxLength":100,"minLength":1,"title":"Last Name"},"email":{"type":"string","maxLength":100,"minLength":3,"title":"Email"}},"type":"object","required":["nuid","first_name","last_name","email"],"title":"AdminInviteRequest","description":"Pending admin user + Auth0 signup URL (no faculty row; same linking as bootstrap_admin)."},"AlgorithmParameters":{"properties":{"MaxTimeBlockCapacity":{"type":"number","maximum":1.0,"exclusiveMinimum":0.0,"title":"Maxtimeblockcapacity","description":"Max department percentage per time block","default":0.15}},"type":"object","title":"AlgorithmParameters"},"Body_upload_courses_upload_courses_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_courses_upload_courses_post"},"Body_upload_faculty_preferences_upload_faculty_preferences_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_faculty_preferences_upload_faculty_preferences_post"},"Body_upload_time_preferences_upload_time_preferences_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_time_preferences_upload_time_preferences_post"},"CampusCreate":{"properties":{"name":{"type":"string","title":"Name"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["name"],"title":"CampusCreate"},"CampusResponse":{"properties":{"campus_id":{"type":"integer","title":"Campus Id"},"name":{"type":"string","title":"Name"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["campus_id","name","active"],"title":"CampusResponse"},"CampusUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"CampusUpdate"},"CommentResponse":{"properties":{"comment_id":{"type":"integer","title":"Comment Id"},"user_id":{"type":"integer","title":"User Id"},"section_id":{"type":"integer","title":"Section Id"},"parent_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Id"},"content":{"type":"string","title":"Content"},"resolved":{"type":"boolean","title":"Resolved"},"active":{"type":"boolean","title":"Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"user":{"$ref":"#/components/schemas/CommentUserInfo"}},"type":"object","required":["comment_id","user_id","section_id","parent_id","content","resolved","active","created_at","user"],"title":"CommentResponse"},"CommentSchema":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"user_id":{"type":"integer","title":"User Id"},"content":{"type":"string","title":"Content"}},"type":"object","required":["section_id","user_id","content"],"title":"CommentSchema"},"CommentUserInfo":{"properties":{"user_id":{"type":"integer","title":"User Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"}},"type":"object","required":["user_id","first_name","last_name","email"],"title":"CommentUserInfo"},"CourseCreate":{"properties":{"subject":{"type":"string","maxLength":10,"minLength":1,"title":"Subject"},"code":{"type":"integer","exclusiveMinimum":0.0,"title":"Code"},"name":{"type":"string","minLength":1,"title":"Name"},"description":{"type":"string","minLength":1,"title":"Description"},"credits":{"type":"integer","minimum":0.0,"title":"Credits"},"priority":{"type":"boolean","title":"Priority","default":false}},"type":"object","required":["subject","code","name","description","credits"],"title":"CourseCreate"},"CourseInfo":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"subject":{"type":"string","title":"Subject"},"code":{"type":"integer","title":"Code"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"credits":{"type":"integer","title":"Credits"}},"type":"object","required":["course_id","subject","code","name","description","credits"],"title":"CourseInfo"},"CoursePreferenceInfo":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"course_name":{"type":"string","title":"Course Name"},"preference":{"type":"string","title":"Preference"}},"type":"object","required":["course_id","course_name","preference"],"title":"CoursePreferenceInfo"},"CourseResponse":{"properties":{"course_id":{"type":"integer","title":"Course Id"},"subject":{"type":"string","title":"Subject"},"code":{"type":"integer","title":"Code"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"credits":{"type":"integer","title":"Credits"},"priority":{"type":"boolean","title":"Priority","default":false},"section_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Section Count"},"qualified_faculty":{"type":"integer","title":"Qualified Faculty","default":0}},"type":"object","required":["course_id","subject","code","name","credits"],"title":"CourseResponse"},"CourseUpdate":{"properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"code":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Code"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"credits":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Credits"},"priority":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Priority"}},"type":"object","title":"CourseUpdate"},"FacultyCreate":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"first_name":{"type":"string","minLength":1,"title":"First Name"},"last_name":{"type":"string","minLength":1,"title":"Last Name"},"email":{"type":"string","minLength":1,"title":"Email"},"campus":{"type":"integer","exclusiveMinimum":0.0,"title":"Campus"},"active":{"type":"boolean","title":"Active","default":true},"max_load":{"type":"integer","minimum":1.0,"title":"Max Load","default":3}},"type":"object","required":["nuid","first_name","last_name","email","campus"],"title":"FacultyCreate"},"FacultyProfileResponse":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"campus":{"type":"integer","title":"Campus"},"active":{"type":"boolean","title":"Active"},"maxLoad":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxload"},"needsAdminReview":{"type":"boolean","title":"Needsadminreview","default":false},"course_preferences":{"items":{"$ref":"#/components/schemas/CoursePreferenceInfo"},"type":"array","title":"Course Preferences"},"meeting_preferences":{"items":{"$ref":"#/components/schemas/MeetingPreferenceInfo"},"type":"array","title":"Meeting Preferences"}},"type":"object","required":["nuid","first_name","last_name","email","campus","active","course_preferences","meeting_preferences"],"title":"FacultyProfileResponse"},"FacultyResponse":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"campus":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"maxLoad":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Maxload"}},"type":"object","required":["nuid"],"title":"FacultyResponse"},"FacultyUpdate":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"campus":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"},"max_load":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Load"}},"type":"object","title":"FacultyUpdate"},"GenerateScheduleRequest":{"properties":{"parameters":{"$ref":"#/components/schemas/AlgorithmParameters","default":{"MaxTimeBlockCapacity":0.15}}},"type":"object","title":"GenerateScheduleRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"InstructorInfo":{"properties":{"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"course_preferences":{"items":{"$ref":"#/components/schemas/CoursePreferenceInfo"},"type":"array","title":"Course Preferences"},"meeting_preferences":{"items":{"$ref":"#/components/schemas/MeetingPreferenceInfo"},"type":"array","title":"Meeting Preferences"}},"type":"object","required":["nuid","first_name","last_name","email","course_preferences","meeting_preferences"],"title":"InstructorInfo"},"InviteLinkResponse":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"invite_link":{"type":"string","title":"Invite Link"}},"type":"object","required":["first_name","last_name","email","invite_link"],"title":"InviteLinkResponse"},"InviteRequest":{"properties":{"nuid":{"type":"integer","exclusiveMinimum":0.0,"title":"Nuid"},"role":{"type":"string","title":"Role","default":"VIEWER"}},"type":"object","required":["nuid"],"title":"InviteRequest"},"InviteResponse":{"properties":{"user":{"$ref":"#/components/schemas/UserResponse"},"signup_url":{"type":"string","title":"Signup Url"}},"type":"object","required":["user","signup_url"],"title":"InviteResponse"},"MeetingPreferenceInfo":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"preference":{"type":"string","title":"Preference"}},"type":"object","required":["time_block_id","preference"],"title":"MeetingPreferenceInfo"},"RegenerateScheduleRequest":{"properties":{"parameters":{"$ref":"#/components/schemas/AlgorithmParameters","default":{"MaxTimeBlockCapacity":0.15}}},"type":"object","title":"RegenerateScheduleRequest"},"ScheduleActiveLockResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"locked_by":{"type":"integer","title":"Locked By"},"display_name":{"type":"string","title":"Display Name"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["section_id","locked_by","display_name","expires_at"],"title":"ScheduleActiveLockResponse","description":"Returned for each active lock when polling a schedule's lock state."},"ScheduleCreate":{"properties":{"name":{"type":"string","title":"Name"},"semester_id":{"type":"integer","title":"Semester Id"},"campus":{"type":"integer","title":"Campus"},"new_courses":{"items":{"type":"integer"},"type":"array","title":"New Courses","default":[]}},"type":"object","required":["name","semester_id","campus"],"title":"ScheduleCreate"},"ScheduleResponse":{"properties":{"schedule_id":{"type":"integer","title":"Schedule Id"},"name":{"type":"string","title":"Name"},"semester_id":{"type":"integer","title":"Semester Id"},"draft":{"type":"boolean","title":"Draft"},"campus":{"type":"integer","title":"Campus"},"active":{"type":"boolean","title":"Active"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":"idle"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"course_list":{"items":{"$ref":"#/components/schemas/CourseResponse"},"type":"array","title":"Course List","default":[]}},"type":"object","required":["schedule_id","name","semester_id","draft","campus","active"],"title":"ScheduleResponse"},"ScheduleUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"draft":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Draft"}},"type":"object","title":"ScheduleUpdate"},"SectionCreate":{"properties":{"schedule_id":{"type":"integer","title":"Schedule Id"},"course_id":{"type":"integer","title":"Course Id"},"time_block_id":{"type":"integer","title":"Time Block Id"},"capacity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Capacity"},"faculty_nuids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Faculty Nuids"}},"type":"object","required":["schedule_id","course_id","time_block_id"],"title":"SectionCreate"},"SectionLockResponse":{"properties":{"section_lock_id":{"type":"integer","title":"Section Lock Id"},"section_id":{"type":"integer","title":"Section Id"},"locked_by":{"type":"integer","title":"Locked By"},"locked_at":{"type":"string","format":"date-time","title":"Locked At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["section_lock_id","section_id","locked_by","locked_at","expires_at"],"title":"SectionLockResponse","description":"Returned on successful lock acquisition or refresh."},"SectionResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"schedule_id":{"type":"integer","title":"Schedule Id"},"time_block_id":{"type":"integer","title":"Time Block Id"},"course_id":{"type":"integer","title":"Course Id"},"capacity":{"type":"integer","title":"Capacity"},"section_number":{"type":"integer","title":"Section Number"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"assignment_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Assignment Score"}},"type":"object","required":["section_id","schedule_id","time_block_id","course_id","capacity","section_number"],"title":"SectionResponse"},"SectionRichResponse":{"properties":{"section_id":{"type":"integer","title":"Section Id"},"section_number":{"type":"integer","title":"Section Number"},"capacity":{"type":"integer","title":"Capacity"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"schedule_id":{"type":"integer","title":"Schedule Id"},"comment_count":{"type":"integer","title":"Comment Count","default":0},"crosslisted_section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Crosslisted Section Id"},"course":{"$ref":"#/components/schemas/CourseInfo"},"time_block":{"anyOf":[{"$ref":"#/components/schemas/TimeBlockInfo"},{"type":"null"}]},"instructors":{"items":{"$ref":"#/components/schemas/InstructorInfo"},"type":"array","title":"Instructors"}},"type":"object","required":["section_id","section_number","capacity","schedule_id","course","instructors"],"title":"SectionRichResponse"},"SectionUpdate":{"properties":{"time_block_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Time Block Id"},"course_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Course Id"},"capacity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Capacity"},"room":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room"},"crosslisted_section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Crosslisted Section Id"},"faculty_nuids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Faculty Nuids"}},"type":"object","title":"SectionUpdate"},"SemesterCreate":{"properties":{"season":{"type":"string","title":"Season"},"year":{"type":"integer","title":"Year"},"active":{"type":"boolean","title":"Active","default":true}},"type":"object","required":["season","year"],"title":"SemesterCreate"},"SemesterResponse":{"properties":{"semester_id":{"type":"integer","title":"Semester Id"},"season":{"type":"string","title":"Season"},"year":{"type":"integer","title":"Year"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["semester_id","season","year","active"],"title":"SemesterResponse"},"SemesterUpdate":{"properties":{"season":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Season"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","title":"SemesterUpdate"},"Severity":{"type":"integer","enum":[1,2,3],"title":"Severity"},"TimeBlockCreate":{"properties":{"meeting_days":{"type":"string","minLength":1,"title":"Meeting Days","description":"Day letters, e.g. 'MWF' or 'TR'"},"start_time":{"type":"string","title":"Start Time","description":"Start time in HH:MM format"},"end_time":{"type":"string","title":"End Time","description":"End time in HH:MM format"},"campus_id":{"type":"integer","title":"Campus Id"},"block_group":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Block Group","description":"8-character hex string linking two rows of a split block pair"}},"type":"object","required":["meeting_days","start_time","end_time","campus_id"],"title":"TimeBlockCreate","description":"Payload for creating a new time block.\n\n`meeting_days` should be a compact string of uppercase day letters,\ne.g. \"MWF\" for Monday/Wednesday/Friday or \"TR\" for Tuesday/Thursday.\n\n`start_time` and `end_time` must be in \"HH:MM\" 24-hour format.\n\n`block_group` is optional. Set it to the same 8-character hex string on\ntwo sibling rows to mark them as a split block (e.g. \"T 9:50–11:30\" and\n\"R 1:30–2:50\" both with the same block_group). Split blocks are excluded\nfrom auto-assignment and must be assigned manually."},"TimeBlockInfo":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"days":{"type":"string","title":"Days"},"start_time":{"type":"string","title":"Start Time"},"end_time":{"type":"string","title":"End Time"}},"type":"object","required":["time_block_id","days","start_time","end_time"],"title":"TimeBlockInfo"},"TimeBlockResponse":{"properties":{"time_block_id":{"type":"integer","title":"Time Block Id"},"meeting_days":{"type":"string","title":"Meeting Days"},"start_time":{"type":"string","title":"Start Time"},"end_time":{"type":"string","title":"End Time"},"campus_id":{"type":"integer","title":"Campus Id"},"block_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Block Group"}},"type":"object","required":["time_block_id","meeting_days","start_time","end_time","campus_id"],"title":"TimeBlockResponse","description":"Full representation of a time block returned by the API."},"TimeBlockUpdate":{"properties":{"meeting_days":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Meeting Days"},"start_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Time"},"end_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Time"},"campus_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Campus Id"},"block_group":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Block Group"}},"type":"object","title":"TimeBlockUpdate","description":"Partial update payload for a time block. All fields are optional —\nonly the fields included in the request body will be updated."},"UploadResponse":{"properties":{"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"},"records_processed":{"type":"integer","title":"Records Processed","default":0},"records_successful":{"type":"integer","title":"Records Successful","default":0},"records_failed":{"type":"integer","title":"Records Failed","default":0},"available_faculty":{"items":{"type":"integer"},"type":"array","title":"Available Faculty","default":[]},"errors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Errors"}},"type":"object","required":["status","message"],"title":"UploadResponse"},"UserResponse":{"properties":{"user_id":{"type":"integer","title":"User Id"},"nuid":{"type":"integer","title":"Nuid"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"},"active":{"type":"boolean","title":"Active"}},"type":"object","required":["user_id","nuid","first_name","last_name","email","role","active"],"title":"UserResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"Warning":{"properties":{"Type":{"anyOf":[{"$ref":"#/components/schemas/WarningType"},{"type":"null"}],"description":"Type of warning"},"SeverityRank":{"$ref":"#/components/schemas/Severity","description":"Severity of this warning"},"Message":{"type":"string","title":"Message","description":"Warning detail for the user"},"FacultyID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Facultyid","description":"Related faculty member"},"CourseID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Courseid","description":"Related course"},"BlockID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blockid","description":"Related time block"}},"type":"object","required":["SeverityRank","Message"],"title":"Warning"},"WarningResponse":{"properties":{"Type":{"anyOf":[{"$ref":"#/components/schemas/WarningType"},{"type":"null"}],"description":"Type of warning"},"SeverityRank":{"$ref":"#/components/schemas/Severity","description":"Severity of this warning"},"Message":{"type":"string","title":"Message","description":"Warning detail for the user"},"FacultyID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Facultyid","description":"Related faculty member"},"CourseID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Courseid","description":"Related course"},"BlockID":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Blockid","description":"Related time block"},"warning_id":{"type":"integer","title":"Warning Id","description":"Unique warning ID"},"dismissed":{"type":"boolean","title":"Dismissed","description":"Whether this warning was dismissed","default":false},"section_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Section Id","description":"Directly linked section (manual-edit warnings only)"}},"type":"object","required":["SeverityRank","Message","warning_id"],"title":"WarningResponse","description":"Warning with persistence fields — returned by the API."},"WarningType":{"type":"string","enum":["Time block surpasses threshold","No valid time block for section-faculty pair","Faculty assigned unpreferenced course","Faculty assigned unpreferenced time","Conflict group courses overlap","Faculty overloaded with assignments","Insufficient faculty supply for section","Faculty double booked in time block"],"title":"WarningType","description":"Each member carries a human-readable message and a default severity.\n\nUsage:\n WarningType.FACULTY_OVERLOAD.value # → \"Faculty overloaded with assignments\"\n WarningType.FACULTY_OVERLOAD.severity # → Severity.HIGH"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"security":[{"BearerAuth":[]}]} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 35b71f4..63d7eda 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,6 +7,7 @@ import Courses from './pages/Courses'; import Sidebar from './components/Sidebar'; import LoginButton from './components/LoginButton'; import { useAuthInterceptor } from './hooks/useAuthInterceptor'; +import { UserProvider } from './context/UserContext'; function App() { const { isAuthenticated, isLoading, error } = useAuth0(); @@ -56,21 +57,23 @@ function App() { ) : ( -