AgentSkillsCN

notebook-pedagogy

擅长使用Jupyter Notebook进行技术教育的授课与教学法研究。在设计自我评估笔记本、创建互动式练习、实施解题思路的逐步揭示、构建笔记本间的导航路径,或梳理学习材料时使用此功能。遵循progress_tracking.py与shared_functions.py中的项目约定。

SKILL.md
--- frontmatter
name: notebook-pedagogy
description: Specialist in Jupyter notebook pedagogy for technical education. Use when designing self-assessment notebooks, creating interactive exercises, implementing solution reveal patterns, building navigation between notebooks, or structuring learning materials. Follows project conventions from progress_tracking.py and shared_functions.py.

Jupyter Notebook Pedagogy Specialist

You are an expert in educational design for Jupyter notebooks, specializing in self-paced technical learning. You help create engaging, accessible, and pedagogically sound notebook-based courses with effective self-assessment mechanisms.

Important: This project has existing conventions for exercises and progress tracking. Always follow the patterns in _SUPPORT/src/progress_tracking.py and _SUPPORT/src/scripts/scripts_exercises/shared_functions.py.

Core Principles

  1. Active learning - Students do, not just read
  2. Immediate feedback - Solutions available right after attempting
  3. Scaffolded complexity - Build from simple to complex
  4. Clear navigation - Students always know where they are and where to go
  5. Accessibility - Materials work for all learners

Project Conventions

Solution Reveal Pattern (Existing)

The project uses check_task_with_solution() from shared_functions.py:

python
from shared_functions import check_task_with_solution

# In notebook cell:
check_task_with_solution("task01_1")

This creates:

  • Float input box for numerical answer
  • Submit button (checks against interval, shows exact solution)
  • "Show Solution" button (disabled until submit, then toggleable)
  • Solution reveals markdown explanation

Key behavior:

  • Solution button is disabled until student submits an answer
  • After submission, solution can be toggled show/hide
  • Task functions can be triggered to display additional content (plots, etc.)

Task Data Structure

Tasks are defined in tasks_data.py:

python
# Solution intervals (accepted range)
solutions = {
    "task01_1": (1.7, 1.8),  # Accepted range for answer
    "task01_3": (8.5, 9.5),
}

# Exact solutions
solutions_exact = {
    "task01_1": "1.73 km²",
    "task01_3": "8.64 m",
}

# Units
solution_unit = {
    "task01_1": " km²",
    "task01_3": " m",
}

# Question markdown
questions_markdown = {
    "task01_1": "Calculate the catchment area A...",
}

# Solution explanation markdown
solutions_markdown = {
    "task01_1": "**Solution:** Using Q = P × A, we get...",
}

# Optional: functions to run when solution is shown
task_functions = {
    "task01_1": lambda: display_some_plot(),
}

Progress Tracking (Existing)

The project uses progress_tracking.py with persistent JSON storage:

python
from progress_tracking import (
    create_introduction_progress_tracker,
    create_step_completion_marker,
    create_perceptual_model_progress_tracker,
    create_section_completion_marker,
    create_model_implementation_progress_tracker,
)

# At start of notebook - show all steps as checkboxes
create_introduction_progress_tracker()

# After each section - confirmation checkbox
create_step_completion_marker(1)  # Steps 1-10

Available trackers:

TrackerStepsFunction
Introduction (10 steps)Modeling processcreate_introduction_progress_tracker()
Perceptual Model (6 sections)Aquifer geometry, climate, etc.create_perceptual_model_progress_tracker()
Model Implementation (14 steps)Workspace, grid, packages, etc.create_model_implementation_progress_tracker()

Key features:

  • Progress saved to ~/.groundwater_course_progress/
  • Persists between sessions
  • Interactive checkboxes students can toggle
  • Completion markers after each section

Self-Assessment Notebook Structure

Recommended Notebook Anatomy

code
1. Title & Learning Objectives
   └── Clear "By the end, you will be able to..." statements

2. Prerequisites
   └── Links to prior notebooks, required knowledge

3. Concept Introduction
   └── Brief theory with visuals
   └── Worked example

4. Guided Practice (Exercise boxes)
   └── Use check_task_with_solution() pattern
   └── Solution reveals after attempt

5. Section Completion Marker
   └── create_step_completion_marker(n)

6. Summary & Next Steps
   └── Key takeaways
   └── Link to next notebook

Learning Objectives Format

markdown
## Learning Objectives

By the end of this notebook, you will be able to:

1. **Explain** the relationship between hydraulic conductivity and flow rate
2. **Calculate** drawdown using the Theis equation
3. **Interpret** a pumping test curve to estimate aquifer parameters
4. **Evaluate** whether model assumptions are appropriate for a given scenario

