Skip to content

About

Automatic grading and gradebook creation for introductory physics courses at Stanford University

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Automatic Gradebook System

Automated grade calculation. Pulls data from Canvas and Gradescope exports, applies a reflection/revision scoring policy for problem sets and learning assessments (exams), and produces a formatted Excel workbook for easy viewing. It is highly recommended that a new user of this code do two things prior to executing any code: (1) read the full content of the README, and (2) read the full content of the config.py file and edit as necessary for their own course. For new users it is also suggested to turn the DEBUG flag up to 2.

First implemented and used in Physics 81, Spring 2026 at Stanford University. Has also been tested retroactively on grades from Physics 41 and Physics 41E (also at Stanford), demonstrating compatibility with other course structures. Not every edge case has been tested in terms of different functionalities, but most of the major flags (letter grades vs S/NC, automatic attendance, reading quizzes, problem set sub-skills, dropping the lowest problem set, prorating individual categories, changing the number of midterm assessments, rounding-up students near category boundaries, optional extra credit etc.) have been tested by applying the library to these three courses.

Collaboratively developed with Github Copilot, Codex, and a variety of different models.


Privacy Notice

To maintain FERPA compliance as well as abide by Stanford's Moderate-Risk Data handling guidelines, raw grade data containing identifying information about students (usually located within the canvas/, gradescope/, and extra_credit/ directories), as well as any Excel workbooks produced by the code, should be temporarily moved outside the project root directory whenever using AI tools to update/modify the codebase (especially agentic workflows). This is the only foolproof method of preventing student data from being fed into the models during automated testing by the LLMs themselves. The .github/copilot-instructions.md and .codex/config.toml files within the repository attempt to prevent access to common private-data file types (*.csv, *.tsv, *.xlsx, *.xls, *.xlsm, *.ods, and *.zip) and generally seem to be respected during execution of simple tasks, but extensive access testing has demonstrated that both Copilot and Codex can find workarounds to these restrictions during complex debugging operations.

The repository also includes AGENTS.md for AI coding agents that read that convention, plus CLAUDE.md and .claude/settings.json for Claude Code, although these have not been as extensively tested as those for Codex and Copilot. CLAUDE.md imports AGENTS.md so the shared privacy rules stay in one place, and .claude/settings.json denies Claude Code file-tool access to the private data directories and file extensions listed above. These instruction and settings files are portability aids, not complete privacy controls. When using Copilot, verify that repository custom instructions are enabled for the current tool surface and that .github/copilot-instructions.md appears in Copilot's response references. When using Claude Code, verify that CLAUDE.md is loaded and run claude doctor if you need to confirm project settings are valid.

Before using AI coding tools, run these commands from the project root:

  1. Move private grade files out of the repo:
python ./move_grade_files.py
  1. Restore private grade files after development work:
python ./move_grade_files.py copyback

The move-out command relocates all private-data files with these extensions (.csv, .tsv, .xlsx, .xls, .xlsm, .ods, .zip) from the project root, canvas/, gradescope/, and extra_credit/ into a sibling ../<project_root>_tmp/ directory. For example, a project directory named grades uses ../grades_tmp/, while a directory named course_grades uses ../course_grades_tmp/. The script prints each moved file and preserves the same subdirectory layout inside that temporary directory. On copyback, the script removes the temporary directory if it is empty after the files are restored, and prints whether removal succeeded or files remain. If a source directory is missing, or if a destination file already exists, the script skips that item and prints a warning rather than overwriting anything.

Users are responsible for maintaining FERPA compliance, and care should be taken if updates/changes to this code are implemented with AI coding tools.

Stanford announced access to agentic workflows approved for FERPA and Moderate-Risk Data beginning June 30, 2026. The move_grade_files.py functionality is preserved here for users with personal workflows, and in the event that Stanford discontinues sponsorship and support of these tools.

https://uit.stanford.edu/news/new-ai-tools-stanford-arrive-june-30


