Module structure

ModuleRoleKey exports
interview_manager.pyEntry pointmain()
config.pyPalette, fonts, paths, theme systemC, F, load_theme(), get_all_themes()
settings.pyPersistent settings (settings.json)load(), save(), get_smtp(), get_theme()
database.pyAll DB access — single entry pointget_db(), grade_record(), threshold_evaluate()
app.pyManagement window (root window)ManagementWindow
instructor_window.pyInstructor PanelInstructorWindow
student_window.pyStudent Screen (second monitor)show_question(), show_blank(), show_grades()
dialogs.pyAll modal dialogs40+ dialog classes including Canvas import, Theme Builder
email_service.pyEmail composition and sendingbuild_email_body(), send_recap(), send_grade_standings()
widgets.pyWidget factoriesBtn(), Lbl(), Ent(), Txt(), Cmb()
gradebook_panel.pyGradebook UI panelGradebookPanel
stats_window.pyStats & charts (matplotlib)StatsWindow
transcription.pyWhisper audio transcription (optional)transcribe_audio(), is_available()

Design principles

  • Single DB entry point — all grade writes go through grade_record(). No direct SQL grade writes from UI code.
  • No ORM — raw sqlite3 with Row factory for performance and simplicity. Foreign keys enforced on every connection.
  • Local-first — no network required except email send and optional Whisper model download.
  • Highest-wins — grades can only improve, guaranteed at the DB layer in _gr_highest_wins().
  • FERPA compliant — all student data stays in local SQLite. Research exports are HMAC-pseudonymized before leaving the machine.
  • Theme-immune buttonsAppButton uses tk.Label (not tk.Button) so ttk theme engine cannot override custom colors.

grade_record() — the central function

Every grade that enters the system — interview, Canvas import, manual entry — goes through this function:

def grade_record(
    student_id:           int,
    competency_id:        int,
    grade:                str,      # "Mastery"|"Approaching"|"Proficient"|"Quiz Level"
    source:               str = "Interview",
    session_id:           int | None = None,
    duration_seconds:     int | None = None,
    override_daily_limit: bool = False,
    notes:                str = "",
    approaching_only:     bool = False,
) -> dict:
    """
    Returns:
      attempt_id:        PK of the new grade_attempts row
      gradebook_updated: True if competency_grades was improved
      previous_grade:    Grade before this attempt
      new_grade:         Grade after this attempt
      propagated_to:     List of linked competency IDs updated
      locks_earned:      List of letter grades newly locked in
    """

Startup sequence

# interview_manager.py main()
_patch_ssl_with_certifi()   # fix macOS cert issues
settings.load()             # read settings.json
load_theme()                # populate C[] from theme key
init_db()                   # CREATE TABLE IF NOT EXISTS …
ManagementWindow().mainloop()