Otter-Grader

repository·master·Indexed 41 days ago

https://github.com/ucbds-infra/otter-grader

A modular open-source autograding system for Python scripts and Jupyter Notebooks, version 7.0.0. It supports local grading via parallel Docker containers, LMS integrations with Canvas and Gradescope, and a client package for student feedback. The system provides specialized markers for defining solutions, prompts, and hidden tests for both Python and R code questions.

Tokens
26.9K
Snippets
107
Records
134
Agent score
51%

What's inside Otter-Grader

  1. Overview of Otter-Grader

    master

    Otter-Grader is a lightweight, modular open-source autograder designed for grading Python scripts and Jupyter Notebooks. It is built to be compatible with various assignment distribution and collection pipelines by abstracting autograding internals.

    Key capabilities include:

    • Local Grading: Uses parallel Docker containers for isolated grading.
    • LMS Integration: Supports grading via 3rd party Learning Management Systems like Canvas and Gradescope.
    • Managed Infrastructure: Supports deployment of an Otter-managed grading virtual machine.
    • Student Feedback: Provides a client package that allows students to run public checks locally on their own machines.
  2. What is Otter Assign and how is it used?

    master
    Otter Assign is an assignment development and distribution tool for Otter-Grader. It allows instructors to create assignments by writing questions, prompts, solutions, and both public and private tests within a single notebook. Otter Assign then parses this notebook to generate two distinct versions: a student version (containing only questions and prompts) and an autograder version (containing the solutions and tests).
  3. How to use built-in Otter plugins

    master

    Otter-Grader provides several built-in plugins to extend its functionality, such as grade overrides, rate limiting, and Gmail notifications.

    Important: When configuring these plugins, you must use their exact importable names. Do not include the sub-module path in the name. For example, use otter.plugins.builtin.GoogleSheetsGradeOverride instead of otter.plugins.builtin.grade_override.GoogleSheetsGradeOverride.

  4. Filter notebook cells for PDF export using HTML comments

    master

    To control which parts of a notebook are included in a PDF export (e.g., for creating grading templates), use HTML comments in Markdown cells to define filtering groups. Everything inside a group is exported; everything outside is removed.

    Filtering Syntax:

    • Start a group with: <!-- BEGIN QUESTION -->
    • End a group with: <!-- END QUESTION -->

    You can define multiple filtering groups within a single notebook.

    Adding Page Breaks: When calling the export utility, you can optionally force a page break after each filtering group by setting pagebreaks=True in otter.export.export_notebook or using the equivalent flag in the specific utility you are using.

    <!-- BEGIN QUESTION -->
    
    # This content will be in the PDF
    
    <!-- END QUESTION -->
  5. How Python submissions are executed

    master

    Otter-Grader processes all Python submissions by converting them into Jupyter notebooks and executing them using nbformat's ExecutePreprocessor.

    For standard Python scripts (.py files), Otter creates a notebook containing a single code cell with the script's entire content. This unified notebook-based execution ensures that the grading logic remains consistent regardless of whether the original submission was a script or a notebook.

    Key execution steps include:

    1. Running before_execution plugins.
    2. Removing cells tagged with otter_ignore or having otter[ignore]: true in metadata.
    3. (If grading from an Otter log) stripping non-import code and replacing it with environment unpacking code.
    4. Injecting additional checks specified in otter[tests] metadata.
    5. (If intercell seeding is enabled) injecting numpy and random seeding code.
    6. Adding the current working directory to sys.path.
    7. Injecting Otter initialization and result export cells.
    8. Executing the notebook via ExecutePreprocessor (errors are not ignored in debug mode).
    9. Loading exported results and running after_grading plugins.
  6. Best practices and caveats for seeding

    master

    When using seeding in Otter-Grader, keep the following in mind:

    1. Instructor Solutions: Instructors must seed their own solutions before writing hidden tests. This ensures that the reference values used for grading are calculated using the same deterministic random processes.
    2. Student Visibility: Students do not have access to the random seed used by the autograder. Consequently, any random values a student computes locally in their notebook may differ from the results produced when their submission is run through the autograder.
    3. Variable Overriding: When using seed_variable in notebooks, ensure the variable is not initialized in the same cell where it is used, otherwise the student's local assignment will override the autograder's injected seed.
  7. How environment shelving works in Otter logs

    master

    Otter can capture and store a student's global environment (variables, etc.) within a log entry. This allows instructors to debug assignments by inspecting the state of the environment at the time of a check, or allows grading to occur in environments that lack the student's original resources (e.g., access to a specific SQL server).

    Requirements:

    • The Otter configuration file must have "save_environment": true.
    • Environments are pickled using the dill library.
    • Note: Modules are NOT pickled. Libraries must be re-imported in the instructor's environment to use the objects retrieved from the log.
  8. Resolve point values in Exception-Based Python tests

    master

    Otter automatically calculates point values for test cases and questions based on the following logic:

    1. Test cases have points, Question does not: Unspecified test cases are worth 0 points, unless all specified test cases are 0 points (in which case the question is worth 1 point and unspecified cases are weighted equally).
    2. Both Test cases and Question have points: Unspecified test cases are weighted equally and their combined value is the question_points minus the sum of all specified test_case_points.
    3. Test cases have no points, Question has points: Each test case is weighted equally: points_per_case = question_points / number_of_test_cases.
    4. Neither has points specified: The question is worth 1 point, and all test cases are weighted equally.
  9. Create autograded questions in Quarto

    master

    An autograded question consists of a configuration block, a description, a solution block, and zero or more test blocks.

    Question Config Keys:

    • name: The unique identifier for the question.
    • manual: Set to false for autograded questions (default).
    • points: A list of integers representing points for each subpart.

    Test Types:

    • Public Tests: Default behavior. Visible to students.
    • Hidden Tests: Add # HIDDEN as the first line of an R cell within the # BEGIN TESTS block to create a test that is used for scoring but not distributed to students.
    # BEGIN QUESTION
    name: q1
    manual: false
    points:
        - 1
        - 1

    Question 1: Find the radius...

    # BEGIN SOLUTION
    ans.1 <- 2 * 2 * pi / 2 # SOLUTION
    # END SOLUTION
    # BEGIN TESTS
    expect_true(ans.1 > 1)
    # HIDDEN
    tol = 1e-5
    expect_true(ans.1 > 1.27 - tol)
    # END TESTS
    # END QUESTION
  10. Use solution removal rules to create student prompts

    master

    Otter uses specific comment markers to replace instructor code with prompts (...) in the student version of the notebook:

    • Replace with prompt: A line ending in # SOLUTION is replaced by .... If it is an assignment (e.g., x = 5 # SOLUTION), only the expression after the = is replaced.
    • Remove line: A line ending in # SOLUTION NO PROMPT or # SEED is completely removed.
    • Block replacement: Lines between # BEGIN SOLUTION and # END SOLUTION are replaced with ... (or # YOUR CODE HERE in R). If using # BEGIN SOLUTION NO PROMPT, the block is removed.
    • Custom Multiline Prompt: Use a multiline string to define a custom prompt for students:
      """ # BEGIN PROMPT
      # Custom prompt text
      """; # END PROMPT
      (The semicolon ; suppresses output in the student notebook).
    def square(x):
        y = x * x # SOLUTION NO PROMPT
        return y # SOLUTION
    
    nine = square(3) # SOLUTION
  11. Understand plugin initialization and configuration

    master

    When an AbstractOtterPlugin is instantiated, it receives three arguments in its __init__ method:

    1. submission_path: The absolute path to the submission being executed.
    2. submission_metadata: Information about the submission (from submission_metadata.json).
    3. plugin_config: Parsed configurations from the plugins key in otter_config.json.

    These are stored as self.submission_path, self.submission_metadata, and self.plugin_config.

    Warning: Some plugin events are executed in contexts where these variables are unavailable (they will be set to falsey values like {}). Do not rely on these instance variables in such events; instead, use the arguments passed directly to the event method.

  12. How Otter-Grader components work together

    master

    Otter-Grader is organized into six modular components that follow the assignment pipeline. Each component provides its own command-line interface (CLI).

    1. Otter Assign: Used by instructors to develop and distribute assignments. It converts notebooks containing prompts, solutions, and tests into sanitized versions for students and autograders.
    2. Otter Generate: Creates the necessary setup files required for autograding.
    3. Otter Check: A student-facing tool that allows students to run instructor-provided tests against their solutions locally to verify their work.
    4. Otter Export: Generates PDFs from Jupyter Notebooks (with optional filtering) for manual grading.
    5. Otter Run: Grades assignments locally on an instructor's machine without using containers. It also supports grading on a JupyterHub account.
    6. Otter Grade: Grades assignments locally using parallel Docker containers and outputs grade breakdowns as a CSV file.