First-Time Setup Checklist

  1. Install dependencies: pip install -r requirements.txt
  2. Read and edit config.py for the course.
  3. Create canvas/, gradescope/, and extra_credit/ as needed, then add the required exports.
  4. Check configuration: python validate_config.py
  5. Create or update attendance data if using manual attendance: python update_attendance.py
  6. Optionally verify Gradescope files: python verify_gradescope_files.py
  7. Generate the gradebook: python generate_gradebook.py
  8. Review <COURSE_PREFIX>_missing_scores.xlsx, enter normalized missing scores if needed, then re-run the generator.

Directory Structure

grades/                                 # Default root directory name, can be anything.
├── AGENTS.md                           # Shared AI-agent privacy and development instructions
├── CLAUDE.md                           # Claude Code instructions that import AGENTS.md
├── .codex/config.toml                  # Codex privacy guardrails
├── .claude/settings.json               # Claude Code project permission guardrails
├── .github/copilot-instructions.md     # GitHub Copilot privacy instructions
├── config.py                           # All tunable parameters (weights, paths, counts)
├── requirements.txt                    # Python libraries required, in pip format for easy installation
├── <COURSE_PREFIX>_attendance.xlsx     # Attendance file (create with update_attendance.py)
├── <COURSE_PREFIX>_gradebook.xlsx      # Output gradebook (generated)
├── <COURSE_PREFIX>_missing_scores.xlsx # Missing-score review workbook (generated)
├── canvas/                             # Canvas CSV exports (not tracked by git, so must be created)
├── gradescope/                         # Gradescope CSV exports (also not tracked, must be created)
├── extra_credit/                       # Problem-specific Gradescope CSV exports for optional extra credit
├── generate_gradebook.py               # Main command: produce the gradebook
├── update_attendance.py                # Command: create/update the attendance workbook
├── validate_config.py                  # Command: check config.py before loading private data files
├── verify_gradescope_files.py          # Command: check which Gradescope files are present
├── grade_processing/                   # Importable grading implementation modules
│   ├── debug_diagnostics.py            # Privacy-conscious aggregate debug summaries
│   ├── grade_histogram.py              # ASCII grade-distribution rendering helper
│   ├── load_data.py                    # Data loaders (Canvas, Gradescope, attendance)
│   ├── process_grades.py               # Core grade calculation logic
│   ├── sid_utils.py                    # Shared SIS/Gradescope ID normalization and matching helpers
│   └── validation.py                   # Configuration validation logic
├── tests/
│   └── test_synthetic_grade_logic.py   # Developer tests using synthetic in-memory data and temporary files only
└── tools/
    └── inspect_student_grade.py        # Instructor-only private diagnostic for one student

All commands are run from the grades/ root directory, using root-level paths such as python generate_gradebook.py.


Setup

pip install -r requirements.txt

Configuration (config.py)

Edit this file before running anything. Key settings:

Setting Description
COURSE_PREFIX Prepended to all generated output files. Default: course_year_quarter
WEIGHTS Per-category grade weights. Values are normalized by their sum, so they don't need to sum to 1.0
ATTENDANCE_FILE File for tracking attendance. Initial execution of the update_attendance.py script will create this file, after which the user may edit for manual attendance tracking
GRADESCOPE_MAIN_FILE Main Gradescope export filename used for automatic activity-sheet attendance. Default placeholder: Course_Term_YYYY_grades.csv
ATTENDANCE_AUTO_FROM_GRADESCOPE If False, use the manual attendance workbook. If True, calculate attendance from activity-sheet submission times in the main Gradescope export
ATTENDANCE_ACTIVITY_SHEET_COUNT Upper bound on activity-sheet numbers to search for when automatic attendance is enabled
N_PROBLEM_SETS Upper bound on problem set numbers to search for, excluding PS0
PSET_POINTS_BACK_FRACTION Fraction of lost PS points recoverable via revisions
PSET_DROP_LOWEST If True, drop each student's lowest-scoring problem set
PSET_SUBSKILLS_ENABLED Set True if this course uses Canvas-tracked problem set sub-skills assignments
N_PSET_SUBSKILLS Upper bound on problem set sub-skills assignment numbers to search for
PSET_SUBSKILLS_WEIGHT_FRACTION Fraction of the configured problem_sets weight assigned to problem set sub-skills when enabled
N_LEARNING_ASSESSMENTS Number of midterm LAs (not including the final)
LA_POINTS_BACK_FRACTIONS Per-LA revision fraction dict (final has no revisions)
REFLECTION_MAX_SCORE Maximum score on each per-problem reflection/revision rubric used to scale points recovered
RQ_ENABLED Set True if this course uses Canvas-tracked reading quizzes
N_READING_QUIZZES Upper bound on reading quiz assignment numbers to search for
PRORATE_THRESHOLDS Per-category pro-rate threshold (PSets/sub-skills/attendance/RQs only, not LAs)
Z_SCORE_THRESHOLD Scores below this fraction are excluded from z-score statistics (for withdraws etc)
GRADE_CUTOFFS Final numerical thresholds for letter grades or S/NC cutoff
OUTPUT_SCORE_DECIMAL_PLACES Maximum number of decimal places displayed for normalized scores in the main gradebook workbook; trailing zeros are hidden, and stored numeric values are rounded to one extra place
DEBUG_LEVEL Debug verbosity. Level 2 prints aggregate diagnostics only; it does not print student score samples

