Notebook Submission Helper (submit.py)

This helper supports submitting completed notebooks for credit by extracting student responses and sending them to a remote grading service.

Answer format convention

Student answers are expected to appear in code cells using the following pattern:

q1_answer = "A"
q2_answer = 3.14
q3_answer = ["token", "vector", "embedding"]

Where: - Each answer variable name begins with q - The question number follows (q1, q2, …) - The variable is assigned a valid Python literal

This convention allows answers to be reliably extracted from the notebook without requiring special widgets or forms.

Submission workflow

The submission process has three steps:

  1. Collect answers Code cells matching the answer pattern are located and extracted.

  2. Parse answers Extracted code is converted into a structured Python dictionary.

  3. Submit answers The parsed answers are sent via HTTP to a grading service.

Each step is implemented as a separate function to keep the logic clear and testable.


collect_answers


def collect_answers(
    show:bool=True, # Print extracted answers
    namespace:NoneType=None, # Namespace to search (defaults to globals())
    dname:NoneType=None, # Dialog name (dialoghelper only)
    path:NoneType=None, # Notebook path (local fallback only)
): # [('code', 'q1_answer = ...'), ...]

Collect student answers.

Priority: 1. Live Python variable state (default, works everywhere) 2. dialoghelper (optional, Solveit) 3. Notebook file parsing (local Jupyter only, requires path)


parse_answers


def parse_answers(
    raw, # Output from `collect_answers`, containing raw code strings.
): # Mapping from answer variable name (e.g. 'q1_answer') to its value.

Parse extracted answer code into a dictionary.


submit_answers


def submit_answers(
    student_id, answers:NoneType=None, raw_answers:NoneType=None, dname:NoneType=None, path:NoneType=None,
    url:str='https://nbsubmit-production.up.railway.app', api_key:NoneType=None, verbose:bool=True
):

Call self as a function.

# Test (not exported)

from data401_nlp.helpers.submit import parse_answers

review_answers


def review_answers(
    namespace:NoneType=None, # Namespace to search (defaults to globals())
    dname:NoneType=None, # Dialog name (Solveit / Deepnote)
    path:NoneType=None, # Notebook path (local fallback only)
    show:bool=True, # Print formatted output.
): # Parsed answers ready for submission.

Review extracted student answers WITHOUT submitting.

Priority: 1. Live variable state 2. dialoghelper (optional) 3. Notebook parsing (local only, requires path)

raw = [
    ("code", 'q1_answer = "A"'),
    ("code", "q2_answer = 42"),
]

assert parse_answers(raw) == {
    "q1_answer": "A",
    "q2_answer": 42,
}

print("parse_answers OK")
parse_answers OK