Ed Helper#

This notebook is meant to assist with Between-Class Participation grading. To use:

  1. Download the Discussion data

    1. Go to Ed

    2. Open analytics

    3. Download the Threads JSON

  2. Make a copy of this notebook

  3. Upload the data

  4. Adjust the filename and dates below

  5. Run all cells in the notebook

  6. Review the student contributions at the bottom

import pandas as pd

FILENAME = "FILEPATH.json"
# dates are inclusive; 1-based
START = pd.Timestamp(year=2026, month=1, day=28, tz="US/Eastern")
# add a day, since the default time is 00:00:00
END = pd.Timestamp(year=2026, month=2, day=3, tz="US/Eastern") + pd.Timedelta(days=1)

Load data#

import json
import pandas as pd

data = json.load(open(FILENAME))

Include replies#

The JSON data includes reples (comments and answers) as nested under each post.

from typing import Any


def flatten_nested_items(items: list[dict[str, Any]]):
    """Recursively flatten all nested comments/answers."""

    flattened = items.copy()

    for item in items:
        for field in ["comments", "answers"]:
            if field in item:
                nested_items = item[field]
                flattened_nested_items = flatten_nested_items(nested_items)
                flattened.extend(flattened_nested_items)

    return flattened


flattened_data = flatten_nested_items(data)
# flattened_data

Convert to DataFrame#

posts = pd.json_normalize(flattened_data)
posts.info()
posts["created_at"] = pd.to_datetime(posts["created_at"])
# posts["created_at"]

Filter#

output = posts[(posts["created_at"] >= START) & (posts["created_at"] < END)]
print(output["created_at"].min())
print(output["created_at"].max())

Prep output#

# exclude the instructors
output = output[output["user.role"] != "admin"]

# sort by name
output = output.sort_values(["user.name", "created_at"])

# only include a subset of the columns
output = output[
    [
        "user.name",
        # "user.email",
        "url",
        # "created_at",
        # "title",
        "text",
    ]
]

# make links clickable
# https://stackoverflow.com/a/20043785/358804
output["url"] = output["url"].apply(lambda url: f'<a href="{url}">Open</a>')

# render newlines
# https://stackoverflow.com/a/56881411/358804
styled = output.style.set_properties(
    **{
        "text-align": "left",
        "white-space": "pre-wrap",
    }
)

Output#

from IPython.display import HTML

HTML(styled.to_html(escape=False))