Grade Weights

The defaults below come from config.py — edit config.py to change them. The code normalizes weights by their sum, so values do not need to sum to 1.0; set a category to 0 to exclude it.

Category Default weight
Problem Sets 20%
LA 1 (Midterm 1) 20%
LA 2 (Midterm 2) 20%
Final 30%
Attendance 10%
Reading Quizzes 0% (disabled)
Problem Set Sub-skills 0% (disabled)

When PSET_SUBSKILLS_ENABLED = True, the configured problem_sets weight is split by PSET_SUBSKILLS_WEIGHT_FRACTION. For example, if problem_sets is 30% and PSET_SUBSKILLS_WEIGHT_FRACTION = 0.05, regular problem sets receive 28.5% and problem set sub-skills receive 1.5%. The total configured weight remains 30%.

To add more midterm learning assessments, increase N_LEARNING_ASSESSMENTS and add a matching la_i entry to WEIGHTS for each numbered LA. For example, with three midterms, set N_LEARNING_ASSESSMENTS = 3 and include la_1, la_2, and la_3 in WEIGHTS. The Gradescope filename patterns use the same numbering, so the third midterm is expected at Learning_Assessment_3_scores.csv with an optional Learning_Assessment_3_-_Reflections_and_Revisions_scores.csv. LA_POINTS_BACK_FRACTIONS may include per-LA entries for any numbered LA; LA_POINTS_BACK_DEFAULT is used when an entry is omitted.

Other Default Course Settings

These defaults are intentionally generic starting points. Edit config.py for the actual course calendar, assignment names, and grading policy before loading real exports.

Setting Default
COURSE_PREFIX course_year_quarter
GRADESCOPE_MAIN_FILE Course_Term_YYYY_grades.csv
N_PROBLEM_SETS 9
PSET_POINTS_BACK_FRACTION 0.5
PSET_NO_REVISION_THRESHOLD 3
PSET_DROP_LOWEST True
PSET_SUBSKILLS_ENABLED False
PSET_SUBSKILLS_WEIGHT_FRACTION 0.05
N_LEARNING_ASSESSMENTS 2 midterm LAs, plus a final
LA_POINTS_BACK_FRACTIONS 0.25 for LA1 and LA2
REFLECTION_MAX_SCORE 5.0
ATTENDANCE_AUTO_FROM_GRADESCOPE False
ATTENDANCE_ACTIVITY_SHEET_COUNT 30
RQ_ENABLED False
N_READING_QUIZZES 30
GRADE_ROUNDING_MARGIN 0.01
OUTPUT_SCORE_DECIMAL_PLACES 4
Z_SCORE_THRESHOLD 0.50

Data Requirements

