Use when evaluating partial credit for "show your work" math problems on MyOpenMath grading pages, especially when students uploaded work images, auto-scores are below passing, and the teacher wants a report-first bonus-only review before any score changes.
Review uploaded student work images on MyOpenMath
gbviewassess.phppages and recommend bonus partial credit for visible effort. This skill is bonus only: it rewards work shown with+2,+1, or+0, generates a full approval report first, and only applies approved bonuses afterward.
gbviewassess.php individual-student grading view+2, +1, or +0 based on work shown, then wait for teacher approval before writing anythinggbviewassess.php individual-student grading URLgrade-show-work-state.jsonviewworkwrap⚠️ Must NOT:
- Never decrease a score. This workflow is bonus only.
- Never skip the report. Generate the full report, show it to the teacher, and wait for explicit approval before editing any
scoreboxinput.- Never overwrite an approved non-zero score with a lower value. Only add bonus points.
- Never exceed the question max. Cap
currentScore + bonusat the displayed maximum.- Never judge correctness instead of effort. Award for visible mathematical work, not for a correct final answer.
- Never depend on UI preview clicks to obtain files. Extract URLs from
a.attach.preppedprogrammatically.- Never evaluate questions without both conditions: auto-score
< 4and a visible work-upload area (.viewworkwrap).- Never continue past 20 students in one session. Soft warning at 15, stop at 20.
gbviewassess.php grading page and load or create grade-show-work-state.json.+2, +1, or +0, then generate the report.scorebox fields, and save.grade-show-work-state.jsonscan while collecting recommendations, apply while writing approved bonuses.h2 and h3.gbviewassess.php pageEligibility reminder: only review questions that have a visible work-upload pane (div.questionpane.viewworkwrap / .viewworkwrap), at least one uploaded file, and an auto-score below 4.
const data = await state.gradePage.evaluate(() => {
const studentName = document.querySelector('h2')?.textContent?.trim() || '';
const assessmentName = document.querySelector('h3')?.textContent?.trim() || '';
const questions = Array.from(document.querySelectorAll('.bigquestionwrap')).map((wrap, qIndex) => {
const scoreInputs = Array.from(wrap.querySelectorAll('input[id^="scorebox"]'));
const fileLinks = Array.from(wrap.querySelectorAll('a.attach.prepped')).map((a) => ({
url: a.href,
filename: a.textContent.trim(),
}));
return {
qIndex,
label: wrap.querySelector('strong')?.textContent?.trim() || `Question ${qIndex + 1}`,
hasWorkUpload: !!wrap.querySelector('.viewworkwrap'),
scoreInputs: scoreInputs.map((input) => ({
id: input.id,
value: parseFloat(input.value) || 0,
max: parseFloat((input.nextSibling?.textContent || '').trim().replace('/', '')) || 0,
})),
fileLinks,
};
});
return { studentName, assessmentName, questions };
});
< 4hasWorkUpload === true because a .viewworkwrap work pane is present/visible for that questionhttps://files.myopenmath.com/ufiles/{uid}/{filename} and do not require authentication.| Bonus | Criteria | Examples |
|---|---|---|
+2 | Substantial work shown | correct setup, relevant formula, multiple steps, organized attempt, meaningful progress |
+1 | Some relevant work shown | wrote givens, began setup, drew relevant diagram, one real attempt step |
+0 | No meaningful work shown | blank, illegible, unrelated image, copied prompt only |
uid URL parameter to change to the next student; the original stu parameter may disappear after navigation.domcontentloaded, then verify the h2 student name changed.+1 or +2 listed in full detail+0 only summarized as a count instead of expandedSuggested structure:
# Partial Credit Report: {Assignment Name}
## Summary
| Student | Question | Auto-Score | Bonus | New Score | Work Shown |
|---------|----------|------------|-------|-----------|------------|
## Detailed Breakdown
| Student | Question | Work Description | Credit | Rationale |
|---------|----------|------------------|--------|-----------|
## Approval Request
Apply these bonuses? (yes / no / adjust)
scorebox inputs tied to the approved question parts.grade-show-work-state.json to phase apply with progress.for (const update of approvedUpdates) {
const input = state.gradePage.locator(`input#${update.scoreboxId}`);
await input.fill('');
await input.fill(String(update.newScore));
}
| Problem | Action |
|---|---|
No viewworkwrap or no uploaded file links | Skip the question; it is not eligible for this skill. |
| File fetch fails | Note the failure in the report and award +0 unless the teacher wants manual review. |
| Image is blank, illegible, or unrelated | Award +0 and say why in the report. |
| Student already has full or near-full credit | Cap bonus at the question max; never exceed it. |
| Score input pattern unclear | Use the scorebox{qIndex} / scorebox{qIndex}-{partIndex} convention and re-read the DOM before writing. |
| Session reaches 20 students | Stop, save state, generate report, and ask the teacher to resume in a new session. |
| Mistake | Fix |
|---|---|
| Grading mathematical correctness instead of work shown | Judge effort, setup, and visible reasoning only. |
| Clicking file previews instead of using URLs | Extract a.attach.prepped links directly from the DOM. |
| Writing scores before teacher approval | Report first, then wait for explicit approval. |
| Lowering an existing score during adjustment | Never decrease scores; this is bonus only. |
| Ignoring multipart score IDs | Use scorebox0-0, scorebox0-1, etc. for multipart questions. |
| Letting one long session run indefinitely | Warn at 15 students and stop at 20. |
grade-show-work-state.jsonscan — actively reviewing students and building recommendationsapply — approved changes are being written backuid, studentUrl, question index, and exact scoreboxId values for approved updates.{
"<grading-page-url>": {
"lastStudent": "LastName, FirstName",
"count": 12,
"phase": "scan",
"report": [
{
"student": "LastName, FirstName",
"uid": "7158609",
"studentUrl": "https://example.com/gbviewassess.php?cid=1&aid=2&uid=7158609",
"questions": [
{
"index": 0,
"label": "Question 1.",
"scoreboxId": "scorebox0-0",
"currentScore": 2,
"maxScore": 5,
"bonus": 2,
"newScore": 4,
"rationale": "Substantial work shown"
}
]
}
],
"timestamp": "2026-03-15T12:00:00Z"
}
}
| Element | Selector / Pattern | Notes |
|---|---|---|
| Student name | h2 | Current student identity check |
| Assessment name | h3 | Assignment/report title |
| Question container | .bigquestionwrap | One per question, 0-based order |
| Question label | .bigquestionwrap strong | Usually Question N. |
| Score inputs | input[id^="scorebox"] | Catch both scoreboxN and scoreboxN-M |
| Score wrapper hint | .score-inputs | Useful on variants, but do not rely on this wrapper alone |
| Single-part score | input#scoreboxN | Example: scorebox0 |
| Multipart score | input#scoreboxN-M | Example: scorebox0-0 |
| Max score text | scoreInput.nextSibling | Text node like /5 or /10 |
| Work upload pane | .viewworkwrap | Required for eligibility |
| File download link | a.attach.prepped[target="_blank"] | Direct file URL lives on the anchor |
| Work toggle | .viewworkwrap button.slim | Text flips between “Hide Work” and “View Work” |
| Save and Next Student | button.primary:has-text("Save and Next Student") | Full-page navigation |
| Save Changes | button.primary:has-text("Save Changes") | Appears more than once |
| Element | Selector | Notes |
|---|---|---|
| Overall score | button#assess_select | Text like Scored attempt. Score: X/Y. |
| Make Exception | button:has-text("Make Exception") | Header action |
| Override score | button:has-text("Override score") | Header action |
| Filters and Options | button:has-text("Filters and Options") | Header display control |
| Question expand/collapse | button#qselectN | Per-question version/score toggle |
| Question content region | div[role="region"][aria-label="Question N"] | Inside the selected question panel |
| More button | button#qmoreN | Per-question options |
| Student response input | input#qnNNNN or input#qnN-M | Read-only student answer field |
| View key button | button:has-text("View Key for Question N Part M") | Part-specific key display |
| Full credit button | button:has-text("Full credit") | Text may be Full credit all parts |
| Add feedback button | button.slim:has-text("Add feedback") | One per question |
| Question feedback box | div#fbN.fbbox | TinyMCE inline editor |
| General feedback box | div#fbgen.fbbox | Bottom-of-page feedback editor |
| General feedback label | span#fblblgen | Label for general feedback area |
| Show all tries | button.slim:has-text("Show all tries") | Conditional |
| Show applied penalties | button.slim:has-text("Show applied penalties") | Conditional |
| Side-by-side wrapper | div.sidebyside | Question pane plus work pane layout |
| Question content pane | div.questionwrap.questionpane | Left pane when uploads exist |
| Work upload pane | div.questionpane.viewworkwrap | Right pane with file uploads |
| Work list container | .viewworkwrap .introtext ul.nomark | File links remain in DOM even if hidden |
| File download link | a.attach.prepped[target="_blank"] | Direct fetch target for uploaded work |
| Preview toggle | span.videoembedbtn#fileembedbtnN[role="button"] | A <span> with button role, not a real <button> |
| Inline preview image | img#fileiframefileembedbtnN | Created dynamically after preview toggle |
| Return to Gradebook | button.secondary:has-text("Return to Gradebook") | Exit button |
| Message Student | a:has-text("Message Student") | External/student contact link |
scorebox{qIndex}scorebox{qIndex}-{partIndex}const input = document.getElementById('scorebox0');
const maxScore = parseFloat(input.nextSibling.textContent.trim().replace('/', ''));
files.myopenmath.com/ufiles/{uid}/{filename}.jpg) suitable for vision review.png, .pdf, .jpeg, .heicspan.videoembedbtn#fileembedbtnN[role="button"] is a span-based control, not a real button.[+] to [-] when expanded.img#fileiframefileembedbtnN.