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
@@ -486,3 +486,19 @@ td.user-answer-score-2::after {
.notes .complete { .notes .complete {
color: darkslategray; 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> 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>
<div class="parent-help" title="Click to enable / disable the exam results"> <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> </div>
{% endif %} {% endif %}
<p><a href="{% url 'anatomy:mark_overview' pk=exam.pk %}"><button>Mark exam</button></a></p> <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 id="stats-plot">{{plot|safe}}</div>
</div> </div>
<div> <div>
<table> <table class="table table-dark table-striped table-hover table-sm">
<tr> <tr>
<th>Candidate ID</th> <th>Candidate ID</th>
<th>Score</th> <th>Score</th>
@@ -36,8 +36,9 @@
</table> </table>
</div> </div>
<div> <div>
<h3>Answers as a table</h3> <h3>Results as a table</h3>
<table> <table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr> <tr>
<th>Candidate</th> <th>Candidate</th>
{% for cid in cids %} {% for cid in cids %}
@@ -45,6 +46,7 @@
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
{% for question in questions %} {% for question in questions %}
<tr> <tr>
<td>Question {{forloop.counter}}</td> <td>Question {{forloop.counter}}</td>
+20 -1
View File
@@ -1,6 +1,6 @@
import django_filters import django_filters
from .models import Long, LongSeries, Exam from .models import Long, LongSeries, Exam, CidUserAnswer
from django.contrib.auth.models import User from django.contrib.auth.models import User
def get_exams(request): def get_exams(request):
@@ -52,3 +52,22 @@ class LongSeriesFilter(django_filters.FilterSet):
queryset = 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) super(LongSeriesFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)
pass 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
+25 -1
View File
@@ -1,7 +1,7 @@
import django_tables2 as tables import django_tables2 as tables
from django_tables2.utils import A from django_tables2.utils import A
from .models import Long, LongSeries from .models import Long, LongSeries, CidUserAnswer
from django.utils.html import format_html from django.utils.html import format_html
@@ -112,3 +112,27 @@ class LongSeriesTable(tables.Table):
def render_popup(self, value, record): def render_popup(self, value, record):
print(self) 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> </div>
<a href="{% url 'longs:exam_json' pk=exam.pk %}">JSON</a> <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: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-open-access'>Make questions open access</button>
<button id='button-closed-access'>Make questions closed access</button> <button id='button-closed-access'>Make questions closed access</button>
+8 -6
View File
@@ -16,13 +16,13 @@
<div id="stats-block"> <div id="stats-block">
<h3>Stats</h3> <h3>Stats</h3>
Candidates: {{cids|length}}<br /> Candidates: {{cids|length}}<br />
Max score: {{max_score}}<br/> Max score: {{max_score}}<br />
Mean: {{mean}}, Median {{median}}, Mode {{mode}} Mean: {{mean}}, Median {{median}}, Mode {{mode}}
<div id="stats-plot">{{plot|safe}}</div> <div id="stats-plot">{{plot|safe}}</div>
</div> </div>
<div> <div class="col-sm-4">
<table> <table class="table table-dark table-striped table-hover table-sm">
<tr> <tr>
<th>Candidate ID</th> <th>Candidate ID</th>
<th>Score</th> <th>Score</th>
@@ -35,9 +35,10 @@
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<div> <div class="col">
<h3>Answers as a table</h3> <h3>Results as a table</h3>
<table> <table class="longs table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr> <tr>
<th>Candidate</th> <th>Candidate</th>
{% for cid in cids %} {% for cid in cids %}
@@ -45,6 +46,7 @@
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
{% for question in questions %} {% for question in questions %}
<tr> <tr>
<td>Question {{forloop.counter}}</td> <td>Question {{forloop.counter}}</td>
+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>/", 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>/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", views.exam_scores_cid, name="exam_scores_cid"),
path( path(
"exam/<int:pk>/scores/<int:sk>/", "exam/<int:pk>/scores/<int:sk>/",
@@ -104,4 +105,8 @@ urlpatterns = [
name="long_series_update", name="long_series_update",
), ),
path("<int:pk>/add_note", views.AddNote.as_view(), name="long_add_note"), 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"),
] ]
+44 -2
View File
@@ -8,6 +8,8 @@ from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist
from django.contrib.auth.mixins import LoginRequiredMixin 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.edit import CreateView, UpdateView, DeleteView
from django.views.generic import ListView from django.views.generic import ListView
@@ -33,8 +35,8 @@ from .models import (
Exam, Exam,
CidUserAnswer, CidUserAnswer,
) )
from .tables import LongTable, LongSeriesTable from .tables import LongTable, LongSeriesTable, UserAnswerTable
from .filters import LongFilter, LongSeriesFilter from .filters import LongFilter, LongSeriesFilter, UserAnswerFilter
from django_tables2 import SingleTableView, SingleTableMixin from django_tables2 import SingleTableView, SingleTableMixin
from django_filters.views import FilterView from django_filters.views import FilterView
@@ -999,3 +1001,43 @@ def question_json_recreate(request, pk):
#question.get_question_json() #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> </div>
<div> <div>
<table> <table class="table table-dark table-striped table-hover table-sm">
<tr> <tr>
<th>Candidate ID</th> <th>Candidate ID</th>
<th>Score</th> <th>Score</th>
@@ -31,13 +31,14 @@
</div> </div>
<div> <div>
<h3>Answers as a table</h3> <h3>Answers as a table</h3>
<table> <table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr> <tr>
<th>Candidate</th> <th>Candidate</th>
{% for cid in cids %} {% for cid in cids %}
<th>{{cid}}</th> <th>{{cid}}</th>
{% endfor %} {% endfor %}
</thead>
</tr> </tr>
{% for question in questions %} {% for question in questions %}
<tr> <tr>
+4 -2
View File
@@ -22,7 +22,7 @@
<div id="stats-plot">{{plot|safe}}</div> <div id="stats-plot">{{plot|safe}}</div>
</div> </div>
<div> <div>
<table> <table class="table table-dark table-striped table-hover table-sm">
<tr> <tr>
<th>Candidate ID</th> <th>Candidate ID</th>
<th>Score</th> <th>Score</th>
@@ -37,7 +37,8 @@
</div> </div>
<div> <div>
<h3>Answers as a table</h3> <h3>Answers as a table</h3>
<table> <table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr> <tr>
<th>Candidate</th> <th>Candidate</th>
{% for cid in cids %} {% for cid in cids %}
@@ -45,6 +46,7 @@
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
{% for question in questions %} {% for question in questions %}
<tr> <tr>
<td>Question {{forloop.counter}}</td> <td>Question {{forloop.counter}}</td>
+2 -2
View File
@@ -66,7 +66,7 @@ urlpatterns = [
name="rapid_update", name="rapid_update",
), ),
path("<int:pk>/add_note", views.AddNote.as_view(), name="rapid_add_note"), 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/", views.UserAnswerTableView.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/<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"), 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) return super().form_valid(form)
class RapidUserAnswerView(LoginRequiredMixin, DetailView): class UserAnswerView(LoginRequiredMixin, DetailView):
model = CidUserAnswer model = CidUserAnswer
class RapidUserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView): class UserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = CidUserAnswer model = CidUserAnswer
table_class = RapidUserAnswerTable table_class = RapidUserAnswerTable
template_name = "rapids/rapid_question_view.html" template_name = "rapids/rapid_question_view.html"
+5 -3
View File
@@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div> <div>
<table> <table class="table table-dark table-striped table-hover table-sm">
<tr> <tr>
<th>Candidate ID</th> <th>Candidate ID</th>
<th>Score</th> <th>Score</th>
@@ -30,8 +30,9 @@
</table> </table>
</div> </div>
<div> <div>
<h3>Answers as a table</h3> <h3>Results as a table</h3>
<table> <table class="table table-dark table-striped table-hover table-sm">
<thead class="thead-dark">
<tr> <tr>
<th>Candidate</th> <th>Candidate</th>
{% for cid in cids %} {% for cid in cids %}
@@ -39,6 +40,7 @@
{% endfor %} {% endfor %}
</tr> </tr>
</thead>
{% for question in questions %} {% for question in questions %}
<tr> <tr>
<td>Question {{forloop.counter}}</td> <td>Question {{forloop.counter}}</td>
+16
View File
@@ -486,3 +486,19 @@ td.user-answer-score-2::after {
.notes .complete { .notes .complete {
color: darkslategray; 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
}