Canvas Export

  • Export the entire gradebook from Canvas as CSV.
  • Place in canvas/. The loader auto-selects the most recent file based on the YYYY-MM-DDTHHMM timestamp in the filename.
  • Required columns: Student, ID, SIS User ID, SIS Login ID, Section, SU ID.
  • Optional Canvas-tracked assignments, including reading quizzes and problem set sub-skills, are matched from configured patterns such as RQ_ASSIGNMENT_PATTERN and PSET_SUBSKILLS_ASSIGNMENT_PATTERN. Canvas assignment IDs such as (766054) are ignored, and matching tolerates case, spacing, and common punctuation differences such as colons and hyphens. The configured pattern should still describe the expected assignment name, for example "Problem Set {i}: Sub-skills".

Gradescope Files

Place all Gradescope CSV exports in gradescope/. Expected filenames (configurable via patterns in config.py):

Assignment Filename
Problem Set i Problem_Set_{i}_scores.csv
PS Reflection/Revision i Problem_Set_{i}_-_Reflections_and_Revisions_scores.csv
Learning Assessment i Learning_Assessment_{i}_scores.csv
LA Reflection/Revision i Learning_Assessment_{i}_-_Reflections_and_Revisions_scores.csv
Final Exam Final_Learning_Assessment_scores.csv
Main grades export e.g. Course_Term_YYYY_grades.csv

The Canvas export is the roster reference used by the gradebook. The main Gradescope export is not the primary roster source; it is required when automatic activity-sheet attendance is enabled because it contains the activity submission-time columns used for attendance.

Run verify_gradescope_files.py to check which files are present or missing.

Attendance

Use manual attendance unless ATTENDANCE_AUTO_FROM_GRADESCOPE = True in config.py.

For manual attendance, first-time setup creates the template with 10 weekly columns and a two-line header (second row reserved for days tracked):

python update_attendance.py

Edit the second header row (under each 'Week X') to set the number of days tracked that week. For each student, fill each week's cell with a fraction (days present / days tracked) or with a count (days present). Recalculate final attendance scores:

python update_attendance.py update

The updater avoids writing pandas index columns and reconstructs a clean two-line header when updating; it will prompt before overwriting the existing attendance file when run without the 'update' argument.

Automatic activity-sheet attendance is also available. Set this in config.py:

ATTENDANCE_AUTO_FROM_GRADESCOPE = True

Then configure the activity-sheet settings as needed:

ATTENDANCE_ACTIVITY_SHEET_COUNT = 30
ATTENDANCE_ACTIVITY_SHEET_PATTERN = "Activity Sheet {i}"
ATTENDANCE_ACTIVITY_SUBMISSION_TIME_SUFFIX = " - Submission Time"
ATTENDANCE_ALLOWED_NON_GROUP_OR_BLANK = 3

With automatic attendance enabled, load_attendance() calculates attendance directly from GRADESCOPE_MAIN_FILE. Matching non-empty submission timestamps are treated as group submissions and receive full credit for that activity sheet. Individual submissions and blank submissions are counted against the configured allowance; within the allowance they receive full credit, and after the allowance they receive no credit.

ATTENDANCE_ACTIVITY_SHEET_COUNT is an upper bound on activity-sheet numbers to search for. Missing activity-sheet columns are skipped, so the attendance denominator is based on detected activity sheets rather than the configured upper bound.

To write a review workbook with one abbreviated column per detected activity sheet (as1, as2, ..., asN) plus the final normalized attendance column:

python update_attendance.py update

Extra Credit

On occasion, a sub-part of a problem or an entire problem on a problem set or a learning assessment will be counted for extra credit, although only once or twice per quarter. It is assumed that this is done with 0 point rubric items on Gradescope, with the number of extra credit points in the rubric description. This extra credit can be applied across all problem sets, or split evenly across the configured learning assessments (midterms), or both!

