> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pawtograder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Gradebook

> Track student grades with expression-based calculations, CSV imports, and flexible column types

# Gradebook

## Column Semantics

In the Pawtograder gradebook system, **columns** represent individual grade components that collectively form a student's overall course performance. Each column is defined by a unique slug identifier, a human-readable name, and a maximum score, but their power lies in their flexibility for grade calculation and data sources.

Columns can be:

* **Manually entered** by instructors who directly input grades cell-by-cell (supporting both whole numbers and decimal values)
* **Imported from CSV files** to bulk-load grades from external systems (with full metadata tracking of the import source and date)
* **Linked to programming assignments** through the assignment reference system

When linked to assignments, columns automatically pull grades from the autograder system, creating a seamless connection between student code submissions and gradebook entries.

**Expressions:** The most sophisticated column type uses **mathematical expressions** that reference other columns or assignments, creating a powerful dependency system for calculated grades. Using MathJS syntax, instructors can create complex formulas like `(gradebook_columns("homework-*") + assignments("project-*")) / 2` to automatically compute weighted averages, drop lowest scores, or apply curve adjustments. Score expressions can define functions and have access to the full MathJS library of math and utility functions. Any grade that is automatically calculated can also be manually overridden by the instructor on a case-by-case basis.

## Student Visibility of Grades

