start updating physics

This commit is contained in:
Ross
2021-12-10 23:02:19 +00:00
parent d9c6f464ee
commit ea52d573fe
7 changed files with 546 additions and 273 deletions
+2
View File
@@ -27,6 +27,8 @@ class CidUserAnswerForm(ModelForm):
super(CidUserAnswerForm, self).__init__(*args, **kwargs)
#self.fields['answer'].required = False
# This should be made generic?
class ExamForm(ModelForm):
class Meta:
@@ -0,0 +1,23 @@
{% extends 'base.html' %}
{% block content %}
CID: {{cid}}
<h2>Exam: {{exam.name}}</h2>
<div><p>Questions</p></div>
{{answer_count}} out of {{exam_length}} questions answered. Click to go to question.
<div class="sba-finish-list">
{% for question, answer in question_answer_tuples %}
<a href="{% url 'physics:exam_take' pk=exam.id sk=forloop.counter0 cid=cid %}"><button {% if not answer %}class="unanswered"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
{% endfor %}
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(() => {
})
</script>
{% endblock %}
+25
View File
@@ -0,0 +1,25 @@
{% extends 'base.html' %}
{% block content %}
<h2>{{exam.name}}</h2>
Enter your CID in the below box.<br />
<input id="cid-box" type="text" value="Candidate ID">
<button>Start exam</button>
<script type="text/javascript">
$(document).ready(function () {
$("button").click(() => {
let cid = $("#cid-box").val();
if (Number.isInteger(parseInt(cid))) {
window.location.replace("{% url 'physics:exam_take' pk=exam.pk sk=0 cid='0000000' %}".replace("0000000", cid));
} else {
alert("Please enter a valid Candidate ID (CID).")
}
});
});
</script>
{% endblock %}
+93 -269
View File
@@ -1,280 +1,104 @@
{% extends 'physics/base.html' %}
{% extends 'base.html' %}
{% block content %}
<div class="physics">
<h1>Exam: {{ exam.name }}</h1>
<div class="alert alert-info" role="alert">
<details>
<summary title="click to open"><h3>Instructions</h3></summary>
<p>
Please make sure you submit answers before closing or navigating from this page (as otherwise they will not
be saved). The submit button is at the bottom of the page.
</p>
<p>
Click on a question to toggle between true / false.
</p>
<p>
Ensure your candidate number is entered in the below box.
</p>
</details>
</div>
<div>
Candidate number: <input type="number" id="cid" name="cid" title="please enter your candidate number">
</div>
<h2>Questions</h2>
{% autoescape off %}
<ol id="full-question-list-physics">
{% for question in questions.all %}
<li>
{{ question.stem }}
<ol type="a" class="abcde">
<li>
<span class="question-text">{{ question.a }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question a" id="{{question.pk}}-a-checkbox" data-q="{{question.pk}}"
data-a="a">
<div class="slider round a"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.b }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question b" id="{{question.pk}}-b-checkbox" data-q="{{question.pk}}"
data-a="b">
<div class="slider round b"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.c }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question c" id="{{question.pk}}-c-checkbox" data-q="{{question.pk}}"
data-a="c">
<div class="slider round c"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.d }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question d" id="{{question.pk}}-d-checkbox" data-q="{{question.pk}}"
data-a="d">
<div class="slider round d"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.e }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question e" id="{{question.pk}}-e-checkbox" data-q="{{question.pk}}"
data-a="e">
<div class="slider round e"></div>
</label>
</li>
</ol>
</li>
{% endfor %}
</ol>
{% endautoescape %}
<button id="submit">Submit answers</button>
CID: {{cid}}
<div class="no-select">
<h2>Question [{{pos|add:1}}/{{exam_length}}]</h2>
{% if exam.publish_results %}
<div class="alert alert-primary" role="alert">
Exam is in review mode. Add question feedback <a href="#" onclick="return window.create_popup_window({% url 'feedback_create' question_type='physicss' pk=question.pk %}">here</a>.
</div>
{% endif %}
<div>
<p>{{question.stem|safe}}</p>
</div>
<ul class="physics-answer-list">
<li data-ans="a">{{question.a_answer|safe}}</li>
<li data-ans="b">{{question.b_answer|safe}}</li>
<li data-ans="c">{{question.c_answer|safe}}</li>
<li data-ans="d">{{question.d_answer|safe}}</li>
<li data-ans="e">{{question.e_answer|safe}}</li>
</ul>
<script type="text/javascript">
<div>
window.answers_submitted = false;
$(document).ready(function () {
console.log($(".question-text"));
$(".question-text").click(function (evt) {
$(evt.target).parent().find("input").click();
});
<form method="POST" class="post-form">{% csrf_token %}
<div class="form-contents">
{{form}}
</div>
$("#submit").click(() => {
let cid = $("#cid").val()
if (cid == "") {
alert("You need to enter a valid candidate number.");
$("#cid").focus();
return
}
{% if previous > -1 %}
<button type="submit" name="previous" class="save btn btn-default">Previous</button>
{% endif %}
{% if next %}
<button type="submit" name="next" class="save btn btn-default">Next</button>
{% else %}
{% if not exam.publish_results %}
<button type="submit" name="save" class="save btn btn-default">Save</button>
{% endif %}
{% endif %}
answers = [];
{% if exam.publish_results and question.feedback %}
<h3>Feedback</h3>
<p>{{question.feedback|safe}}</p>
{% endif %}
<br />
<button type="submit" name="finish" class="save btn btn-default">Overview</button>
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
</form>
</div>
</div>
<h4>Questions</h4>
<div id="menu-list">
$("input.question").each((n, el) => {
answers.push([el.dataset.q, el.dataset.a, el.checked]);
})
ans = {
cid: parseInt(cid),
eid: {{exam.pk}},
ans: answers
}
let json = {
eid: {{exam.pk}},
answers: JSON.stringify([ans]),
};
postAnswers(json);
})
});
window.onbeforeunload = confirmExit;
function confirmExit() {
if (!window.answers_submitted) {
return "You have attempted to leave this page. Are you sure?";
}
}
function postAnswers(ans) {
console.log(ans);
$.post("{% url 'physics:exam_answers_submit' %}", ans, null, "json").done((data) => {
console.log(data);
if (data.success) {
let ret = confirm(
`Answers sucessfully submitted. Click OK to finish the exam.`
);
if (ret) {
//window.saveSession();
$("#exams-button-link").remove();
$("#submit").after('<button id="exams-button-link">Return to exam list</button>');
$("#exams-button-link").click(() => {
window.location.href = "{% url 'physics:active_exams' %}";
})
window.answers_submitted = true;
} else {
}
} else {
alert(`Error submitting answers: ${data.error}`);
}
});
// $.post( "http://localhost:8000/submit_answers", JSON.stringify(ans));
}
</script>
<style>
.truefalse-switch {
position: relative;
display: inline-block;
width: 100px;
height: 20px;
margin: 0px;
margin-bottom: -5px;
float: right;
}
.truefalse-switch input {
display: none;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 4, 255, 0.5);
-webkit-transition: .4s;
transition: .4s;
border-radius: 20px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border-radius: 50%;
}
input:checked+.slider {
background-color: rgba(162, 0, 255, 0.5);
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(68px);
}
/*------ ADDED CSS ---------*/
.slider:after {
color: white;
display: block;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
font-size: 10px;
font-family: Verdana, sans-serif;
}
.a.slider:after {
content: 'a) FALSE';
}
.b.slider:after {
content: 'b) FALSE';
}
.c.slider:after {
content: 'c) FALSE';
}
.d.slider:after {
content: 'd) FALSE';
}
.e.slider:after {
content: 'e) FALSE';
}
input:checked+.slider.a:after {
content: 'a )TRUE';
}
input:checked+.slider.b:after {
content: 'b )TRUE';
}
input:checked+.slider.c:after {
content: 'c )TRUE';
}
input:checked+.slider.d:after {
content: 'd )TRUE';
}
input:checked+.slider.e:after {
content: 'e )TRUE';
}
.question-text {
display: inline-block;
vertical-align: text-top;
clear: both;
width: 600px;
}
.abcde {
clear: both;
}
</style>
</div>
{% endblock %}
{% block js %}
<script>
$(document).ready(() => {
/* beautify ignore:start */
{% if not exam.publish_results %}
$("ul.physics-answer-list li").each((n, el) => {
$(el).click((e) => {
console.log(1, el.dataset.ans);
$("#id_answer").val(el.dataset.ans);
$("ul.physics-answer-list li").removeClass("selected");
$(el).addClass("selected")
})
})
{% else %}
$("ul.physics-answer-list li[data-ans='{{question.best_answer}}']").addClass("correct");
{% endif %}
{% if saved_answer %}
$("ul.physics-answer-list li[data-ans='{{saved_answer}}']").addClass("selected"); {% endif %}
for (let i = 0; i < {{ exam_length }}; i++) {
$("#menu-list").append($(
`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`));
}
$("button.question-menu-item").on("click", (e) => {
console.log(e.currentTarget.dataset.qn);
document.getElementById("goto-button").value = e.currentTarget.dataset.qn;
$("#goto-button").click();
});
/* beautify ignore:end */
})
</script>
{% endblock %}
{% block css %}
<style type="text/css">
.form-contents {
display: none;
}
.selected {
border: 1px solid purple;
}
</style>
{% endblock %}
@@ -0,0 +1,280 @@
{% extends 'physics/base.html' %}
{% block content %}
<div class="physics">
<h1>Exam: {{ exam.name }}</h1>
<div class="alert alert-info" role="alert">
<details>
<summary title="click to open"><h3>Instructions</h3></summary>
<p>
Please make sure you submit answers before closing or navigating from this page (as otherwise they will not
be saved). The submit button is at the bottom of the page.
</p>
<p>
Click on a question to toggle between true / false.
</p>
<p>
Ensure your candidate number is entered in the below box.
</p>
</details>
</div>
<div>
Candidate number: <input type="number" id="cid" name="cid" title="please enter your candidate number">
</div>
<h2>Questions</h2>
{% autoescape off %}
<ol id="full-question-list-physics">
{% for question in questions.all %}
<li>
{{ question.stem }}
<ol type="a" class="abcde">
<li>
<span class="question-text">{{ question.a }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question a" id="{{question.pk}}-a-checkbox" data-q="{{question.pk}}"
data-a="a">
<div class="slider round a"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.b }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question b" id="{{question.pk}}-b-checkbox" data-q="{{question.pk}}"
data-a="b">
<div class="slider round b"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.c }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question c" id="{{question.pk}}-c-checkbox" data-q="{{question.pk}}"
data-a="c">
<div class="slider round c"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.d }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question d" id="{{question.pk}}-d-checkbox" data-q="{{question.pk}}"
data-a="d">
<div class="slider round d"></div>
</label>
</li>
<li>
<span class="question-text">{{ question.e }}:</span> <label class="truefalse-switch"> <input
type="checkbox" class="question e" id="{{question.pk}}-e-checkbox" data-q="{{question.pk}}"
data-a="e">
<div class="slider round e"></div>
</label>
</li>
</ol>
</li>
{% endfor %}
</ol>
{% endautoescape %}
<button id="submit">Submit answers</button>
</div>
<script type="text/javascript">
window.answers_submitted = false;
$(document).ready(function () {
console.log($(".question-text"));
$(".question-text").click(function (evt) {
$(evt.target).parent().find("input").click();
});
$("#submit").click(() => {
let cid = $("#cid").val()
if (cid == "") {
alert("You need to enter a valid candidate number.");
$("#cid").focus();
return
}
answers = [];
$("input.question").each((n, el) => {
answers.push([el.dataset.q, el.dataset.a, el.checked]);
})
ans = {
cid: parseInt(cid),
eid: {{exam.pk}},
ans: answers
}
let json = {
eid: {{exam.pk}},
answers: JSON.stringify([ans]),
};
postAnswers(json);
})
});
window.onbeforeunload = confirmExit;
function confirmExit() {
if (!window.answers_submitted) {
return "You have attempted to leave this page. Are you sure?";
}
}
function postAnswers(ans) {
console.log(ans);
$.post("{% url 'physics:exam_answers_submit' %}", ans, null, "json").done((data) => {
console.log(data);
if (data.success) {
let ret = confirm(
`Answers sucessfully submitted. Click OK to finish the exam.`
);
if (ret) {
//window.saveSession();
$("#exams-button-link").remove();
$("#submit").after('<button id="exams-button-link">Return to exam list</button>');
$("#exams-button-link").click(() => {
window.location.href = "{% url 'physics:active_exams' %}";
})
window.answers_submitted = true;
} else {
}
} else {
alert(`Error submitting answers: ${data.error}`);
}
});
// $.post( "http://localhost:8000/submit_answers", JSON.stringify(ans));
}
</script>
<style>
.truefalse-switch {
position: relative;
display: inline-block;
width: 100px;
height: 20px;
margin: 0px;
margin-bottom: -5px;
float: right;
}
.truefalse-switch input {
display: none;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 4, 255, 0.5);
-webkit-transition: .4s;
transition: .4s;
border-radius: 20px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 2px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
border-radius: 50%;
}
input:checked+.slider {
background-color: rgba(162, 0, 255, 0.5);
}
input:focus+.slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked+.slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(68px);
}
/*------ ADDED CSS ---------*/
.slider:after {
color: white;
display: block;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
font-size: 10px;
font-family: Verdana, sans-serif;
}
.a.slider:after {
content: 'a) FALSE';
}
.b.slider:after {
content: 'b) FALSE';
}
.c.slider:after {
content: 'c) FALSE';
}
.d.slider:after {
content: 'd) FALSE';
}
.e.slider:after {
content: 'e) FALSE';
}
input:checked+.slider.a:after {
content: 'a )TRUE';
}
input:checked+.slider.b:after {
content: 'b )TRUE';
}
input:checked+.slider.c:after {
content: 'c )TRUE';
}
input:checked+.slider.d:after {
content: 'd )TRUE';
}
input:checked+.slider.e:after {
content: 'e )TRUE';
}
.question-text {
display: inline-block;
vertical-align: text-top;
clear: both;
width: 600px;
}
.abcde {
clear: both;
}
</style>
{% endblock %}
+4 -1
View File
@@ -13,7 +13,10 @@ urlpatterns = [
#path("question/<int:pk>/update", views.QuestionUpdate.as_view(), name="anatomy_question_update"),
#path("exam/<int:pk>/<int:sk>/mark", views.mark, name="mark"),
#path("exam/<int:pk>/mark", views.mark_overview, name="mark_overview"),
path("exam/<int:pk>/take", views.exam_take, name="exam_take"),
path("exam/<int:pk>/take_old", views.exam_take_old, name="exam_take_old"),
path("exam/<int:pk>/<int:sk>/<str:cid>/take", views.exam_take, name="exam_take"),
path("exam/<int:pk>/start", views.exam_start, name="exam_start"),
path("exam/<int:pk>/<str:cid>/finish", views.exam_finish, name="exam_finish"),
path("exam/<int:pk>/", views.PhysicsExamViews.exam_overview, name="exam_overview"),
path("exam/<int:pk>/scores", views.exam_scores_cid,
name="exam_scores_cid"),
+119 -3
View File
@@ -46,7 +46,7 @@ from rest_framework.pagination import PageNumberPagination
from django.core.exceptions import PermissionDenied
from .forms import ExamForm
from .forms import CidUserAnswerForm, ExamForm
from .decorators import user_is_author_or_physics_checker, user_is_exam_author_or_physics_checker
class AuthorOrCheckerRequiredMixin(object):
@@ -240,7 +240,7 @@ def exam_scores_cid_user(request, pk, sk):
)
def exam_take(request, pk):
def exam_take_old(request, pk):
exam = get_object_or_404(Exam, pk=pk)
@@ -251,13 +251,129 @@ def exam_take(request, pk):
return render(
request,
"physics/exam_take.html",
"physics/exam_take_old.html",
{
"exam": exam,
"questions": questions,
},
)
def exam_start(request, pk):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
raise Http404("Exam not found")
return render(
request,
"physics/exam_start.html",
{
"exam": exam,
},
)
def exam_finish(request, pk, cid):
exam = get_object_or_404(Exam, pk=pk)
questions = exam.exam_questions.all()
answers = CidUserAnswer.objects.filter(
cid=cid, exam=exam)
answer_question_map = {}
for ans in answers:
answer_question_map[ans.question] = ans
question_answer_tuples = []
answer_count = 0
for q in questions:
if q in answer_question_map and answer_question_map[q].answer:
question_answer_tuples.append((q, answer_question_map[q]))
answer_count += 1
else:
question_answer_tuples.append((q, None))
if not exam.active:
raise Http404("Exam not found")
return render(
request,
"physics/exam_finish.html",
{
"exam": exam,
"cid": cid,
"question_answer_tuples": question_answer_tuples,
"answer_count": answer_count,
"exam_length": len(questions),
},
)
def exam_take(request, pk, sk, cid):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
raise Http404("Exam not found")
question = exam.exam_questions.all()[sk]
exam_length = len(exam.exam_questions.all())
pos = exam.get_question_index(question)
answer = question.cid_user_answers.filter(
cid=cid, exam=exam).first()
if request.method == "POST":
if answer:
form = CidUserAnswerForm(request.POST, instance=answer)
else:
form = CidUserAnswerForm(request.POST)
if form.is_valid():
answer = form.save(commit=False)
answer.cid = cid
answer.question = question
answer.exam = exam
# answer.published_date = timezone.now()
answer.save()
if "next" in request.POST:
return redirect("sbas:exam_take", pk=pk, sk=pos + 1, cid=cid)
elif "previous" in request.POST:
return redirect("sbas:exam_take", pk=pk, sk=pos - 1, cid=cid)
elif "finish" in request.POST:
return redirect("sbas:exam_finish", pk=pk, cid=cid)
elif "goto" in request.POST:
return redirect("sbas:exam_take", pk=pk, sk=int(request.POST.get("goto")), cid=cid)
else:
form = CidUserAnswerForm(instance=answer)
previous = -1
if sk > 0:
previous = sk - 1
next = sk + 1
if sk == exam_length - 1:
next = False
saved_answer = False
if answer is not None:
saved_answer = answer.answer
return render(
request,
"physics/exam_take.html",
{
"form": form,
"cid": cid,
"exam": exam,
"question": question,
"next": next,
"previous": previous,
"exam_length": exam_length,
"pos": pos,
"saved_answer": saved_answer,
},
)
def loadJsonAnswer(answer):
# As access is not restricted make sure the data appears valid