Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
548 changes: 471 additions & 77 deletions bases/rsptx/assignment_server_api/routers/peer.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@
<li><a href='/ns/course/index'>Course Home</a></li>
<li><a href='/assignment/student/chooseAssignment'>Assignments</a></li>
<li><a href='/{{appname}}/assignments/practice'>Practice</a></li>
<li id="inst_peer_link"><a href='/{{appname}}/peer/instructor.html'>Peer Instruction (Instructor)</a></li>
<li><a href='/{{appname}}/peer/student.html'>Peer Instruction (Student)</a></li>
<li id="inst_peer_link"><a href='/assignment/peer/instructor'>Peer Instruction (Instructor)</a></li>
<li><a href='/assignment/peer/student'>Peer Instruction (Student)</a></li>
<li class="divider"></li>
{% if minimal_outside_links != 'True' %}
<li><a href='/{{appname}}/default/courses'>Change Course</a></li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,25 @@
display: none;
}

.oneq label.mchoice-option,
#pi-student-interface label.mchoice-option {
display: block !important;
margin-bottom: 4px;
}

.oneq button[name="compare"],
#pi-student-interface button[name="compare"] {
display: none !important;
}

.oneq button[aria-label="Close"],
.oneq .close,
.oneq .knowl-close,
#pi-student-interface button[aria-label="Close"],
#pi-student-interface .close {
display: none !important;
}

/* ------------------- */
/* Student's interface */
/* ------------------- */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@
<li class="divider"></li>
<li><a href="{{=URL('assignments','chooseAssignment')}}">Assignments</a></li>
<li><a href="{{=URL('assignments','practice')}}">Practice</a></li>
<li id="inst_peer_link"><a href='/runestone/peer/instructor.html'>Peer Instruction (Instructor)</a></li>
<li><a href='/runestone/peer/student.html'>Peer Instruction (Student)</a></li>
<li id="inst_peer_link"><a href='/assignment/peer/instructor'>Peer Instruction (Instructor)</a></li>
<li><a href='/assignment/peer/student'>Peer Instruction (Student)</a></li>
<li><a href="/assignment/instructor/invoice_request">Request Invoice</a></li>
<li class="divider"></li>
{{ if settings.academy_mode: }}
Expand Down
2 changes: 1 addition & 1 deletion components/rsptx/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ class AssignmentQuestion(Base, IdMixin):
activities_required = Column(
Integer
) # only reading assignments will have this populated
use_llm = Column(Web2PyBoolean, server_default="F")
async_mode = Column(String(20), server_default="standard")


AssignmentQuestionValidator: TypeAlias = sqlalchemy_to_pydantic(AssignmentQuestion) # type: ignore
Expand Down
4 changes: 2 additions & 2 deletions components/rsptx/templates/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@
<a class="dropdown-item" href="/ns/course/index">Course Home</a>
<a class="dropdown-item" href="/assignment/student/chooseAssignment">Assignments</a>
<a class="dropdown-item" href="/runestone/assignments/practice">Practice</a>
<a class="dropdown-item" href="/runestone/peer/student.html">Peer Instruction (Student)</a>
<a class="dropdown-item" href="/assignment/peer/student">Peer Instruction (Student)</a>
<a class="dropdown-item" href="/runestone/dashboard/studentreport">Progress Page</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/runestone/default/courses">Change Course</a>
<div class="dropdown-divider"></div>
{% if is_instructor %}
<a class="dropdown-item" href="/admin/instructor/menu">Instructor Dashboard</a>
<a class="dropdown-item" href="/runestone/peer/instructor.html">Peer Instruction (Instructor)</a>
<a class="dropdown-item" href="/assignment/peer/instructor">Peer Instruction (Instructor)</a>
<a class="dropdown-item" href="https://author.runestone.academy/author">Author Tools</a>
<a class="dropdown-item" href="/runestone/admin/manage_exercises">Editorial Page</a>
<a class="dropdown-item" href="/assignment/instructor/invoice_request">Request Invoice</a>
Expand Down
2 changes: 1 addition & 1 deletion components/rsptx/templates/admin/instructor/menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ <h4>Configure Practice</h4>
<h4>Reset Student Exam</h4>
<p>Reset timed exams for individual students</p>
</a>
<a href="/runestone/peer/instructor" class="menu-item">
<a href="/assignment/peer/instructor" class="menu-item">
<h4>Peer Instruction Dashboard</h4>
<p>Manage peer assignment reviews</p>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,23 @@ <h3>Question {{ current_qnum }} of {{ num_questions }}</h3>
</div>

