Module structure
| Module | Role | Key exports |
|---|---|---|
interview_manager.py | Entry point | main() |
config.py | Palette, fonts, paths, theme system | C, F, load_theme(), get_all_themes() |
settings.py | Persistent settings (settings.json) | load(), save(), get_smtp(), get_theme() |
database.py | All DB access — single entry point | get_db(), grade_record(), threshold_evaluate() |
app.py | Management window (root window) | ManagementWindow |
instructor_window.py | Instructor Panel | InstructorWindow |
student_window.py | Student Screen (second monitor) | show_question(), show_blank(), show_grades() |
dialogs.py | All modal dialogs | 40+ dialog classes including Canvas import, Theme Builder |
email_service.py | Email composition and sending | build_email_body(), send_recap(), send_grade_standings() |
widgets.py | Widget factories | Btn(), Lbl(), Ent(), Txt(), Cmb() |
gradebook_panel.py | Gradebook UI panel | GradebookPanel |
stats_window.py | Stats & charts (matplotlib) | StatsWindow |
transcription.py | Whisper 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
sqlite3with 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 buttons —
AppButtonusestk.Label(nottk.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()