Optional extra credit is supplied via CSV files from the Gradescope "Export Evaluations" functionality. This gives problem-specific application of rubric items for each student. Configure paths in config.py via PSET_EXTRA_CREDIT_CSV and LA_EXTRA_CREDIT_CSV and place the files in the directory extra_credit, as detailed in the directory structure at the top of this README.

  • CSV format: configure paths in config.py via PSET_EXTRA_CREDIT_CSV and LA_EXTRA_CREDIT_CSV and place the files in the directory extra_credit (or other path if desired).
    • Rows: one student per row. The 5th column must contain the student's SIS User ID, which is standard for the "Export Evaluations" function.
    • Extra-credit columns: headers (i.e. rubric item descriptions) must include the pattern (+X pts) (e.g. Full Solution (+3 pts)).
    • Student cells: TRUE / FALSE (case-insensitive). If a column is TRUE for a student, X points are awarded for that student.
    • For problem sets, the awarded points are added to the student's PSet revised total after dropping the lowest PSet (if enabled) but before normalization, and capped at the PSet total max.
    • For LAs, the extra credit is split evenly across loaded numbered LAs from LA1 through LA{N_LEARNING_ASSESSMENTS} that have positive max scores; each LA's revised score is capped at its max.

When generate_gradebook.py runs, extra credit CSVs are loaded automatically by grade_processing.load_data.load_extra_credit() and applied during category score calculation.


Example Usage

1. Verify Gradescope files (optional)

python verify_gradescope_files.py

2. Update attendance if needed

If ATTENDANCE_AUTO_FROM_GRADESCOPE = False and manual attendance entries have changed, recalculate the normalized attendance column before generating the gradebook:

python update_attendance.py update

If ATTENDANCE_AUTO_FROM_GRADESCOPE = True, skip this step for gradebook generation; generate_gradebook.py calculates attendance directly from GRADESCOPE_MAIN_FILE. Run python update_attendance.py update only when you want to write or refresh the automatic attendance review workbook.

3. Generate the gradebook

python generate_gradebook.py

The generator runs the same validation logic exposed by validate_config.py first and exits with a list of configuration issues if required settings are missing or inconsistent. To check configuration without generating a gradebook:

python validate_config.py

Produces <COURSE_PREFIX>_gradebook.xlsx with the following sheets:

Sheet Contents
Summary One row per student; all category and overall scores
Summary (Sorted) Same columns as Summary, sorted by revised overall score descending
Problem Sets Per-assignment original/revised/max raw scores
Learning Assessments Per-LA scores including final
Attendance Attendance data reference
Category Scores Normalized category scores plus each numbered mid-quarter LA (original/revised) and the final
Canvas Roster Raw roster reference
Upload Two-column upload sheet with headers Student ID and Grade (uses rounded-up letter grade by default)

All score values are normalized to [0, 1]. Main gradebook score columns are displayed with up to OUTPUT_SCORE_DECIMAL_PLACES decimal places, trailing zeros are hidden, and stored numeric values are rounded to one extra decimal place.

Missing scores workbook

When generate_gradebook.py runs it also creates/updates a workbook named <COURSE_PREFIX>_missing_scores.xlsx (path configured in config.MISSING_SCORES_FILE). This workbook contains two sheets:

  • Missing Exams: lists students who have a 0 or missing score on any Learning Assessment or the final. Columns: SIS User ID, Student, LA1, LA1 Replacement, LA2, LA2 Replacement, ..., LA{N}, LA{N} Replacement, Final where N is N_LEARNING_ASSESSMENTS. The first two rows after the header show class_mean and class_std values for each exam, calculated from nonzero normalized revised LA scores where revision data is available; the final uses its raw normalized score because it has no revisions. The generator normalizes each exam as total score divided by max points, inferring max points from Gradescope problem-column point totals if needed. Replacement columns are static, review-only suggestions for missing midterm LAs: each suggestion uses the target LA class mean plus target LA class standard deviation times the student's average z-score on later revised LAs and the final. For the last midterm LA, that average contains only the final z-score. Suggestions are capped to the valid normalized range from 0 to 1 and written to four decimal places. If you want to apply a suggested value, copy it into the corresponding LA# column; the replacement suggestion remains visible on the next run for auditability. Only normalized numeric scores entered in the LA# or Final columns are preserved and incorporated into grade calculations on the next run; copied midterm LA values populate both the raw and revised LA scores used for grade calculations.
  • Missing Problem Sets: lists students who have a 0 or missing score on any loaded Problem Set. Columns: SIS User ID, Student, PS1, PS2, ..., PS{N} where N is the N_PROBLEM_SETS search upper bound. If an entire problem-set export is absent, that assignment is skipped rather than listed as missing for every student. Manual normalized numeric entries are preserved and applied to the corresponding problem-set totals for grade calculation.