The **"released"** status in the gradebook determines whether students can see a particular column and its grades. For imported or manually-entered columns, instructors have direct control over this visibility — they can release columns to make them visible to students or unrelease them to hide grades during grading periods or for sensitive assessments. This setting is at the column level only (you can't release or unrelease individual grades).

For columns that are automatically calculated by Pawtograder (via an expression), students always see the current calculation *based only on what they can see*. For example, a column defined as the average of all homeworks will show the student the average of all homeworks they have released.

### Instructor-Only Columns

When creating or editing a calculated column, you can mark it as **"Staff-only"** (instructor-only). These columns are completely hidden from students until you release them. While unreleased, students cannot see the column definition or their grades.

When you release an instructor-only column:

1. Pawtograder creates a frozen snapshot of the current private scores for students
2. The instructor-only flag is cleared, making the column behave like a normal column going forward
3. Subsequent edits propagate to students via standard sync; unreleasing the column clears the public score but the column remains visible

This feature is useful for internal grading calculations, curve adjustments, bonus calculations, or draft grade formulas you want to finalize before sharing with students.

### Automatic Recalculation of Dependent Columns

When you release or unrelease a gradebook column, Pawtograder automatically enqueues recalculation for all columns that depend on it. This ensures that calculated columns (those using expressions) always reflect the current visibility state and scores of their source columns. For example, if you have a "Final Grade" column that references "Homework Average," releasing the homework average will trigger the final grade to recalculate for all students, ensuring they see accurate totals based on what's now visible to them.

## Managing Columns

### Reordering Columns

You can reorder gradebook columns to organize them logically:

* **Drag and drop**: Click and drag the grip icon (⋮⋮) in any column header to reorder columns
* **Move left/right**: Use the column menu (⋮) and select **Move Left** or **Move Right** to shift a column one position
* **Auto-layout**: Click the auto-layout button (grid icon) to automatically organize columns by their slug prefixes

Column reordering is saved immediately and affects the display for all instructors and the export order in CSV files.

## Manual Grade Entry

You can manually enter or edit grades directly in the gradebook by clicking on any cell. The gradebook supports both whole numbers and decimal values, allowing you to enter precise grades like `8.5` or `92.75`. This is particularly useful for assignments with partial credit or weighted scoring systems. The system validates that scores don't exceed the maximum score for the column.

When you override a calculated grade or manually adjust a grade, you can add a note explaining the reason for the override. These notes are visible in the gradebook interface and help maintain transparency about grade adjustments.

## Gradebook Recalculation

When gradebook columns use expressions that reference other columns, Pawtograder automatically recalculates affected grades when dependencies change. This recalculation process runs asynchronously in the background to maintain performance for large courses.

* When a grade changes, the system enqueues a recalculation job for affected gradebook rows
* Edge functions process these jobs asynchronously to update calculated columns
* The system tracks row versions to ensure updates are applied correctly and prevent race conditions
* If a row is already being recalculated, new updates are queued without incrementing the version, allowing the current worker to complete successfully
* When rubric check scores change, submission grade totals automatically recalculate to reflect the updated scores

This architecture ensures that calculated grades (such as averages and weighted scores) stay up-to-date even during periods of high activity, such as when multiple graders are entering scores simultaneously.

<Note>
  The gradebook recalculation system was recently fixed to address a critical bug where rows could become permanently stuck in the `is_recalculating=true` state. The fix ensures that version numbers are read after upserts complete, preventing version mismatches during batch processing.
</Note>

## "What If" Grade Calculations

The gradebook includes a "What If" mode that allows students and instructors to explore hypothetical grade scenarios by temporarily adjusting scores. This feature helps answer questions like "What grade do I need on the final exam to get an A?" or "How will my grade change if I skip this assignment?"

When a student enters "what if" mode:

* They can edit any editable grade cell to enter hypothetical scores
* Calculated columns automatically update to reflect the hypothetical scenario
* All changes are temporary and client-side only — no actual grades are modified
* Modified cells are visually highlighted to distinguish them from actual grades
* Students can reset to actual grades at any time

This feature is useful for:

* Students planning how to allocate study time across remaining assignments
* Instructors modeling the impact of curve adjustments or extra credit
* Advisors helping students understand grade requirements for specific outcomes

Instructors can enable the "What-If" feature for students via **Course Settings → Feature flags**. When disabled, the student gradebook is view-only. The What-If feature does not apply to the staff preview in the instructor gradebook.

## Importing Grades

To import grades, instructors use a 3-step wizard:

1. **File Selection**: Click the **Import Columns** button and select a CSV file from your computer, which the system parses client-side to extract tabular data.

2. **Column Mapping**: Choose which CSV column contains student identifiers and select whether they are email addresses or student IDs. For each remaining CSV column, decide whether to map it to an existing gradebook column for updates, create a new column (requiring you to set the maximum score), or ignore the column entirely. The system automatically detects email columns if present.

3. **Preview & Confirm**: Review a comprehensive preview table that shows exactly what changes will occur, highlighting grade updates with strikethrough old values and bold new values, warning about students in the CSV who aren't enrolled in the course, and providing alerts when attempting to override calculated column scores. Only after reviewing this detailed preview can you click **Confirm Import** to execute the changes.

The system automatically creates new columns with full audit metadata including filename, date, and creator information, updates only enrolled students' grades while preserving override behavior for calculated columns, and maintains complete import history for traceability and compliance purposes.

## Expression Builder

When creating or editing a column with a score expression, Pawtograder provides an **Expression Builder** interface with live validation and debugging tools:

* **Live validation**: As you type, the expression is parsed and validated in real-time. Parse errors and dependency errors (unknown column slugs, cycles) are shown immediately.
* **Full-screen mode**: Click **Expression Builder** to expand to a full-screen Monaco editor with additional features:
  * **Syntax highlighting and autocomplete**: The editor provides syntax highlighting for the gradebook expression language, with autocomplete for built-in functions and column slugs. Typing inside `gradebook_columns("…")` shows matching column slugs from your gradebook.
  * **Hover documentation**: Hover over any sub-expression to see its current evaluated value for the selected student.
  * **Student picker**: Select a student to test the expression against their actual gradebook data.
  * **Per-line evaluation**: See the intermediate value of each statement in your expression, displayed as inline annotations (e.g. `= 42`) next to each line.
  * **Score preview**: View both the raw numeric result and the rendered form (if a render expression is set).
  * **Incomplete values panel**: See which dependencies are missing or not released for the selected student.
* **Save blocking**: The Save button is disabled until the expression parses correctly and all dependency slugs resolve.
* **Gradebook table filters**: The gradebook table now includes **Select all / Select none** toolbar buttons in column and section filter dropdowns for faster multi-select filtering.

The Expression Builder helps you write and debug complex expressions before saving them, ensuring they work correctly for all students.

## Expression Syntax

The gradebook expression system uses **MathJS** as its foundation, enhanced with specialized functions for grade calculations. Expressions must return a numeric value that becomes the student's score for that column. The final expression line is the student's score for that column.

### Core Syntax

Expressions follow standard mathematical notation with variables, operators, and functions:

```javascript theme={null}
// Basic arithmetic
hw_average = (hw1 + hw2 + hw3) / 3

// Boolean logic and conditionals
passing = grade >= 60 ? 1 : 0

// Multi-line expressions (last line is the result)
midterm_weight = 0.3
final_weight = 0.7
weighted_score = midterm * midterm_weight + final * final_weight
weighted_score
```

### Data Access Functions

#### `gradebook_columns("slug-pattern")`

Retrieves gradebook column data with glob pattern matching. Returns objects with:

* `score`: `number | null` - The student's score (including overrides)
* `max_score`: `number` - The maximum score for the column
* `is_missing`: `boolean` - Whether the grade is missing/not entered
* `is_excused`: `boolean` - Whether the student is excused from this item
* `is_droppable`: `boolean` - Whether this item can be dropped in calculations

```javascript theme={null}
// Single column
homework1 = gradebook_columns("hw-01")

// Multiple columns with glob patterns
all_homeworks = gradebook_columns("hw-*")
skill_assessments = gradebook_columns("skill-*")

// Access score property
hw_score = gradebook_columns("hw-01").score
```

### Array Processing Functions

#### `countif(array, predicate)`

Counts array elements matching a condition using lambda syntax:

```javascript theme={null}
// Count skill assessments scoring exactly 2
meets_expectations = countif(gradebook_columns("skill-*"), f(x) = x.score == 2)

// Count passing grades
passing_count = countif(gradebook_columns("exam-*"), f(x) = x.score >= 70)
```

#### `sum(array)`

Sums numeric values or scores from gradebook objects:

```javascript theme={null}
total_points = sum(gradebook_columns("hw-*"))
```

#### `mean(array, weighted=true)`

Calculates weighted (default) or unweighted averages. The function gracefully handles undefined values and missing grades:

```javascript theme={null}
// Weighted average (accounts for different max scores)
hw_average = mean(gradebook_columns("hw-*"))

// Unweighted average
hw_simple_avg = mean(gradebook_columns("hw-*"), false)
```

Weighted average is defined as:

```javascript theme={null}
const totalPoints = validValues.reduce((a, b) => a + (b?.max_score ?? 0), 0);
const totalScore = validValues.reduce((a, b) => a + (b?.score ?? 0), 0);
if (totalPoints === 0) {
    return undefined;
}
return (100 * totalScore) / totalPoints;
```

Unweighted average is defined as:

```javascript theme={null}
return (100 * validValues.reduce((a, b) => a + (b && b.score ? b.score / b.max_score : 0), 0)) / validValues.length;
```

In either case, grades that are missing will count as 0 points, *unless* the grade is marked as `excused`, in which case it is excluded from the average. The calculation tolerates undefined values in the input array, ensuring robust grade calculations even when some data is missing.

<Note>
  The mean calculation now tolerates undefined values in the input array, automatically filtering them out before computing the average. This prevents errors when some gradebook columns have not yet been populated.
</Note>

#### `drop_lowest(array, count)`

Removes the lowest scoring droppable items:

```javascript theme={null}
// Drop 2 lowest homework scores
best_homeworks = drop_lowest(gradebook_columns("hw-*"), 2)
hw_final = mean(best_homeworks)
```

`drop_lowest` drops the lowest grades (including those that are "missing" or "excused") as long as the "droppable" flag is set on the grade (this is the default).

### Conditional Logic

#### `case_when([condition, value; ...])`

Convenience function for multi-condition branching using matrix syntax:

```javascript theme={null}
letter_grade = case_when([
  score >= 93, 97;  // A
  score >= 90, 93;  // A-
  score >= 87, 90;  // B+
  score >= 83, 87;  // B
  true, 0           // Default case
])
```

### Comparison Functions

Normal comparison functions like `==`, `>=`, `<=`, etc. are available, and can automatically coerce a gradebook value to a score for you (so that you do not need to map each gradebook value to a score to compare it).

### Complete Examples

#### Simple Counting Example

```javascript theme={null}
// Count skills meeting expectations (score = 2)
countif(gradebook_columns("skill-*"), f(x) = x.score == 2)
```

#### Complex Grade Calculation

```javascript theme={null}
// Multi-criteria grading with modifiers
CriteriaA = gradebook_columns("meets-expectations") >= 10 and 
            gradebook_columns("does-not-meet-expectations") == 0 and 
            gradebook_columns("average.hw") >= 85

CriteriaB = gradebook_columns("meets-expectations") >= 8 and 
            gradebook_columns("does-not-meet-expectations") == 0 and 
            gradebook_columns("average.hw") >= 75

CriteriaC = gradebook_columns("meets-expectations") >= 5 and 
            gradebook_columns("does-not-meet-expectations") == 0 and 
            gradebook_columns("average.hw") >= 65

CriteriaD = gradebook_columns("approaching-expectations") >= 9 and 
            gradebook_columns("does-not-meet-expectations") == 0 and 
            gradebook_columns("average.hw") >= 55

CriteriaPlus = gradebook_columns("total-labs") >= 8
CriteriaMinus = gradebook_columns("total-labs") < 6

letter = case_when([
  CriteriaA, 95;
  CriteriaB, 85;
  CriteriaC, 75;
  CriteriaD, 65;
  true, 0
])

mod = case_when([
  CriteriaPlus, 3;
  CriteriaMinus, -3;
  true, 0
])

final = max(letter + mod, 0)
final
```

## Exporting Grades

You can export gradebook data to CSV format for external analysis or record-keeping. Export buttons display a loading state while the export is being generated, providing clear feedback during the process.

### Export Options

The export feature provides two options:

1. **Standard Export**: Exports raw scores as stored in the gradebook, including any manual overrides
2. **Export with Rendered Expressions**: For columns defined by expressions, evaluates the expression for each student and exports the calculated numeric result instead of the formula

### How to Export

1. Click the **Download Gradebook** button at the bottom of the gradebook page
2. Optionally check **Use render expressions in CSV** to apply formatting
3. Click **Download CSV**

### Export Contents

The exported CSV includes:

* Student names and identifiers
* All gradebook columns (both manually entered and calculated)
* Current grade values for each student
* **Expression-rendered columns** (when enabled): Columns defined by expressions are exported with their calculated values
* Proper UTF-8 encoding with BOM for Excel compatibility
* Formula injection protection through proper CSV escaping

### Use Cases for Rendered Expressions

* Share final calculated grades with students or administrators
* Import computed averages into external grading systems that don't support expression syntax
* Analyze grade distributions based on complex calculations
* Create reports with final weighted scores