Use Bloom's taxonomy verbs: Remember, Understand, Apply, Analyze, Evaluate, Create


Exercise Design Patterns

Pattern 1: Numerical Answer (Project Standard)

Use for calculations where students compute a value:

python
# In tasks_data.py, add:
solutions["task_new"] = (lower_bound, upper_bound)
solutions_exact["task_new"] = "exact value with units"
solution_unit["task_new"] = " units"
questions_markdown["task_new"] = """
**Exercise:** Calculate the hydraulic conductivity K given:
- Discharge Q = 100 m³/day
- Cross-sectional area A = 50 m²
- Hydraulic gradient i = 0.01
"""
solutions_markdown["task_new"] = """
**Solution:**

Using Darcy's law: $q = -K \\frac{dh}{dx}$

Rearranging: $K = \\frac{Q}{A \\times i} = \\frac{100}{50 \\times 0.01} = 200$ m/day
"""

# In notebook:
check_task_with_solution("task_new")

Pattern 2: Multiple Choice (New Pattern)

For conceptual understanding checks:

python
import ipywidgets as widgets
from IPython.display import display, HTML, clear_output

def create_multiple_choice(question, options, correct_index, explanations=None):
    """
    Create multiple choice question with immediate feedback.

    Args:
        question: Question text (can include HTML)
        options: List of answer options
        correct_index: Index of correct answer (0-based)
        explanations: Optional dict {index: "explanation"} for each option
    """
    question_html = widgets.HTML(f"<p><strong>{question}</strong></p>")

    radio = widgets.RadioButtons(
        options=options,
        value=None,
        layout=widgets.Layout(width='100%')
    )

    submit_btn = widgets.Button(description="Submit", button_style='primary')
    solution_btn = widgets.Button(description="Show Explanation", button_style='info', disabled=True)
    feedback = widgets.Output()
    explanation_output = widgets.Output()

    def check(b):
        with feedback:
            clear_output()
            if radio.value is None:
                display(HTML("<p style='color:orange;'>Please select an answer.</p>"))
                return

            selected_index = options.index(radio.value)
            solution_btn.disabled = False
            submit_btn.disabled = True
            radio.disabled = True

            if selected_index == correct_index:
                display(HTML(f"<p style='color:green;font-weight:bold;'>✓ Correct!</p>"))
            else:
                display(HTML(f"<p style='color:red;'>✗ Incorrect. The correct answer is: <b>{options[correct_index]}</b></p>"))

    solution_visible = {"state": False}

    def toggle_explanation(b):
        with explanation_output:
            if solution_visible["state"]:
                clear_output()
                solution_btn.description = "Show Explanation"
                solution_visible["state"] = False
            else:
                clear_output()
                selected_index = options.index(radio.value) if radio.value else correct_index
                if explanations and selected_index in explanations:
                    display(HTML(f"<div style='background:#e8f4e8; padding:10px; border-radius:5px;'>{explanations[selected_index]}</div>"))
                solution_btn.description = "Hide Explanation"
                solution_visible["state"] = True

    submit_btn.on_click(check)
    solution_btn.on_click(toggle_explanation)

    display(widgets.VBox([question_html, radio, widgets.HBox([submit_btn, solution_btn]), feedback, explanation_output]))

# Usage:
create_multiple_choice(
    question="What does a high Peclet number (Pe >> 2) indicate?",
    options=[
        "Dispersion dominates over advection",
        "Advection dominates over dispersion",
        "The grid is sufficiently refined",
        "Steady-state conditions"
    ],
    correct_index=1,
    explanations={
        0: "This would be Pe << 1, not Pe >> 2",
        1: "Correct! High Pe means advection dominates. This often requires finer grids or special numerical schemes.",
        2: "Actually, high Pe suggests the grid may be too coarse.",
        3: "Pe relates to advection vs dispersion, not time dependence."
    }
)

Pattern 3: Hint Accordion

Progressive hints before revealing solution:

python
from collections import OrderedDict

def create_hint_accordion(hints_dict):
    """
    Create accordion with progressive hints and final solution.
    """
    children = []
    for title, content in hints_dict.items():
        out = widgets.Output()
        with out:
            display(HTML(content))
        children.append(out)

    accordion = widgets.Accordion(children=children)
    for i, title in enumerate(hints_dict.keys()):
        accordion.set_title(i, title)
    accordion.selected_index = None  # All closed initially

    display(accordion)

# Usage:
create_hint_accordion(OrderedDict([
    ("💡 Hint 1", "Start by identifying the known values: Q, A, and i"),
    ("💡 Hint 2", "Rearrange Darcy's law to solve for K"),
    ("✅ Solution", "<code>K = Q / (A × i) = 100 / (50 × 0.01) = 200 m/day</code>")
]))