Populate these sheets with oral/alternate exam scores or corrected PS totals as normalized values from 0 to 1, then re-run generate_gradebook.py to incorporate manual scores into the gradebook.

3. Look closer at a single student

python tools/inspect_student_grade.py <SIS_USER_ID>

Prints a detailed breakdown of every PS, each mid-quarter LA, the final, attendance, and RQ score for that student, followed by the summary-only LA aggregate and overall-grade calculation. This is an instructor utility for targeted local review with private files in place. It is not safe for agentic AI workflows and should not be run by coding agents.


Developer Utilities

Run the synthetic tests with:

python tests/test_synthetic_grade_logic.py

These tests use synthetic in-memory data and temporary directories; they do not require real Canvas or Gradescope exports and do not leave dummy grade files in the project.

Debug Diagnostics

Set DEBUG_LEVEL = 2 in config.py for aggregate diagnostics while generating the gradebook. The generator reports table-level health information such as row counts, score-column valid/zero/nonzero counts, missing/non-numeric counts, min/max/mean values, duplicate ID counts, and all-zero warnings. These summaries are intended to catch failed score calculations, zeroed-out columns, NaN-heavy tables, and ID-matching issues without printing individual student rows or sample scores.

For AI-assisted development, use the synthetic tests and these aggregate diagnostics. Do not run tools/inspect_student_grade.py in agentic sessions, because it prints a detailed single-student grade breakdown.


Scoring Logic

Reflection/Revision Rubric Structure

Problem set and learning assessment revisions are represented by separate Gradescope reflection/revision assignments. Each revised problem has its own rubric score in that reflection/revision export, and the grade calculation treats that per-problem score as a multiplier on the allowed points back. The code does not inspect individual rubric item labels; it reads the exported per-problem score and divides it by REFLECTION_MAX_SCORE.

The original PH81 implementation used a 5-point rubric per revised problem:

Component Points Current rubric meaning
Reflection 3 1 point for clearly identifying where the original solution went wrong, plus up to 2 points for the quality of the reflection
Revision 2 Up to 2 points for the revised solution, with partial credit if the revision is not sufficiently complete

The current Gradescope rubric also uses non-credit/penalty items for cases such as no original solution tagged, unclear labeling that cannot be graded, or no reflection/revision submitted. Those items affect the exported reflection/revision score before this code reads it.

Instructors using a different reflection/revision scoring method should update the Gradescope rubric and config.REFLECTION_MAX_SCORE together. The points-back fractions (PSET_POINTS_BACK_FRACTION and LA_POINTS_BACK_FRACTIONS) are separate policy choices: they control how much of the lost original-assignment credit is recoverable when the rubric score is full credit.

Problem Sets

  • Problems worth ≤ PSET_NO_REVISION_THRESHOLD points are not eligible for revision.
  • For eligible problems: points_back = PSET_POINTS_BACK_FRACTION × points_lost × (reflection_score / REFLECTION_MAX_SCORE)
  • revised_score = original_score + points_back
  • Category score is weighted by max points across all PSets (optionally dropping the lowest).
  • N_PROBLEM_SETS is an upper bound on problem set numbers to search for. Missing problem-set files are skipped for category-score denominators because their generated max-point columns are 0.
  • Pro-rate optionally applies: if score ≥ threshold → 1.0; otherwise score / threshold.

Problem Set Sub-skills

  • Loaded from Canvas when PSET_SUBSKILLS_ENABLED = True.
  • Assignment columns must match PSET_SUBSKILLS_ASSIGNMENT_PATTERN after mild normalization of case, spacing, Canvas assignment IDs, colons, and hyphens.
  • Each sub-skill assignment is normalized by its own Canvas Points Possible value, then all detected sub-skills are averaged equally.
  • N_PSET_SUBSKILLS defines the assignment numbers to search for; missing/unmatched sub-skills are skipped rather than counted as zero.
  • Pro-rate optionally applies using PRORATE_THRESHOLDS['problem_set_subskills'].
  • Overall weighting is carved out of the configured problem_sets weight using PSET_SUBSKILLS_WEIGHT_FRACTION.