<div id="pi-assignment-navigation">
{% if not is_last %}
<button type="submit" id="nextq" class="btn btn-info" name="next" value="Next">
Next Question
</button>
{% else %}
<div id="asyncBtnArea" style="display:inline-block;">
<button
type="button"
id="toggleAsyncBtn"
class="btn btn-info"
onclick="showAsyncConfirm()"
{% if peer_async_visible %}style="background-color:#a3d4ec; border-color:#a3d4ec; color:#fff; margin-right:4px;"{% else %}style="margin-right:4px;"{% endif %}
>
{% if peer_async_visible %}Undo After-Class Release{% else %}Release After-Class PI{% endif %}
</button>
</div>
{% endif %}
<button type="submit" id="restart" class="btn btn-danger" name="next" value="Reset">
Start Over
</button>
Expand Down Expand Up @@ -279,7 +293,41 @@ <h3>Question {{ current_qnum }} of {{ num_questions }}</h3>
var answerCount = 0;
var done = {{ 'true' if is_last else 'false' }};
if (done) {
document.getElementById("nextq").disabled = true;
document.getElementById("nextq") && (document.getElementById("nextq").disabled = true);
}

var asyncReleased = {{ 'true' if peer_async_visible else 'false' }};

function showAsyncConfirm() {
var area = document.getElementById("asyncBtnArea");
var msg = asyncReleased
? "Undo the after-class PI release?"
: "Release after-class PI questions to students?";
area.innerHTML = `
<span style="margin-right:6px; font-weight:bold;">${msg}</span>
<button type="button" class="btn btn-sm btn-default" onclick="confirmToggleAsync()" style="margin-right:4px;">Yes</button>
<button type="button" class="btn btn-sm btn-default" onclick="cancelAsyncConfirm()">Cancel</button>
`;
}

function cancelAsyncConfirm() {
var area = document.getElementById("asyncBtnArea");
var label = asyncReleased ? "Undo After-Class Release" : "Release After-Class PI";
var extraStyle = asyncReleased
? 'style="background-color:#a3d4ec; border-color:#a3d4ec; color:#fff; margin-right:4px;"'
: 'style="margin-right:4px;"';
area.innerHTML = `<button type="button" id="toggleAsyncBtn" class="btn btn-info" onclick="showAsyncConfirm()" ${extraStyle}>${label}</button>`;
}

async function confirmToggleAsync() {
var resp = await fetch("/assignment/peer/api/toggle_async", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ assignment_id: {{ assignment_id }} }),
});
var data = await resp.json();
asyncReleased = data.peer_async_visible;
cancelAsyncConfirm();
}
</script>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% extends "_base.html" %}

{% block title %}Peer Instruction - Extra Info{% endblock %}

{% block content %}
<div style="padding: 20px;">
<h2>Percent Correct for {{ current_question.name }}</h2>
<p><span id="pct_corr" style="font-size: 2em; font-weight: bold;"></span>%</p>
</div>
{% endblock %}

{% block js %}
<script>
var startTime = new Date().toUTCString();
var startTime2 = null;

async function updatePctCorrect() {
let data = {
div_id: "{{ current_question.name }}",
course_name: "{{ course.course_name }}",
start_time: startTime,
};
if (startTime2 !== null) {
data.start_time = startTime2;
}
let resp = await fetch("/assignment/peer/api/percent_correct", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify(data),
});
let spec = await resp.json();
document.getElementById("pct_corr").innerHTML = spec.pct_correct ?? "--";
}

document.addEventListener("DOMContentLoaded", function () {
setInterval(updatePctCorrect, 2000);
});
</script>
{% endblock %}
Loading
Loading