fix: hide score on submit + total denominator + projector chart cleanup
Three small UX/fairness tweaks from manual live testing: 1. Post-submit "wait for reveal" screen: show only the response time, no score. The +score reveal leaked correctness — any positive number = correct, zero = wrong — short-circuiting the "stop and think" beat the reveal pause was supposed to enforce. Time stays as the engagement signal; score now waits for the instructor reveal. 2. Final-screen "Correct X / Y" denominator is now total_questions instead of questions_answered. Missed questions are scored zero, so they belong in the denominator visibly. Server adds total_questions to the session_ended payload. 3. Projector score-distribution: drop the in-chart count labels (they collided with each other and with the median tag at small N), restore the previously-computed-but-not-rendered x-axis tick labels at the bottom. Stats line at the foot keeps n / mean / max. Also: short-circuit the per-submit instructor + presence broadcasts when no instructor / projector is connected (no listener, no DB work). The 50-student load test was tight on margin against its 2 s time_limit; with the new presence_message / live_histogram_message DB queries firing on every submit, the margin disappeared on busy boxes. Conftest fixture also bumped to 8 s per question for the same reason — gives breathing room for sequential WS submits in the load test. 71/71 pytest green.
This commit is contained in:
@@ -576,19 +576,23 @@ function renderScoreArea(dist) {
|
||||
`;
|
||||
}).join("");
|
||||
|
||||
// X-axis tick labels at each bucket centre
|
||||
// X-axis tick labels at each bucket centre. With 10 buckets across the
|
||||
// 1000-unit-wide SVG these read cleanly at projector scale; the SVG
|
||||
// stretches but the text rotates if we wanted, here it's horizontal
|
||||
// because the labels are short ("0.0-1.0" etc.).
|
||||
const xLabels = buckets.map((b, i) => {
|
||||
const cx = (xEdge(i) + xEdge(i + 1)) / 2;
|
||||
return `<text class="x-tick-label" x="${cx}" y="${H - padB + 16}">${escapeText(b.label)}</text>`;
|
||||
return `<text class="x-tick-label" x="${cx}" y="${H - padB + 18}" text-anchor="middle">${escapeText(b.label)}</text>`;
|
||||
}).join("");
|
||||
|
||||
// Per-bucket count labels above each top, only if non-zero
|
||||
const dataLabels = buckets.map((b, i) => {
|
||||
// Per-bucket data points (small circles at the top of each band) — no
|
||||
// numeric labels above them. With small N the count labels collide
|
||||
// with the median tag and with each other when bars are short; the
|
||||
// x-axis labels + bottom legend (n / mean / max) carry that info now.
|
||||
const dataPoints = buckets.map((b, i) => {
|
||||
if (b.count === 0) return "";
|
||||
const cx = (xEdge(i) + xEdge(i + 1)) / 2;
|
||||
const cy = yFor(b.count) - 8;
|
||||
return `<text class="data-label" x="${cx}" y="${cy - 12}">${b.count}</text>
|
||||
<circle class="data-point" cx="${cx}" cy="${yFor(b.count)}" r="5.5"></circle>`;
|
||||
return `<circle class="data-point" cx="${cx}" cy="${yFor(b.count)}" r="5.5"></circle>`;
|
||||
}).join("");
|
||||
|
||||
// Median tag — find the bucket containing the cumulative midpoint
|
||||
@@ -622,15 +626,15 @@ function renderScoreArea(dist) {
|
||||
${yGrid}
|
||||
<line class="axis" x1="${padL}" x2="${padL + innerW}" y1="${padT + innerH}" y2="${padT + innerH}"></line>
|
||||
<line class="axis" x1="${padL}" x2="${padL}" y1="${padT}" y2="${padT + innerH}"></line>
|
||||
<text class="axis-title" x="${padL + innerW / 2}" y="${H - 6}" text-anchor="middle">Score band (out of ${(dist.max_total || 0).toFixed(1)})</text>
|
||||
<text class="axis-title" x="${padL + innerW / 2}" y="${H - 4}" text-anchor="middle">Score band (out of ${(dist.max_total || 0).toFixed(1)})</text>
|
||||
<text class="axis-title" x="${padL - 36}" y="${padT + innerH / 2}" transform="rotate(-90 ${padL - 36} ${padT + innerH / 2})" text-anchor="middle">Students</text>
|
||||
<path class="area-fill" d="${fillPath.join(" ")}"></path>
|
||||
<path class="area-line" d="${linePath.join(" ")}"></path>
|
||||
${dataLabels}
|
||||
${xLabels}
|
||||
${dataPoints}
|
||||
${medianMarks}
|
||||
</svg>
|
||||
<div class="chart-legend">
|
||||
<span>10 score bands · ${n} buckets</span>
|
||||
<span class="stat">n = <b>${total}</b> · mean <b>${mean.toFixed(2)}</b> · max <b>${(dist.max_total || 0).toFixed(1)}</b></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user