Learning Assessments

  • All problems are eligible for revision.
  • Same formula as PSets, using per-LA LA_POINTS_BACK_FRACTIONS.
  • The final exam has no revisions.
  • Pro-rate does not apply to LAs.
  • N_LEARNING_ASSESSMENTS controls the numbered midterm LAs. For each i from 1 through N_LEARNING_ASSESSMENTS, configure the matching la_i weight in WEIGHTS; validation fails if one is missing.
  • Each LA is normalized to [0, 1] individually and weighted by its own entry in WEIGHTS.
  • The Category Scores sheet shows each numbered LA (original and revised) and the final individually. The weighted-average la and la_revised display values appear only in the two Summary sheets.

Reading Quizzes

  • Loaded from Canvas. Assignment columns must match RQ_ASSIGNMENT_PATTERN (e.g., "RQ 1").
  • Each matched RQ is normalized by its own Canvas Points Possible value, then the detected normalized RQ scores are averaged equally.
  • N_READING_QUIZZES defines the assignment numbers to search for; missing/unmatched RQs are skipped rather than counted as zero.
  • Only active when RQ_ENABLED = True.

Overall Grade

overall = sum(category_score_i × weight_i) / sum(weight_i)

Dividing by the sum of weights means unused categories (weight = 0) are automatically excluded.

Z-Scores

  • Computed for revised overall score only.
  • Mean and std exclude students below Z_SCORE_THRESHOLD (default 50%).

Letter Grades

Final letter grades assigned based on the GRADE_CUTOFFS dictionary defined in the config.py file. If a student's numeric overall score falls within GRADE_ROUNDING_MARGIN (default 1%) below a higher cutoff, the grade is rounded up; that rounded value is stored in the Summary sheets as column letter_round so it can be reviewed before uploading. By default, the "Upload" sheet uses the rounded grade. Adjust the margin in config.py via GRADE_ROUNDING_MARGIN.


Column Naming Convention in Output Gradebook

All scores are normalized to [0, 1].

Column Description
ps{i} / ps{i}_revised / ps{i}_max Per-PS raw scores
la{i} / la{i}_revised / la{i}_max Per-LA raw scores
final / final_max Final exam raw score and max points; there is no final_revised column because the final has no revisions
pset / pset_revised PS category score [0, 1]
pset_subskills Problem set sub-skills score [0, 1]
la{i} / la{i}_revised Individual normalized LA score [0, 1] in Category Scores
final Individual normalized final-exam score [0, 1] in Category Scores
la / la_revised Weighted-average LA display score [0, 1] in Summary and Summary (Sorted) only
attendance Attendance fraction [0, 1]
rq Reading quiz mean [0, 1]
overall / overall_revised Weighted overall grade [0, 1]
revision_benefit overall_revised − overall
zscore Revised-score z-score relative to students above threshold
letter Letter grade from revised overall score
letter_round Letter grade rounded up when within GRADE_ROUNDING_MARGIN (stored separately; used by the Upload sheet)

Acknowledgments

This project was created and maintained by Charles Blakemore for Stanford physics instruction, with substantial development support from AI coding tools, especially GitHub Copilot and OpenAI Codex. These tools were used extensively for implementation, refactoring, documentation, and debugging support, alongside direct instructor design, review, testing, and maintenance.

Grading policy decisions, privacy constraints, validation, and final responsibility for use of the code remain with the human maintainer. The repository is intentionally documented with AI-assisted development in mind: privacy guardrails, synthetic tests, and aggregate diagnostics are included so future maintainers can continue using coding assistants without exposing student data.

License

This project is released under the MIT License. See LICENSE for details. Given standard 'work-for-hire' stipulations, Stanford University retains copyright.

About

Automatic grading and gradebook creation for introductory physics courses at Stanford University

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages