Merge branch 'master' of ssh://161.35.163.87:/home/django/rad

This commit is contained in:
Ross
2021-08-08 10:12:01 +01:00
17 changed files with 256 additions and 48 deletions
+16
View File
@@ -485,4 +485,20 @@ td.user-answer-score-2::after {
.notes .complete {
color: darkslategray;
}
table.longs .user-answer-score-4, table.longs .user-answer-score-4\.5 {
color: red
}
table.longs .user-answer-score-5\.0, table.longs .user-answer-score-5\.5 {
color: yellow
}
table.longs .user-answer-score-6\.0, table.longs .user-answer-score-6\.5 {
color: yellowgreen
}
table.longs .user-answer-score-7\.0, table.longs .user-answer-score-7\.5 {
color: green
}
+1 -1
View File
@@ -17,7 +17,7 @@
Exam active: <input type="checkbox" id="exam-active-switch" {% if exam.active %}checked{% endif %}> <span class="help-text">[When checked the exam will be available to take in the test system]</span>
</div>
<div class="parent-help" title="Click to enable / disable the exam results">
Publish results: <input type="checkbox" id="exam-publish-results-switch" {% if exam.publish_results %}checked{% endif %}> <span class="help-text">[When checked the exam results will be available on this site]</span>
Publish results: <input type="checkbox" id="exam-publish-results-switch" {% if exam.publish_results %}checked{% endif %}> <span class="help-text">[When checked the exam results will be available to users on this site]</span>
</div>
{% endif %}
<p><a href="{% url 'anatomy:mark_overview' pk=exam.pk %}"><button>Mark exam</button></a></p>
+5 -3
View File
@@ -22,7 +22,7 @@
<div id="stats-plot">{{plot|safe}}</div>
</div>
<div>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<tr>
<th>Candidate ID</th>
<th>Score</th>
@@ -36,8 +36,9 @@
</table>
</div>
<div>
<h3>Answers as a table</h3>
<table>
<h3>Results as a table</h3>
<table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Candidate</th>
{% for cid in cids %}
@@ -45,6 +46,7 @@
{% endfor %}
</tr>
</thead>
{% for question in questions %}
<tr>
<td>Question {{forloop.counter}}</td>
+20 -1
View File
@@ -1,6 +1,6 @@
import django_filters
from .models import Long, LongSeries, Exam
from .models import Long, LongSeries, Exam, CidUserAnswer
from django.contrib.auth.models import User
def get_exams(request):
@@ -51,4 +51,23 @@ class LongSeriesFilter(django_filters.FilterSet):
#queryset = queryset.filter(open_access=True) | queryset.filter(author__id=request.user.id)
queryset = queryset.filter(author__id=request.user.id)
super(LongSeriesFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)
pass
class UserAnswerFilter(django_filters.FilterSet):
class Meta:
model = CidUserAnswer
fields = (
"cid",
#"normal",
#"answer",
#"answer_compare",
"exam",
"created",
"updated",
)
def __init__(self, data=None, queryset=None, prefix=None, strict=None, user=None, request=None):
if not request.user.groups.filter(name="long_checker").exists():
queryset = queryset.filter(open_access=True) | queryset.filter(author__id=request.user.id)
super(UserAnswerFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)
pass
+26 -2
View File
@@ -1,7 +1,7 @@
import django_tables2 as tables
from django_tables2.utils import A
from .models import Long, LongSeries
from .models import Long, LongSeries, CidUserAnswer
from django.utils.html import format_html
@@ -111,4 +111,28 @@ class LongSeriesTable(tables.Table):
def render_popup(self, value, record):
print(self)
return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk)
return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk)
class UserAnswerTable(tables.Table):
select = tables.CheckBoxColumn(accessor=("pk"))
delete = tables.LinkColumn(
"longs:user_answer_delete", text="Delete", args=[A("pk")], orderable=False
)
view = tables.LinkColumn('longs:user_answer_view',
text='View',
args=[A('pk')],
orderable=False)
class Meta:
model = CidUserAnswer
template_name = "django_tables2/bootstrap4.html"
fields = (
"cid",
"question",
#"normal",
#"answer",
#"answer_compare",
"exam",
"created",
"updated",
)
@@ -0,0 +1,19 @@
{% extends 'rapids/base.html' %}
{% block content %}
CID: {{ciduseranswer.cid}}
<h3>Question</h3>
{{ciduseranswer.question}}
<h3>Answers</h3>
{{ciduseranswer.model_observations}}<br/>
{{ciduseranswer.model_interpretation}}<br/>
{{ciduseranswer.model_principle_diagnosis}}<br/>
{{ciduseranswer.model_differential_diagnosis}}<br/>
{{ciduseranswer.model_management}}<br/>
{% endblock content %}
+2
View File
@@ -62,6 +62,8 @@
</div>
<a href="{% url 'longs:exam_json' pk=exam.pk %}">JSON</a>
<a href="{% url 'longs:exam_json_recreate' pk=exam.pk %}">Refresh JSON cache</a>
<a href="{% url 'longs:refresh_exam_question_json' pk=exam.pk %}">Refresh Question JSON cache</a>
<button id='button-open-access'>Make questions open access</button>
<button id='button-closed-access'>Make questions closed access</button>
+28 -26
View File
@@ -16,13 +16,13 @@
<div id="stats-block">
<h3>Stats</h3>
Candidates: {{cids|length}}<br />
Max score: {{max_score}}<br/>
Max score: {{max_score}}<br />
Mean: {{mean}}, Median {{median}}, Mode {{mode}}
<div id="stats-plot">{{plot|safe}}</div>
</div>
<div>
<table>
<div class="col-sm-4">
<table class="table table-dark table-striped table-hover table-sm">
<tr>
<th>Candidate ID</th>
<th>Score</th>
@@ -35,30 +35,32 @@
{% endfor %}
</table>
</div>
<div>
<h3>Answers as a table</h3>
<table>
<tr>
<th>Candidate</th>
{% for cid in cids %}
<th>{{cid}}</th>
{% endfor %}
<div class="col">
<h3>Results as a table</h3>
<table class="longs table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Candidate</th>
{% for cid in cids %}
<th>{{cid}}</th>
{% endfor %}
</tr>
{% for question in questions %}
<tr>
<td>Question {{forloop.counter}}</td>
{% for ans, score in by_question|get_item:question %}
<td class="user-answer-score-{{score}}" title="answer score: {{score}}">{{score}}</td>
</tr>
</thead>
{% for question in questions %}
<tr>
<td>Question {{forloop.counter}}</td>
{% for ans, score in by_question|get_item:question %}
<td class="user-answer-score-{{score}}" title="answer score: {{score}}">{{score}}</td>
{% endfor %}
</tr>
{% endfor %}
</tr>
{% endfor %}
<tr>
<td>Score:</td>
{% for score in user_scores_list %}
<td>{{score}}</td>
{% endfor %}
<tr>
<tr>
<td>Score:</td>
{% for score in user_scores_list %}
<td>{{score}}</td>
{% endfor %}
<tr>
</table>
</div>
+56
View File
@@ -0,0 +1,56 @@
{% extends 'longs/base.html' %}
{% load render_table from django_tables2 %}
{% block css %}
{% endblock %}
{% block content %}
<div id="view-filter-options">
<h3>Filter Long </h3>
<form action="" method="get">
{{ filter.form }}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</div>
<button id="delete-all-button">Delete all</button>
{% render_table table %}
{% endblock %}
{% block js %}
<script>
$(document).ready(function () {
//$("thead input:checked").each((n, el) => {
answer_ids = [];
$("tbody input:checked").each((n, el) => {
answer_ids.push(el.value);
})
$("#delete-all-button").on("click", function () {
$.ajax({
url: "{% url 'longs:user_answer_delete_multiple' %}",
data: {
csrfmiddlewaretoken: "{{ csrf_token }}",
answer_ids: JSON.stringify(answer_ids) // true if checked else false
},
type: "POST",
dataType: "json",
})
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
.done(function (data) {
console.log(data);
if (data.status == "success") {
toastr.info('Deleted.')
}
// show some message according to the response.
// For eg. A message box showing that the status has been changed
})
.always(function () {
console.log('[Done]');
})
})
});
</script>
{% endblock %}
+5
View File
@@ -39,6 +39,7 @@ urlpatterns = [
),
path("exam/<int:pk>/", views.LongExamViews.exam_overview, name="exam_overview"),
path("exam/<int:pk>/json_edit", views.LongExamViews.exam_json_edit, name="exam_json_edit"),
path("exam/<int:pk>/question_json_refresh", views.refresh_exam_question_json, name="refresh_exam_question_json"),
path("exam/<int:pk>/scores", views.exam_scores_cid, name="exam_scores_cid"),
path(
"exam/<int:pk>/scores/<int:sk>/",
@@ -104,4 +105,8 @@ urlpatterns = [
name="long_series_update",
),
path("<int:pk>/add_note", views.AddNote.as_view(), name="long_add_note"),
path("user_answers/", views.UserAnswerTableView.as_view(), name="user_answer_table_view"),
path("user_answers/<int:pk>", views.UserAnswerView.as_view(), name="user_answer_view"),
path("user_answers/<int:pk>/delete", views.UserAnswerDelete.as_view(), name="user_answer_delete"),
path("user_answers/delete", views.user_answer_delete_multiple, name="user_answer_delete_multiple"),
]
+45 -3
View File
@@ -8,6 +8,8 @@ from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.detail import DetailView
from generic.mixins import SuperuserRequiredMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic import ListView
@@ -33,8 +35,8 @@ from .models import (
Exam,
CidUserAnswer,
)
from .tables import LongTable, LongSeriesTable
from .filters import LongFilter, LongSeriesFilter
from .tables import LongTable, LongSeriesTable, UserAnswerTable
from .filters import LongFilter, LongSeriesFilter, UserAnswerFilter
from django_tables2 import SingleTableView, SingleTableMixin
from django_filters.views import FilterView
@@ -998,4 +1000,44 @@ def question_json_recreate(request, pk):
#question.get_question_json()
return redirect("longs:question_detail", pk=pk)
return redirect("longs:question_detail", pk=pk)
class UserAnswerView(LoginRequiredMixin, DetailView):
model = CidUserAnswer
class UserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = CidUserAnswer
table_class = UserAnswerTable
template_name = "longs/question_view.html"
filterset_class = UserAnswerFilter
class UserAnswerDelete(SuperuserRequiredMixin, DeleteView):
model = CidUserAnswer
success_url = reverse_lazy("longs:user_answer_table_view")
@user_passes_test(lambda u: u.is_superuser)
def user_answer_delete_multiple(request):
if request.is_ajax():
answer_ids = json.loads(request.POST["answer_ids"])
# We could probably delete them all at once....
for id in answer_ids:
CidUserAnswer.objects.get(pk=id).delete()
return HttpResponseRedirect(request.path_info)
return HttpResponse("/")
@user_passes_test(lambda u: u.is_superuser)
def refresh_exam_question_json(request, pk):
exam = Exam.objects.get(pk=pk)
for question in exam.exam_questions.all():
question.recreate_json = True
question.save()
return redirect("longs:exam_overview", pk=pk)
+4 -3
View File
@@ -16,7 +16,7 @@
</div>
</div>
<div>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<tr>
<th>Candidate ID</th>
<th>Score</th>
@@ -31,13 +31,14 @@
</div>
<div>
<h3>Answers as a table</h3>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Candidate</th>
{% for cid in cids %}
<th>{{cid}}</th>
{% endfor %}
</thead>
</tr>
{% for question in questions %}
<tr>
+4 -2
View File
@@ -22,7 +22,7 @@
<div id="stats-plot">{{plot|safe}}</div>
</div>
<div>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<tr>
<th>Candidate ID</th>
<th>Score</th>
@@ -37,7 +37,8 @@
</div>
<div>
<h3>Answers as a table</h3>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Candidate</th>
{% for cid in cids %}
@@ -45,6 +46,7 @@
{% endfor %}
</tr>
</thead>
{% for question in questions %}
<tr>
<td>Question {{forloop.counter}}</td>
+2 -2
View File
@@ -66,7 +66,7 @@ urlpatterns = [
name="rapid_update",
),
path("<int:pk>/add_note", views.AddNote.as_view(), name="rapid_add_note"),
path("user_answers/", views.RapidUserAnswerTableView.as_view(), name="rapid_user_answer_table_view"),
path("user_answers/<int:pk>", views.RapidUserAnswerView.as_view(), name="rapid_user_answer_view"),
path("user_answers/", views.UserAnswerTableView.as_view(), name="rapid_user_answer_table_view"),
path("user_answers/<int:pk>", views.UserAnswerView.as_view(), name="rapid_user_answer_view"),
path("user_answers/<int:pk>/delete", views.UserAnswerDelete.as_view(), name="rapid_user_answer_delete"),
]
+2 -2
View File
@@ -985,11 +985,11 @@ class ExamUpdate(
return super().form_valid(form)
class RapidUserAnswerView(LoginRequiredMixin, DetailView):
class UserAnswerView(LoginRequiredMixin, DetailView):
model = CidUserAnswer
class RapidUserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView):
class UserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = CidUserAnswer
table_class = RapidUserAnswerTable
template_name = "rapids/rapid_question_view.html"
+5 -3
View File
@@ -16,7 +16,7 @@
</div>
</div>
<div>
<table>
<table class="table table-dark table-striped table-hover table-sm">
<tr>
<th>Candidate ID</th>
<th>Score</th>
@@ -30,8 +30,9 @@
</table>
</div>
<div>
<h3>Answers as a table</h3>
<table>
<h3>Results as a table</h3>
<table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr>
<th>Candidate</th>
{% for cid in cids %}
@@ -39,6 +40,7 @@
{% endfor %}
</tr>
</thead>
{% for question in questions %}
<tr>
<td>Question {{forloop.counter}}</td>
+16
View File
@@ -485,4 +485,20 @@ td.user-answer-score-2::after {
.notes .complete {
color: darkslategray;
}
table.longs .user-answer-score-4, table.longs .user-answer-score-4\.5 {
color: red
}
table.longs .user-answer-score-5\.0, table.longs .user-answer-score-5\.5 {
color: yellow
}
table.longs .user-answer-score-6\.0, table.longs .user-answer-score-6\.5 {
color: yellowgreen
}
table.longs .user-answer-score-7\.0, table.longs .user-answer-score-7\.5 {
color: green
}