Merge branch 'master' of ssh://161.35.163.87:/home/django/rad
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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 %}
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user