1
0
Files
coldcall_lti/coldcall_lti/templates/instructor_home.html
Benjamin Mako Hill 4e29baddf8 Setup banner, assessment timestamps, and installation docs
Instructor home shows a setup banner while a course has no class
schedule, since that is the one gap that degrades the student
experience (free-date opt-outs, no withdrawal locking); it points at
the prefilled schedule form and settings and disappears once any
class day exists.

Calls gain assessment_entered_at, stamped when an outcome is recorded
or actually changed (bulk saves that change nothing don't restamp),
null for pending calls and legacy imports, and included in the calls
export — distinguishing assessed-in-class from assessed-later.

The README setup section now gives both install routes: pure
pip/venv, and Debian system packages with the apt line. Fixing this
uncovered that numpy was missing from the project dependencies (the
grading engine needs it; the system-site-packages venv had masked
the omission).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 13:35:25 -07:00

97 lines
3.6 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ course.title or "Cold Call" }}{% endblock %}
{% block body %}
<h1>{{ course.title or course.lti_context_id }}</h1>
{% if not has_schedule %}
<div class="banner">
<p><strong>No class schedule yet.</strong> Until you add your meeting
days, students can report an absence for <em>any</em> date, and
opt-out withdrawals can't be locked to class start times.
<a href="{{ url_for('instructor.schedule') }}">Set up the
schedule</a> — pick your weekdays and the range is prefilled from
the Canvas course dates when available.</p>
<p class="muted">Also worth one look before your first class:
<a href="{{ url_for('instructor.settings') }}">course settings</a>
(selection mode, weight, the assessment scale). Grading parameters
can wait until the end of the term.</p>
</div>
{% endif %}
<p>
{{ roster_count }} active students on the roster;
{{ present_count }} available on {{ day.isoformat() }}
({{ optout_count }} opted out).
</p>
<form method="get" action="{{ url_for('instructor.home') }}">
<label>Working date:
<input type="date" name="date" value="{{ day.isoformat() }}"></label>
<button type="submit">Switch</button>
{% if day != today %}
<a href="{{ url_for('instructor.home') }}">back to today</a>
{% endif %}
{% if upcoming_class_days %}
<span class="muted">Class days:</span>
{% for d in upcoming_class_days %}
<a href="{{ url_for('instructor.home', date=d.isoformat()) }}"
{% if d == day %}style="font-weight: bold"{% endif %}>
{{ d.strftime("%a %b %-d") }}</a>
{% endfor %}
{% endif %}
</form>
<p>
<a href="{{ url_for('instructor.live', date=day.isoformat()) }}">Live cold call</a> ·
<a href="{{ url_for('instructor.day_edit', date_str=day.isoformat()) }}">Calls for {{ day.isoformat() }}</a> ·
<a href="{{ url_for('instructor.report') }}">Report</a> ·
<a href="{{ url_for('grades.view') }}">Grades</a> ·
<a href="{{ url_for('instructor.schedule') }}">Schedule</a> ·
<a href="{{ url_for('instructor.settings') }}">Settings</a>
</p>
<form method="post" action="{{ url_for('instructor.roster_sync') }}">
<button type="submit">Sync roster from Canvas now</button>
<span class="muted">The roster also refreshes automatically every
time you open this tool from Canvas.</span>
</form>
<h2>Printable list</h2>
{% if has_pending %}
<p>
<a href="{{ url_for('instructor.day_print', date_str=day.isoformat()) }}">
View the list for {{ day.isoformat() }}</a>
</p>
{% endif %}
<form method="post"
action="{{ url_for('instructor.generate', date_str=day.isoformat()) }}"
{% if resolved %}
onsubmit="return confirm('{{ resolved }} call(s) on {{ day.isoformat() }} already have recorded outcomes. Those are kept, but the unused list lines are replaced by a fresh draw. Continue?');"
{% endif %}>
<label>Number of calls (in cycle mode, the next batch of the
rotation): <input type="number" name="n" min="1" value="{{ present_count }}"></label>
<button type="submit">
{{ "Regenerate list (replaces the current one)" if has_pending
else "Generate list for " + day.isoformat() }}
</button>
</form>
{% if recent_days %}
<h2>Recent days</h2>
<table>
<tr><th>Date</th><th>Calls</th><th>Answered</th><th></th></tr>
{% for d, total, answered in recent_days %}
<tr>
<td>{{ d.isoformat() }}</td>
<td>{{ total }}</td>
<td>{{ answered or 0 }}</td>
<td>
<a href="{{ url_for('instructor.day_edit', date_str=d.isoformat()) }}">edit</a> ·
<a href="{{ url_for('instructor.day_print', date_str=d.isoformat()) }}">print</a>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}