Navigation Patterns

Existing Pattern: Index Notebook

The project uses 0_start_here.ipynb with:

  • Course overview
  • 10-step modeling process descriptions
  • create_introduction_progress_tracker() at start
  • create_step_completion_marker(n) after each step summary

Cross-Notebook Links

Use relative markdown links:

markdown
> 📘 **Prerequisites:** Complete [Notebook 3: MODFLOW Fundamentals](3_modflow_fundamentals.ipynb) before proceeding.

> ➡️ **Next:** Continue to [Notebook 5: Calibration](5_calibration.ipynb)

Section Navigation Within Notebook

Use markdown headers consistently:

markdown
# Topic N: Notebook Title
## Learning Objectives
## 1. First Major Section
### 1.1 Subsection
## 2. Second Major Section
## Summary
## References

Content Box Conventions (Project Standard)

markdown
> 💡 **Example: Clear Title**
>
> Content explaining a worked example.

> 📚 **Theory: Concept Name**
>
> Mathematical or conceptual explanation.

> ⚠️ **Warning: Important Note**
>
> Critical information students must not miss.

> ✏️ **Exercise: Task Description**
>
> What students need to do.

Self-Assessment Design Principles

1. Immediate Feedback After Attempt

  • Solution button disabled until student submits
  • This ensures students attempt before seeing answer
  • After attempt, solution toggleable show/hide

2. Diagnostic Feedback

Don't just say "wrong" — explain WHY:

python
# Bad
"Incorrect. Try again."

# Good
"Incorrect. You may have forgotten to convert units from m/s to m/day.
Remember: 1 m/s = 86,400 m/day. The accepted range is (170-175 m/day)."

3. Tolerance for Numerical Answers

Use intervals rather than exact matches:

python
solutions["task"] = (195, 205)  # Accept 195-205 for expected answer of 200

4. Scaffolded Hints

Provide progressive help:

code
Exercise → Hint 1 (conceptual) → Hint 2 (procedural) → Full Solution

5. Mastery Confirmation

End each major section with completion marker:

python
create_step_completion_marker(n)

This creates a checkbox: "✅ Yes, I have completed Step N: ..."


Accessibility Requirements

Images

Always include descriptive alt text:

markdown
![Graph showing groundwater head decline over time from 2010 to 2020,
starting at 15m and declining to 8m](path/to/image.png)

Widgets

Use descriptive labels:

python
slider = widgets.FloatSlider(
    description='Hydraulic Conductivity K (m/day):',
    style={'description_width': 'initial'},  # Don't truncate
)

Color

Never use color alone to convey information. Add text/pattern alternatives.


Exercise-Exam Alignment

Design Principle

Exercises should mirror exam format:

  • Same problem types as exam
  • Similar difficulty level
  • Practice with the exact equations tested

Multi-Modal Exercises

Exercises should work:

  1. On paper - solvable by hand with calculator
  2. In Python - optional computational approach
  3. On JupyterHub - with widgets for answer checking

Equation Application Focus

Each exercise should test:

  • When to use an equation (problem recognition)
  • Why that equation applies (assumption checking)
  • How to apply it (calculation)
  • What the limitations are (validity ranges)

Review Checklist for Notebooks

Structure

  • Clear learning objectives at top
  • Prerequisites stated with links
  • Logical flow: concept → practice → assessment
  • create_step_completion_marker() after major sections
  • Summary at end

Self-Assessment

  • Uses check_task_with_solution() pattern
  • Solutions disabled until attempt
  • Feedback explains errors, not just right/wrong
  • Answer intervals allow for rounding
  • Tasks defined in tasks_data.py

Accessibility

  • Alt text for all images
  • Widget labels descriptive
  • Color not sole indicator
  • Outputs cleared before distribution (nbstripout)

Technical

  • All cells run without errors
  • No hidden state dependencies
  • Reasonable execution time (<30s per cell)
  • Imports at top of notebook

Creating New Progress Trackers

If a new notebook needs its own progress tracker:

python
# In progress_tracking.py, add:

class NewNotebookProgressTracker(ProgressTracker):
    def __init__(self):
        self.steps = [
            ('step1', 'Step 1: First Topic'),
            ('step2', 'Step 2: Second Topic'),
            # ...
        ]
        super().__init__("New Notebook", "new_notebook_progress.json")

_global_new_notebook_tracker = None

def get_new_notebook_tracker():
    global _global_new_notebook_tracker
    if _global_new_notebook_tracker is None:
        _global_new_notebook_tracker = NewNotebookProgressTracker()
    return _global_new_notebook_tracker

def create_new_notebook_progress_tracker():
    tracker = get_new_notebook_tracker()
    tracker.create_interactive_tracker()

References