switch to generic exam urls
This commit is contained in:
+4
-95
@@ -1,6 +1,7 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
from anatomy.models import Structure
|
from anatomy.models import Structure
|
||||||
|
from generic.urls import generic_exam_urls, generic_view_urls
|
||||||
from . import views
|
from . import views
|
||||||
from django.views.decorators.cache import cache_page
|
from django.views.decorators.cache import cache_page
|
||||||
|
|
||||||
@@ -8,15 +9,11 @@ app_name = "anatomy"
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.question_list, name='question_list'),
|
# path('', views.question_list, name='question_list'),
|
||||||
path("", views.GenericExamViews.index, name="index"),
|
|
||||||
path(
|
path(
|
||||||
"question/",
|
"question/",
|
||||||
views.AnatomyQuestionView.as_view(),
|
views.AnatomyQuestionView.as_view(),
|
||||||
name="question_list",
|
name="question_list",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"question/<int:pk>/", views.GenericViews.question_detail, name="question_detail"
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"question/create/",
|
"question/create/",
|
||||||
views.AnatomyQuestionCreate.as_view(),
|
views.AnatomyQuestionCreate.as_view(),
|
||||||
@@ -46,102 +43,16 @@ urlpatterns = [
|
|||||||
views.QuestionDelete.as_view(),
|
views.QuestionDelete.as_view(),
|
||||||
name="question_delete",
|
name="question_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"question/<int:pk>/user_answers",
|
|
||||||
views.GenericViews.question_user_answers,
|
|
||||||
name="question_user_answers",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/review",
|
|
||||||
views.GenericViews.question_review,
|
|
||||||
name="question_review",
|
|
||||||
),
|
|
||||||
path("exam/<int:exam_pk>/<int:sk>/mark", views.mark, name="mark"),
|
path("exam/<int:exam_pk>/<int:sk>/mark", views.mark, name="mark"),
|
||||||
path("exam/<int:exam_pk>/<int:sk>/mark/all", views.mark_all, name="mark_all"),
|
path("exam/<int:exam_pk>/<int:sk>/mark/all", views.mark_all, name="mark_all"),
|
||||||
path(
|
path(
|
||||||
"exam/<int:exam_pk>/<int:sk>/mark/review", views.mark_review, name="mark_review"
|
"exam/<int:exam_pk>/<int:sk>/mark/review", views.mark_review, name="mark_review"
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/mark", views.GenericExamViews.mark_overview, name="mark_overview"
|
|
||||||
),
|
|
||||||
# path("exam/<int:pk>/<int:sk>/", views.exam_take, name="exam_take"),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/question/<int:sk>/",
|
|
||||||
views.GenericExamViews.exam_question_detail,
|
|
||||||
name="exam_question_detail",
|
|
||||||
),
|
|
||||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
|
||||||
path("exam/<int:exam_id>/report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"),
|
|
||||||
path("exam/<int:exam_id>/report/email_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"),
|
|
||||||
path("exam/<int:exam_id>/report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>", views.GenericExamViews.exam_user_report, name="exam_user_report"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>/email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"),
|
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/json_edit",
|
|
||||||
views.GenericExamViews.exam_json_edit,
|
|
||||||
name="exam_json_edit",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores",
|
|
||||||
views.GenericExamViews.exam_scores_cid,
|
|
||||||
# cache_page(60 * 1)(views.exam_scores_cid),
|
|
||||||
name="exam_scores_cid",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/refresh",
|
|
||||||
views.GenericExamViews.exam_scores_refresh,
|
|
||||||
# cache_page(60 * 1)(views.exam_scores_cid),
|
|
||||||
name="exam_scores_refresh",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
|
||||||
views.exam_scores_cid_user,
|
|
||||||
name="exam_scores_cid_user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_active",
|
|
||||||
views.GenericExamViews.exam_toggle_active,
|
|
||||||
name="exam_toggle_active",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_results_published",
|
|
||||||
views.GenericExamViews.exam_toggle_results_published,
|
|
||||||
name="exam_toggle_results_published",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/submit",
|
|
||||||
views.GenericExamViews.postExamAnswers,
|
|
||||||
name="exam_answers_submit",
|
|
||||||
),
|
|
||||||
path("exam/", views.GenericExamViews.exam_list, name="exam_list"),
|
|
||||||
path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"),
|
|
||||||
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
|
|
||||||
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
|
|
||||||
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
|
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"),
|
|
||||||
path("exam/json/<int:pk>", views.GenericExamViews.exam_json, name="exam_json"),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/unbased",
|
|
||||||
views.GenericExamViews.exam_json_unbased,
|
|
||||||
name="exam_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/<int:sk>/unbased",
|
|
||||||
views.GenericExamViews.exam_question_json_unbased,
|
|
||||||
name="exam_question_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/recreate",
|
|
||||||
views.GenericExamViews.exam_json_recreate,
|
|
||||||
name="exam_json_recreate",
|
|
||||||
),
|
|
||||||
# path("cid/", views.cid_selector, name="cid_selector"),
|
|
||||||
# path("ajax/exam/flag/", views.flag_question, name="flag_question"),
|
|
||||||
path("body_part/create/", views.create_body_part, name="create_body_part"),
|
path("body_part/create/", views.create_body_part, name="create_body_part"),
|
||||||
path(
|
path(
|
||||||
"body_part/ajax/get_body_part_id",
|
"body_part/ajax/get_body_part_id",
|
||||||
@@ -173,14 +84,12 @@ urlpatterns = [
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"user_answers/delete",
|
|
||||||
views.GenericViews.user_answer_delete_multiple,
|
|
||||||
name="user_answer_delete_multiple",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"structure-autocomplete",
|
"structure-autocomplete",
|
||||||
views.StructureAutocomplete.as_view(model=Structure, create_field="structure"),
|
views.StructureAutocomplete.as_view(model=Structure, create_field="structure"),
|
||||||
name="structure-autocomplete",
|
name="structure-autocomplete",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
+159
-5
@@ -60,10 +60,14 @@ urlpatterns = [
|
|||||||
name="group_email_resend",
|
name="group_email_resend",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cids/group/<int:pk>/update", views.CidUserGroupUpdate.as_view(), name="cid_group_update"
|
"cids/group/<int:pk>/update",
|
||||||
|
views.CidUserGroupUpdate.as_view(),
|
||||||
|
name="cid_group_update",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cids/group/<int:pk>/delete", views.CidUserGroupDelete.as_view(), name="cid_group_delete"
|
"cids/group/<int:pk>/delete",
|
||||||
|
views.CidUserGroupDelete.as_view(),
|
||||||
|
name="cid_group_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
||||||
@@ -71,13 +75,163 @@ urlpatterns = [
|
|||||||
path("user/group/", views.user_group_view, name="user_group_view"),
|
path("user/group/", views.user_group_view, name="user_group_view"),
|
||||||
path("user/group/all", views.user_group_view_all, name="user_group_view_all"),
|
path("user/group/all", views.user_group_view_all, name="user_group_view_all"),
|
||||||
path(
|
path(
|
||||||
"user/group/<int:pk>/update", views.UserUserGroupUpdate.as_view(), name="user_group_update"
|
"user/group/<int:pk>/update",
|
||||||
|
views.UserUserGroupUpdate.as_view(),
|
||||||
|
name="user_group_update",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"user/group/<int:pk>/delete", views.UserUserGroupDelete.as_view(), name="user_group_delete"
|
"user/group/<int:pk>/delete",
|
||||||
|
views.UserUserGroupDelete.as_view(),
|
||||||
|
name="user_group_delete",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"user/group/create", views.UserUserGroupCreate.as_view(), name="user_group_create"
|
"user/group/create",
|
||||||
|
views.UserUserGroupCreate.as_view(),
|
||||||
|
name="user_group_create",
|
||||||
),
|
),
|
||||||
path("cids/create", views.manage_cid_users, name="manage_cid_users"),
|
path("cids/create", views.manage_cid_users, name="manage_cid_users"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def generic_view_urls(generic_views):
|
||||||
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
"user_answers/delete",
|
||||||
|
generic_views.user_answer_delete_multiple,
|
||||||
|
name="user_answer_delete_multiple",
|
||||||
|
),
|
||||||
|
path("author/<int:pk>/", generic_views.author_detail, name="author_detail"),
|
||||||
|
path("author/", generic_views.author_list, name="author_list"),
|
||||||
|
path(
|
||||||
|
"question/<int:pk>/", generic_views.question_detail, name="question_detail"
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"question/<int:pk>/user_answers",
|
||||||
|
generic_views.question_user_answers,
|
||||||
|
name="question_user_answers",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/review",
|
||||||
|
generic_views.question_review,
|
||||||
|
name="question_review",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
return urlpatterns
|
||||||
|
|
||||||
|
|
||||||
|
def generic_exam_urls(generic_exam_view):
|
||||||
|
urlpatterns = [
|
||||||
|
path("", generic_exam_view.index, name="index"),
|
||||||
|
path("exam/<int:pk>/", generic_exam_view.exam_overview, name="exam_overview"),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/mark", generic_exam_view.mark_overview, name="mark_overview"
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/question/<int:sk>/",
|
||||||
|
generic_exam_view.exam_question_detail,
|
||||||
|
name="exam_question_detail",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/user/<int:user_id>",
|
||||||
|
generic_exam_view.exam_user,
|
||||||
|
name="exam_user",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/report/email",
|
||||||
|
generic_exam_view.exam_report_email,
|
||||||
|
name="exam_report_email",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/report/email_unsent",
|
||||||
|
generic_exam_view.exam_report_email_unsent,
|
||||||
|
name="exam_report_email_unsent",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/report/email/status",
|
||||||
|
generic_exam_view.exam_report_email_status,
|
||||||
|
name="exam_report_email_status",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/report/<int:user_id>",
|
||||||
|
generic_exam_view.exam_user_report,
|
||||||
|
name="exam_user_report",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/report/<int:user_id>/email",
|
||||||
|
generic_exam_view.exam_user_report_email,
|
||||||
|
name="exam_user_report_email",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/json_edit",
|
||||||
|
generic_exam_view.exam_json_edit,
|
||||||
|
name="exam_json_edit",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/scores",
|
||||||
|
generic_exam_view.exam_scores_cid,
|
||||||
|
name="exam_scores_cid",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/scores/refresh",
|
||||||
|
generic_exam_view.exam_scores_refresh,
|
||||||
|
# cache_page(60 * 1)(exam_scores_cid),
|
||||||
|
name="exam_scores_refresh",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
||||||
|
generic_exam_view.exam_scores_cid_user,
|
||||||
|
name="exam_scores_cid_user",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/scores/",
|
||||||
|
generic_exam_view.exam_scores_user,
|
||||||
|
name="exam_scores_user",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/toggle_active",
|
||||||
|
generic_exam_view.exam_toggle_active,
|
||||||
|
name="exam_toggle_active",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/toggle_results_published",
|
||||||
|
generic_exam_view.exam_toggle_results_published,
|
||||||
|
name="exam_toggle_results_published",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/submit",
|
||||||
|
generic_exam_view.postExamAnswers,
|
||||||
|
name="exam_answers_submit",
|
||||||
|
),
|
||||||
|
path("exam/", generic_exam_view.exam_list, name="exam_list"),
|
||||||
|
path("exam/all", generic_exam_view.exam_list_all, name="exam_list_all"),
|
||||||
|
path("exam/<int:exam_id>/cids", generic_exam_view.exam_cids, name="exam_cids"),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/cids/edit",
|
||||||
|
generic_exam_view.exam_cids_edit,
|
||||||
|
name="exam_cids_edit",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:exam_id>/users/edit",
|
||||||
|
generic_exam_view.exam_users_edit,
|
||||||
|
name="exam_users_edit",
|
||||||
|
),
|
||||||
|
path("exam/json/", generic_exam_view.active_exams, name="active_exams"),
|
||||||
|
path("exam/json/<int:pk>", generic_exam_view.exam_json, name="exam_json"),
|
||||||
|
path(
|
||||||
|
"exam/json/<int:pk>/unbased",
|
||||||
|
generic_exam_view.exam_json_unbased,
|
||||||
|
name="exam_json_unbased",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/json/<int:pk>/<int:sk>/unbased",
|
||||||
|
generic_exam_view.exam_question_json_unbased,
|
||||||
|
name="exam_question_json_unbased",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/json/<int:pk>/recreate",
|
||||||
|
generic_exam_view.exam_json_recreate,
|
||||||
|
name="exam_json_recreate",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
return urlpatterns
|
||||||
|
|||||||
+127
-22
@@ -284,6 +284,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
cid_user_answer,
|
cid_user_answer,
|
||||||
app,
|
app,
|
||||||
question_type,
|
question_type,
|
||||||
|
normalise_score=None,
|
||||||
):
|
):
|
||||||
self.Exam = exam
|
self.Exam = exam
|
||||||
self.Question = question
|
self.Question = question
|
||||||
@@ -291,6 +292,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
self.CidUserAnswer = cid_user_answer
|
self.CidUserAnswer = cid_user_answer
|
||||||
self.app_name = app
|
self.app_name = app
|
||||||
self.question_type = question_type
|
self.question_type = question_type
|
||||||
|
self.normalise_score = normalise_score
|
||||||
|
|
||||||
# THis may be better than check_user_access below
|
# THis may be better than check_user_access below
|
||||||
# group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"}
|
# group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"}
|
||||||
@@ -562,7 +564,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_user(self, request, exam_id, user_id):
|
def exam_user(self, request, exam_id, user_id):
|
||||||
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
exam = get_object_or_404(
|
||||||
|
self.Exam.objects.prefetch_related("author"), pk=exam_id
|
||||||
|
)
|
||||||
|
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, exam_id):
|
if not self.check_user_access(request.user, exam_id):
|
||||||
@@ -573,10 +577,11 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
return JsonResponse(model_to_dict(user_exam))
|
return JsonResponse(model_to_dict(user_exam))
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_report_email_status(self, request, exam_id):
|
def exam_report_email_status(self, request, exam_id):
|
||||||
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
exam = get_object_or_404(
|
||||||
|
self.Exam.objects.prefetch_related("author"), pk=exam_id
|
||||||
|
)
|
||||||
|
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, exam_id):
|
if not self.check_user_access(request.user, exam_id):
|
||||||
@@ -600,20 +605,21 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
return JsonResponse(status)
|
return JsonResponse(status)
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_report_email_unsent(self, request, exam_id):
|
def exam_report_email_unsent(self, request, exam_id):
|
||||||
return self.exam_report_email(request, exam_id, unsent_only=True)
|
return self.exam_report_email(request, exam_id, unsent_only=True)
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_report_email(self, request, exam_id, unsent_only=False, users=None):
|
def exam_report_email(self, request, exam_id, unsent_only=False, users=None):
|
||||||
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
exam = get_object_or_404(
|
||||||
|
self.Exam.objects.prefetch_related("author"), pk=exam_id
|
||||||
|
)
|
||||||
|
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, exam_id):
|
if not self.check_user_access(request.user, exam_id):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
additional_email = request.GET.get("additional_email", "")
|
additional_email = request.GET.get("additional_email", "")
|
||||||
|
|
||||||
# check addition email is valid
|
# check addition email is valid
|
||||||
@@ -639,9 +645,10 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if json.loads(user_exam.results_emailed_status)[0] == True:
|
if json.loads(user_exam.results_emailed_status)[0] == True:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
if additional_email:
|
if additional_email:
|
||||||
email_results[user.pk] = exam.email_user_results(user, additional_emails=[additional_email])
|
email_results[user.pk] = exam.email_user_results(
|
||||||
|
user, additional_emails=[additional_email]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
email_results[user.pk] = exam.email_user_results(user)
|
email_results[user.pk] = exam.email_user_results(user)
|
||||||
email_results[user.pk].append(f"{time}")
|
email_results[user.pk].append(f"{time}")
|
||||||
@@ -651,7 +658,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
exam.exam_results_emailed = time
|
exam.exam_results_emailed = time
|
||||||
exam.save()
|
exam.save()
|
||||||
|
|
||||||
return JsonResponse(email_results)
|
return JsonResponse(email_results)
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
@@ -660,24 +667,26 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
# print(Exam.objects.all())
|
# print(Exam.objects.all())
|
||||||
# exams = Exam.objects.all()
|
# exams = Exam.objects.all()
|
||||||
# print("test", Exam.objects.all().get(id=pk))
|
# print("test", Exam.objects.all().get(id=pk))
|
||||||
#exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
# exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
||||||
|
|
||||||
#u = get_object_or_404(User, pk=user_id)
|
# u = get_object_or_404(User, pk=user_id)
|
||||||
|
|
||||||
#if request.user not in exam.author.all():
|
# if request.user not in exam.author.all():
|
||||||
# if not self.check_user_access(request.user, exam_id):
|
# if not self.check_user_access(request.user, exam_id):
|
||||||
# raise PermissionDenied
|
# raise PermissionDenied
|
||||||
|
|
||||||
#res = exam.email_user_results(u)
|
# res = exam.email_user_results(u)
|
||||||
|
|
||||||
#return HttpResponse(res)
|
# return HttpResponse(res)
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_user_report(self, request, exam_id, user_id):
|
def exam_user_report(self, request, exam_id, user_id):
|
||||||
# print(Exam.objects.all())
|
# print(Exam.objects.all())
|
||||||
# exams = Exam.objects.all()
|
# exams = Exam.objects.all()
|
||||||
# print("test", Exam.objects.all().get(id=pk))
|
# print("test", Exam.objects.all().get(id=pk))
|
||||||
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id)
|
exam = get_object_or_404(
|
||||||
|
self.Exam.objects.prefetch_related("author"), pk=exam_id
|
||||||
|
)
|
||||||
|
|
||||||
u = get_object_or_404(User, pk=user_id)
|
u = get_object_or_404(User, pk=user_id)
|
||||||
|
|
||||||
@@ -690,7 +699,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
return HttpResponse(res)
|
return HttpResponse(res)
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_overview(self, request, pk):
|
def exam_overview(self, request, pk):
|
||||||
# print(Exam.objects.all())
|
# print(Exam.objects.all())
|
||||||
@@ -698,7 +706,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
# print("test", Exam.objects.all().get(id=pk))
|
# print("test", Exam.objects.all().get(id=pk))
|
||||||
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=pk)
|
exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=pk)
|
||||||
|
|
||||||
|
|
||||||
if request.user not in exam.author.all():
|
if request.user not in exam.author.all():
|
||||||
if not self.check_user_access(request.user, pk):
|
if not self.check_user_access(request.user, pk):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
@@ -1517,6 +1524,103 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
return JsonResponse(question_json)
|
return JsonResponse(question_json)
|
||||||
|
|
||||||
|
@method_decorator(login_required)
|
||||||
|
def exam_scores_user(self, request, pk):
|
||||||
|
return self.exam_scores_cid_user(request, pk)
|
||||||
|
|
||||||
|
def exam_scores_cid_user(self, request, pk, cid=None, passcode=""):
|
||||||
|
exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
|
|
||||||
|
if not exam.exam_mode:
|
||||||
|
raise Http404("Packet not in exam mode")
|
||||||
|
|
||||||
|
# TODO:Need some kind of test for cid
|
||||||
|
|
||||||
|
questions = (
|
||||||
|
exam.exam_questions.all()
|
||||||
|
) # .prefetch_related("cid_user_answers", "answers")
|
||||||
|
|
||||||
|
# cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question"))
|
||||||
|
|
||||||
|
# cid_user_answers_q_map = {}
|
||||||
|
|
||||||
|
# for ans in cid_user_answers:
|
||||||
|
# cid_user_answers_q_map[ans.question] = ans
|
||||||
|
|
||||||
|
answers_and_marks = []
|
||||||
|
answers_marks = []
|
||||||
|
answers = []
|
||||||
|
|
||||||
|
view_all_results = False
|
||||||
|
if request.user.groups.filter(name="view_all_results").exists():
|
||||||
|
view_all_results = True
|
||||||
|
|
||||||
|
for q in questions:
|
||||||
|
# Get user answer
|
||||||
|
if cid is not None:
|
||||||
|
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
||||||
|
else:
|
||||||
|
user_answer = q.cid_user_answers.filter(
|
||||||
|
user=request.user, exam__id=pk
|
||||||
|
).first()
|
||||||
|
# user_answer = cid_user_answers_q_map[q]
|
||||||
|
|
||||||
|
if not user_answer or user_answer is None:
|
||||||
|
# skip if no answer
|
||||||
|
answers_marks.append(0)
|
||||||
|
# answers.append("")
|
||||||
|
answer_score = 0
|
||||||
|
ans = "Not answered"
|
||||||
|
else:
|
||||||
|
if user_answer.normal:
|
||||||
|
ans = "Normal"
|
||||||
|
else:
|
||||||
|
ans = user_answer.answer
|
||||||
|
answer_score = user_answer.get_answer_score()
|
||||||
|
|
||||||
|
correct_answer = q.get_primary_answer()
|
||||||
|
|
||||||
|
if not exam.publish_results and not view_all_results:
|
||||||
|
correct_answer = "*****"
|
||||||
|
answer_score = 0
|
||||||
|
answers.append(ans)
|
||||||
|
answers_marks.append(answer_score)
|
||||||
|
answers_and_marks.append((ans, answer_score, correct_answer))
|
||||||
|
|
||||||
|
if "unmarked" in answers_marks:
|
||||||
|
answered = [i for i in answers_marks if type(i) == int]
|
||||||
|
total_score = sum(answered)
|
||||||
|
unmarked_number = len(answers_marks) - len(answered)
|
||||||
|
total_score = "{} ({} unmarked)".format(total_score, unmarked_number)
|
||||||
|
else:
|
||||||
|
total_score = sum(answers_marks)
|
||||||
|
|
||||||
|
max_score = len(questions) * 2
|
||||||
|
|
||||||
|
template_context = {
|
||||||
|
"exam": exam,
|
||||||
|
"cid": cid,
|
||||||
|
"passcode": passcode,
|
||||||
|
"questions": questions,
|
||||||
|
"answers": answers,
|
||||||
|
"answers_marks": answers_marks,
|
||||||
|
"total_score": total_score,
|
||||||
|
"max_score": max_score,
|
||||||
|
"answers_and_marks": answers_and_marks,
|
||||||
|
"view_all_results": view_all_results,
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.normalise_score is not None:
|
||||||
|
|
||||||
|
normalised_score = self.normalise_score(total_score)
|
||||||
|
template_context["normalised_score"] = normalised_score
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
f"{self.app_name}/exam_scores_user.html",
|
||||||
|
template_context,
|
||||||
|
)
|
||||||
|
|
||||||
def exam_scores_cid(self, request, pk):
|
def exam_scores_cid(self, request, pk):
|
||||||
exam = get_object_or_404(self.Exam, pk=pk)
|
exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
|
|
||||||
@@ -1563,7 +1667,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
cids.add(cid)
|
cids.add(cid)
|
||||||
else:
|
else:
|
||||||
cid = f"u/{cid_user_answer.user.pk}"
|
cid = f"u/{cid_user_answer.user.pk}"
|
||||||
#cids_user_id_map[cid] = cid_user_answer.user.username
|
# cids_user_id_map[cid] = cid_user_answer.user.username
|
||||||
name = f"{cid_user_answer.user.first_name} {cid_user_answer.user.last_name}"
|
name = f"{cid_user_answer.user.first_name} {cid_user_answer.user.last_name}"
|
||||||
if not name.strip():
|
if not name.strip():
|
||||||
name = cid_user_answer.user.username
|
name = cid_user_answer.user.username
|
||||||
@@ -1665,11 +1769,11 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
exam.stats_min = min(user_scores_list)
|
exam.stats_min = min(user_scores_list)
|
||||||
exam.stats_max = max(user_scores_list)
|
exam.stats_max = max(user_scores_list)
|
||||||
|
|
||||||
#exam.stats_graph = fig_html
|
# exam.stats_graph = fig_html
|
||||||
|
|
||||||
user_data = {}
|
user_data = {}
|
||||||
for u in cids:
|
for u in cids:
|
||||||
#uid = cids_user_id_map[u]
|
# uid = cids_user_id_map[u]
|
||||||
user_data[u] = {
|
user_data[u] = {
|
||||||
"score": user_scores[u],
|
"score": user_scores[u],
|
||||||
"normalised_score": user_scores_normalised[u],
|
"normalised_score": user_scores_normalised[u],
|
||||||
@@ -1680,7 +1784,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
user_data[u]["callstates"] = str(counted_callstates)
|
user_data[u]["callstates"] = str(counted_callstates)
|
||||||
user_answers_callstates_counted[u] = counted_callstates
|
user_answers_callstates_counted[u] = counted_callstates
|
||||||
|
|
||||||
|
|
||||||
exam.user_scores = user_data
|
exam.user_scores = user_data
|
||||||
|
|
||||||
exam.save()
|
exam.save()
|
||||||
@@ -1709,7 +1812,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.app_name == "rapids":
|
if self.app_name == "rapids":
|
||||||
template_variables["user_answers_callstates"] = user_answers_callstates_counted
|
template_variables[
|
||||||
|
"user_answers_callstates"
|
||||||
|
] = user_answers_callstates_counted
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
|||||||
+6
-58
@@ -1,11 +1,12 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from generic.urls import generic_exam_urls, generic_view_urls
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = "longs"
|
app_name = "longs"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.question_list, name='question_list'),
|
# path('', views.question_list, name='question_list'),
|
||||||
path("", views.GenericExamViews.index, name="index"),
|
|
||||||
path("author/<int:pk>/", views.author_detail, name="author_detail"),
|
path("author/<int:pk>/", views.author_detail, name="author_detail"),
|
||||||
path("author/", views.author_list, name="author_list"),
|
path("author/", views.author_list, name="author_list"),
|
||||||
path("question/", views.LongView.as_view(), name="long_view"),
|
path("question/", views.LongView.as_view(), name="long_view"),
|
||||||
@@ -81,22 +82,7 @@ urlpatterns = [
|
|||||||
views.mark_question_overview,
|
views.mark_question_overview,
|
||||||
name="mark_question_overview",
|
name="mark_question_overview",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/mark", views.GenericExamViews.mark_overview, name="mark_overview"
|
|
||||||
),
|
|
||||||
# path("exam/<int:pk>/<int:sk>/", views.exam_take, name="exam_take"),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/question/<int:sk>/",
|
|
||||||
views.GenericExamViews.exam_question_detail,
|
|
||||||
name="exam_question_detail",
|
|
||||||
),
|
|
||||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/json_edit",
|
|
||||||
views.GenericExamViews.exam_json_edit,
|
|
||||||
name="exam_json_edit",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"exam/<int:pk>/question_json_refresh",
|
"exam/<int:pk>/question_json_refresh",
|
||||||
views.refresh_exam_question_json,
|
views.refresh_exam_question_json,
|
||||||
@@ -108,52 +94,10 @@ urlpatterns = [
|
|||||||
views.exam_scores_cid_user,
|
views.exam_scores_cid_user,
|
||||||
name="exam_scores_cid_user",
|
name="exam_scores_cid_user",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_active",
|
|
||||||
views.GenericExamViews.exam_toggle_active,
|
|
||||||
name="exam_toggle_active",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_results_published",
|
|
||||||
views.GenericExamViews.exam_toggle_results_published,
|
|
||||||
name="exam_toggle_results_published",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/submit",
|
|
||||||
views.GenericExamViews.postExamAnswers,
|
|
||||||
name="exam_answers_submit",
|
|
||||||
),
|
|
||||||
path("exam/", views.GenericExamViews.exam_list, name="exam_list"),
|
|
||||||
path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"),
|
|
||||||
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
||||||
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
|
|
||||||
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
|
|
||||||
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
|
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"),
|
|
||||||
path("exam/json/<int:pk>", views.GenericExamViews.exam_json, name="exam_json"),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/unbased",
|
|
||||||
views.GenericExamViews.exam_json_unbased,
|
|
||||||
name="exam_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/<int:sk>",
|
|
||||||
views.GenericExamViews.exam_question_json,
|
|
||||||
name="exam_question_json",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/unbased/<int:sk>",
|
|
||||||
views.GenericExamViews.exam_question_json_unbased,
|
|
||||||
name="exam_question_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/recreate",
|
|
||||||
views.GenericExamViews.exam_json_recreate,
|
|
||||||
name="exam_json_recreate",
|
|
||||||
),
|
|
||||||
path("create/", views.LongCreate.as_view(), name="long_create"),
|
path("create/", views.LongCreate.as_view(), name="long_create"),
|
||||||
path("create/series", views.LongSeriesCreate.as_view(), name="long_series_create"),
|
path("create/series", views.LongSeriesCreate.as_view(), name="long_series_create"),
|
||||||
path(
|
path(
|
||||||
@@ -209,3 +153,7 @@ urlpatterns = [
|
|||||||
name="user_answer_delete_multiple",
|
name="user_answer_delete_multiple",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
+5
-43
@@ -1,11 +1,11 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from generic.urls import generic_exam_urls, generic_view_urls
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = "physics"
|
app_name = "physics"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.question_list, name='question_list'),
|
|
||||||
path("", views.GenericExamViews.index, name="index"),
|
|
||||||
# path("question/", views.QuestionView.as_view(), name="question_view"),
|
# path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/", views.QuestionView.as_view(), name="question_view"),
|
path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
||||||
@@ -25,46 +25,10 @@ urlpatterns = [
|
|||||||
views.exam_take_overview,
|
views.exam_take_overview,
|
||||||
name="exam_take_overview",
|
name="exam_take_overview",
|
||||||
),
|
),
|
||||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
|
||||||
path("exam/<int:exam_id>/report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"),
|
|
||||||
path("exam/<int:exam_id>/report/email_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"),
|
|
||||||
path("exam/<int:exam_id>/report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>", views.GenericExamViews.exam_user_report, name="exam_user_report"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>/email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"),
|
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores",
|
|
||||||
views.GenericExamViews.exam_scores_cid,
|
|
||||||
name="exam_scores_cid",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
|
||||||
views.exam_scores_cid_user,
|
|
||||||
name="exam_scores_cid_user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_active",
|
|
||||||
views.GenericExamViews.exam_toggle_active,
|
|
||||||
name="exam_toggle_active",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_results_published",
|
|
||||||
views.GenericExamViews.exam_toggle_results_published,
|
|
||||||
name="exam_toggle_results_published",
|
|
||||||
),
|
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
|
|
||||||
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
|
|
||||||
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
|
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
path(
|
|
||||||
"exam/submit",
|
|
||||||
views.GenericExamViews.postExamAnswers,
|
|
||||||
name="exam_answers_submit",
|
|
||||||
),
|
|
||||||
path("exam/", views.GenericExamViews.exam_list, name="exam_list"),
|
|
||||||
path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"),
|
|
||||||
path("exam/available", views.active_exams, name="active_exams"),
|
path("exam/available", views.active_exams, name="active_exams"),
|
||||||
# path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
# path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
||||||
# path("exam/json/<int:pk>/recreate", views.exam_json_recreate, name="exam_json_recreate"),
|
# path("exam/json/<int:pk>/recreate", views.exam_json_recreate, name="exam_json_recreate"),
|
||||||
@@ -81,9 +45,7 @@ urlpatterns = [
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"user_answers/delete",
|
|
||||||
views.GenericViews.user_answer_delete_multiple,
|
|
||||||
name="user_answer_delete_multiple",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
+8
-101
@@ -4,23 +4,21 @@ from rapids.models import Answer
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
from django.views.decorators.cache import cache_page, cache_control
|
from django.views.decorators.cache import cache_page, cache_control
|
||||||
|
from generic.urls import generic_exam_urls, generic_view_urls
|
||||||
|
|
||||||
app_name = "rapids"
|
app_name = "rapids"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.question_list, name='question_list'),
|
# path('', views.question_list, name='question_list'),
|
||||||
path("", views.GenericExamViews.index, name="index"),
|
#path("", views.GenericExamViews.index, name="index"),
|
||||||
path("author/<int:pk>/", views.GenericViews.author_detail, name="author_detail"),
|
#path("exam", generic_exam_urls(views.GenericExamViews), name="index"),
|
||||||
path("author/", views.GenericViews.author_list, name="author_list"),
|
|
||||||
path("question_viewer", views.question_viewer, name="question_viewer"),
|
path("question_viewer", views.question_viewer, name="question_viewer"),
|
||||||
path("question/", views.RapidView.as_view(), name="rapid_view"),
|
path("question/", views.RapidView.as_view(), name="rapid_view"),
|
||||||
path("question/hash", views.get_question_by_hash, name="rapid_question_by_hash"),
|
path("question/hash", views.get_question_by_hash, name="rapid_question_by_hash"),
|
||||||
# path("unchecked/", views.unchecked_list, name="unchecked_list"),
|
# path("unchecked/", views.unchecked_list, name="unchecked_list"),
|
||||||
# path("verified/<int:pk>/", views.verified_detail, name="verified_detail"),
|
# path("verified/<int:pk>/", views.verified_detail, name="verified_detail"),
|
||||||
path(
|
|
||||||
"question/<int:pk>/", views.GenericViews.question_detail, name="question_detail"
|
|
||||||
),
|
|
||||||
path("question/<int:pk>/json", views.question_json, name="question_json"),
|
path("question/<int:pk>/json", views.question_json, name="question_json"),
|
||||||
path(
|
path(
|
||||||
"question/<int:pk>/json/unbased",
|
"question/<int:pk>/json/unbased",
|
||||||
@@ -42,110 +40,21 @@ urlpatterns = [
|
|||||||
views.QuestionDelete.as_view(),
|
views.QuestionDelete.as_view(),
|
||||||
name="question_delete",
|
name="question_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"question/<int:pk>/user_answers",
|
|
||||||
views.GenericViews.question_user_answers,
|
|
||||||
name="question_user_answers",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"question/<int:pk>/anon",
|
"question/<int:pk>/anon",
|
||||||
views.question_anonymise_dicom,
|
views.question_anonymise_dicom,
|
||||||
name="question_anonymise_dicom",
|
name="question_anonymise_dicom",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/review",
|
|
||||||
views.GenericViews.question_review,
|
|
||||||
name="question_review",
|
|
||||||
),
|
|
||||||
path("exam/<int:exam_pk>/<int:sk>/mark", views.mark, name="mark"),
|
path("exam/<int:exam_pk>/<int:sk>/mark", views.mark, name="mark"),
|
||||||
path("exam/<int:exam_pk>/<int:sk>/mark/all", views.mark_all, name="mark_all"),
|
path("exam/<int:exam_pk>/<int:sk>/mark/all", views.mark_all, name="mark_all"),
|
||||||
path(
|
path(
|
||||||
"exam/<int:exam_pk>/<int:sk>/mark/review", views.mark_review, name="mark_review"
|
"exam/<int:exam_pk>/<int:sk>/mark/review", views.mark_review, name="mark_review"
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/mark", views.GenericExamViews.mark_overview, name="mark_overview"
|
|
||||||
),
|
|
||||||
# path("exam/<int:pk>/<int:sk>/", views.exam_take, name="exam_take"),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/question/<int:sk>/",
|
|
||||||
views.GenericExamViews.exam_question_detail,
|
|
||||||
name="exam_question_detail",
|
|
||||||
),
|
|
||||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
|
||||||
path("exam/<int:exam_id>/user/<int:user_id>", views.GenericExamViews.exam_user, name="exam_user"),
|
|
||||||
path("exam/<int:exam_id>/report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"),
|
|
||||||
path("exam/<int:exam_id>/report/email_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"),
|
|
||||||
path("exam/<int:exam_id>/report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>", views.GenericExamViews.exam_user_report, name="exam_user_report"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>/email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"),
|
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/json_edit",
|
|
||||||
views.GenericExamViews.exam_json_edit,
|
|
||||||
name="exam_json_edit",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores",
|
|
||||||
views.GenericExamViews.exam_scores_cid,
|
|
||||||
name="exam_scores_cid",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/refresh",
|
|
||||||
views.GenericExamViews.exam_scores_refresh,
|
|
||||||
# cache_page(60 * 1)(views.exam_scores_cid),
|
|
||||||
name="exam_scores_refresh",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
|
||||||
views.exam_scores_cid_user,
|
|
||||||
name="exam_scores_cid_user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/",
|
|
||||||
views.exam_scores_user,
|
|
||||||
name="exam_scores_user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_active",
|
|
||||||
views.GenericExamViews.exam_toggle_active,
|
|
||||||
name="exam_toggle_active",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_results_published",
|
|
||||||
views.GenericExamViews.exam_toggle_results_published,
|
|
||||||
name="exam_toggle_results_published",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/submit",
|
|
||||||
views.GenericExamViews.postExamAnswers,
|
|
||||||
name="exam_answers_submit",
|
|
||||||
),
|
|
||||||
path("exam/", views.GenericExamViews.exam_list, name="exam_list"),
|
|
||||||
path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"),
|
|
||||||
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
|
|
||||||
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
|
|
||||||
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
|
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"),
|
|
||||||
path("exam/json/<int:pk>", views.GenericExamViews.exam_json, name="exam_json"),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/unbased",
|
|
||||||
views.GenericExamViews.exam_json_unbased,
|
|
||||||
name="exam_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/<int:sk>/unbased",
|
|
||||||
views.GenericExamViews.exam_question_json_unbased,
|
|
||||||
name="exam_question_json_unbased",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/json/<int:pk>/recreate",
|
|
||||||
views.GenericExamViews.exam_json_recreate,
|
|
||||||
name="exam_json_recreate",
|
|
||||||
),
|
|
||||||
path("create/", views.RapidCreate.as_view(), name="rapid_create"),
|
path("create/", views.RapidCreate.as_view(), name="rapid_create"),
|
||||||
path("create/exam/<int:pk>", views.RapidCreate.as_view(), name="rapid_create_exam"),
|
path("create/exam/<int:pk>", views.RapidCreate.as_view(), name="rapid_create_exam"),
|
||||||
path(
|
path(
|
||||||
@@ -185,11 +94,6 @@ urlpatterns = [
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"user_answers/delete",
|
|
||||||
views.GenericViews.user_answer_delete_multiple,
|
|
||||||
name="user_answer_delete_multiple",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"answer-autocomplete",
|
"answer-autocomplete",
|
||||||
views.AnswerAutocomplete.as_view(
|
views.AnswerAutocomplete.as_view(
|
||||||
@@ -198,3 +102,6 @@ urlpatterns = [
|
|||||||
name="answer-autocomplete",
|
name="answer-autocomplete",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
+1
-88
@@ -686,93 +686,6 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@login_required
|
|
||||||
def exam_scores_user(request, pk):
|
|
||||||
return exam_scores_cid_user(request, pk)
|
|
||||||
|
|
||||||
def exam_scores_cid_user(request, pk, cid=None, passcode=""):
|
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
|
||||||
|
|
||||||
if not exam.exam_mode:
|
|
||||||
raise Http404("Packet not in exam mode")
|
|
||||||
|
|
||||||
# TODO:Need some kind of test for cid
|
|
||||||
|
|
||||||
questions = (
|
|
||||||
exam.exam_questions.all()
|
|
||||||
) # .prefetch_related("cid_user_answers", "answers")
|
|
||||||
|
|
||||||
# cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question"))
|
|
||||||
|
|
||||||
# cid_user_answers_q_map = {}
|
|
||||||
|
|
||||||
# for ans in cid_user_answers:
|
|
||||||
# cid_user_answers_q_map[ans.question] = ans
|
|
||||||
|
|
||||||
answers_and_marks = []
|
|
||||||
answers_marks = []
|
|
||||||
answers = []
|
|
||||||
|
|
||||||
view_all_results = False
|
|
||||||
if request.user.groups.filter(name="view_all_results").exists():
|
|
||||||
view_all_results = True
|
|
||||||
|
|
||||||
for q in questions:
|
|
||||||
# Get user answer
|
|
||||||
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
|
||||||
# user_answer = cid_user_answers_q_map[q]
|
|
||||||
|
|
||||||
if not user_answer or user_answer is None:
|
|
||||||
# skip if no answer
|
|
||||||
answers_marks.append(0)
|
|
||||||
# answers.append("")
|
|
||||||
answer_score = 0
|
|
||||||
ans = "Not answered"
|
|
||||||
else:
|
|
||||||
if user_answer.normal:
|
|
||||||
ans = "Normal"
|
|
||||||
else:
|
|
||||||
ans = user_answer.answer
|
|
||||||
answer_score = user_answer.get_answer_score()
|
|
||||||
|
|
||||||
correct_answer = q.get_primary_answer()
|
|
||||||
|
|
||||||
if not exam.publish_results and not view_all_results:
|
|
||||||
correct_answer = "*****"
|
|
||||||
answer_score = 0
|
|
||||||
answers.append(ans)
|
|
||||||
answers_marks.append(answer_score)
|
|
||||||
answers_and_marks.append((ans, answer_score, correct_answer))
|
|
||||||
|
|
||||||
if "unmarked" in answers_marks:
|
|
||||||
answered = [i for i in answers_marks if type(i) == int]
|
|
||||||
total_score = sum(answered)
|
|
||||||
unmarked_number = len(answers_marks) - len(answered)
|
|
||||||
total_score = "{} ({} unmarked)".format(total_score, unmarked_number)
|
|
||||||
else:
|
|
||||||
total_score = sum(answers_marks)
|
|
||||||
|
|
||||||
normalised_score = normaliseScore(total_score)
|
|
||||||
|
|
||||||
max_score = len(questions) * 2
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"rapids/exam_scores_user.html",
|
|
||||||
{
|
|
||||||
"exam": exam,
|
|
||||||
"cid": cid,
|
|
||||||
"passcode": passcode,
|
|
||||||
"questions": questions,
|
|
||||||
"answers": answers,
|
|
||||||
"answers_marks": answers_marks,
|
|
||||||
"total_score": total_score,
|
|
||||||
"normalised_score": normalised_score,
|
|
||||||
"max_score": max_score,
|
|
||||||
"answers_and_marks": answers_and_marks,
|
|
||||||
"view_all_results": view_all_results,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
|
class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
|
||||||
@@ -780,7 +693,7 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
success_url = reverse_lazy("rapids:rapid_view")
|
success_url = reverse_lazy("rapids:rapid_view")
|
||||||
|
|
||||||
|
|
||||||
GenericExamViews = ExamViews(Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid")
|
GenericExamViews = ExamViews(Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid", normalise_score=normaliseScore)
|
||||||
|
|
||||||
|
|
||||||
class ExamCreate(ExamCreateBase):
|
class ExamCreate(ExamCreateBase):
|
||||||
|
|||||||
+5
-39
@@ -1,13 +1,13 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from generic.urls import generic_exam_urls, generic_view_urls
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from django.views.decorators.cache import cache_page
|
from django.views.decorators.cache import cache_page
|
||||||
|
|
||||||
app_name = "sbas"
|
app_name = "sbas"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# path('', views.question_list, name='question_list'),
|
|
||||||
path("", views.GenericExamViews.index, name="index"),
|
|
||||||
path("question/", views.QuestionView.as_view(), name="question_view"),
|
path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
||||||
# path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
# path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
||||||
@@ -25,40 +25,8 @@ urlpatterns = [
|
|||||||
views.exam_take_overview,
|
views.exam_take_overview,
|
||||||
name="exam_take_overview",
|
name="exam_take_overview",
|
||||||
),
|
),
|
||||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
|
||||||
path("exam/<int:exam_id>/report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"),
|
|
||||||
path("exam/<int:exam_id>/report/email_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"),
|
|
||||||
path("exam/<int:exam_id>/report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>", views.GenericExamViews.exam_user_report, name="exam_user_report"),
|
|
||||||
path("exam/<int:exam_id>/report/<int:user_id>/email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"),
|
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores",
|
|
||||||
views.GenericExamViews.exam_scores_cid,
|
|
||||||
name="exam_scores_cid",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
|
||||||
views.exam_scores_cid_user,
|
|
||||||
name="exam_scores_cid_user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_active",
|
|
||||||
views.GenericExamViews.exam_toggle_active,
|
|
||||||
name="exam_toggle_active",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/toggle_results_published",
|
|
||||||
views.GenericExamViews.exam_toggle_results_published,
|
|
||||||
name="exam_toggle_results_published",
|
|
||||||
),
|
|
||||||
# path("exam/submit", views.GenericExamViews.postExamAnswers, name="exam_answers_submit"),
|
|
||||||
path("exam/", views.GenericExamViews.exam_list, name="exam_list"),
|
|
||||||
path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"),
|
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
|
|
||||||
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
|
|
||||||
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
|
|
||||||
path("exam/available", views.active_exams, name="active_exams"),
|
path("exam/available", views.active_exams, name="active_exams"),
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
@@ -77,9 +45,7 @@ urlpatterns = [
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"user_answers/delete",
|
|
||||||
views.GenericViews.user_answer_delete_multiple,
|
|
||||||
name="user_answer_delete_multiple",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
Reference in New Issue
Block a user