diff --git a/anatomy/models.py b/anatomy/models.py index 3e80b5fd..da94ee3f 100644 --- a/anatomy/models.py +++ b/anatomy/models.py @@ -127,6 +127,9 @@ class AnatomyQuestion(QuestionBase): class Meta: permissions = () + def get_app_name(self): + return "anatomy" + def __str__(self): # Get first answer return "{}/{}: {} [{}, {}]".format( @@ -410,6 +413,9 @@ class Exam(ExamBase): examcollection = models.ForeignKey(ExamCollection, blank=True, null=True, on_delete=models.SET_NULL, related_name="anatomy_exams") + def get_app_name(self): + return "anatomy" + def get_exam_json(self, based=True): questions = self.get_questions() diff --git a/anatomy/templates/anatomy/exam_overview.html b/anatomy/templates/anatomy/exam_overview.html index fe4b4e13..94c322a0 100644 --- a/anatomy/templates/anatomy/exam_overview.html +++ b/anatomy/templates/anatomy/exam_overview.html @@ -1,11 +1,14 @@ {% extends 'anatomy/exams.html' %} +{% block navigation %} +{{ block.super }} + {% include 'generic/exam_overview_headers.html' %} +{% endblock navigation %} + {% block content %} {% load thumbnail %}
- {% include 'generic/exam_overview_headers.html' %} -
    {% for question in questions %} diff --git a/anatomy/views.py b/anatomy/views.py index c7125f00..07bc7a01 100644 --- a/anatomy/views.py +++ b/anatomy/views.py @@ -71,7 +71,7 @@ from helpers.images import image_as_base64 from django.template.defaulttags import register -from generic.mixins import SuperuserRequiredMixin +from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin from rest_framework import viewsets, permissions @@ -805,10 +805,16 @@ class ExamCreate(ExamCreateBase): form_class = ExamForm -class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): +class ExamUpdate(CheckCanEditMixin, ExamUpdateBase, AuthorOrCheckerRequiredMixin): model = Exam form_class = ExamForm + #def get_context_data(self, **kwargs): + # context = super().get_context_data(**kwargs) + + # context["can_edit"] = self.object.check_user_can_edit(self.request.user) + + # return context class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase): model = Exam @@ -831,10 +837,12 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase): # return Exam.objects.filter(author__id=user.id) -class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView): +class ExamAuthorUpdate(RevisionMixin, CheckCanEditMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView): model = Exam form_class = ExamAuthorForm + template_name = "author_form.html" + GenericViews = GenericViewBase("anatomy", AnatomyQuestion, UserAnswer, Exam) diff --git a/atlas/models.py b/atlas/models.py index 6ee96fd0..45205d76 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -5,7 +5,7 @@ import pathlib from typing import Tuple from django.http import Http404, HttpRequest -from generic.mixins import AuthorMixin +from generic.mixins import AuthorMixin, QuestionMixin from rad.settings import REMOTE_URL from django.db.models.fields.files import ImageField from django.db.models.fields.related import ForeignKey @@ -273,7 +273,7 @@ class Structure(SynMixin, models.Model): @reversion.register -class Case(models.Model, AuthorMixin): +class Case(models.Model, AuthorMixin, QuestionMixin): # class SubspecialtyChoices(models.TextChoices): # BREAST = "BR", _("Breast") # CARDIAC = "CA", _("Cardiac") @@ -364,6 +364,9 @@ class Case(models.Model, AuthorMixin): blank=True, ) + def get_app_name(self): + return "atlas" + # question_file = models.FileField(upload_to=question_file_directory_path, blank=True, null=True) def get_absolute_url(self): @@ -373,6 +376,10 @@ class Case(models.Model, AuthorMixin): # return f"{self.pk}: {self.title}" return format_html("{}", self.get_absolute_url(), str(self)) + #def get_base_template(self): + + #def get_edit_template_links(self): + def __str__(self): return f"{self.pk}: {self.title}" @@ -773,6 +780,12 @@ class CaseCollection(ExamOrCollectionGenericBase): default=VIEWER_MODE_CHOICES.BUILT_IN, ) + def get_app_name(self): + return "atlas" + + def get_link_headers(self): + return f"{self.get_app_name()}/collection_headers.html" + def get_absolute_url(self): return reverse("atlas:collection_detail", kwargs={"pk": self.pk}) diff --git a/atlas/templates/atlas/case_detail.html b/atlas/templates/atlas/case_detail.html index 2736a407..c6368a99 100755 --- a/atlas/templates/atlas/case_detail.html +++ b/atlas/templates/atlas/case_detail.html @@ -3,33 +3,7 @@ {% block content %} - -
    - Edit - Clone - Delete - Add - Note - Authors - {% if request.user.is_superuser %} - Admin Edit - {% endif %} - - {% if collection %} -
    - - {% if previous %} - Previous question - {% endif %} - Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}] - {% if next %} - Next question - {% endif %} -
    - {% endif %} -
    + {% include 'atlas/question_link_header.html' %} {% include 'atlas/case_display_block.html' %} diff --git a/atlas/templates/atlas/case_form.html b/atlas/templates/atlas/case_form.html index 0e43584b..3e3c81c3 100755 --- a/atlas/templates/atlas/case_form.html +++ b/atlas/templates/atlas/case_form.html @@ -49,6 +49,10 @@ {% endblock %} {% block content %} + {% if object %} + {% include "atlas/question_link_header.html" %} + {% endif %} +

    Submit Case

    Use this form to create a atlas case. Existing associated image sets can be added using this form. diff --git a/atlas/templates/atlas/collection_headers.html b/atlas/templates/atlas/collection_headers.html index 27ded27e..ac776a7b 100644 --- a/atlas/templates/atlas/collection_headers.html +++ b/atlas/templates/atlas/collection_headers.html @@ -1,15 +1,14 @@ - {% if request.user.is_authenticated %}
    Collection: {{collection.name}}-> Overview / Mark / Scores / Candidates / Add New Case / - Authors
    Edit \ Delete \ Clone + \ Authors {% if request.user.is_superuser %} \ Admin Edit diff --git a/atlas/templates/atlas/question_link_header.html b/atlas/templates/atlas/question_link_header.html new file mode 100644 index 00000000..22c412ce --- /dev/null +++ b/atlas/templates/atlas/question_link_header.html @@ -0,0 +1,26 @@ +
    + Edit + Clone + Delete + Add + Note + Authors + {% if request.user.is_superuser %} + Admin Edit + {% endif %} + + {% if collection %} +
    + + {% if previous %} + Previous question + {% endif %} + Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}] + {% if next %} + Next question + {% endif %} +
    + {% endif %} +
    diff --git a/atlas/views.py b/atlas/views.py index 96e07ad3..4fafb979 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -2247,7 +2247,17 @@ class CaseCollectionAuthorUpdate(RevisionMixin, AuthorRequiredMixin, UpdateView) form_class = CaseCollectionAuthorForm template_name = "author_form.html" + def get_context_data(self, **kwargs): + context = super(CaseCollectionAuthorUpdate, self).get_context_data(**kwargs) + context["collection"] = context["object"] + return context + class CaseAuthorUpdate(RevisionMixin, AuthorRequiredMixin, UpdateView): model = Case form_class = CaseAuthorForm - template_name = "author_form.html" \ No newline at end of file + template_name = "author_form.html" + + def get_context_data(self, **kwargs): + context = super(CaseAuthorUpdate, self).get_context_data(**kwargs) + context["collection"] = context["object"] + return context \ No newline at end of file diff --git a/generic/mixins.py b/generic/mixins.py index 0fd682a6..73737ad0 100644 --- a/generic/mixins.py +++ b/generic/mixins.py @@ -8,6 +8,29 @@ class SuperuserRequiredMixin(UserPassesTestMixin): def test_func(self): return self.request.user.is_superuser +class CheckCanEditMixin(): + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + print(self.object) + print("1234", self.object.check_user_can_edit(self.request.user)) + context["can_edit"] = self.object.check_user_can_edit(self.request.user) + + return context + + + +class QuestionMixin(): + + def get_app_name(self): + raise NotImplementedError("You must implement get_app_name in the derived class") + + def get_base_template(self): + return f"{self.get_app_name()}/base.html" + + def get_link_headers(self): + return f"{self.get_app_name()}/question_link_header.html" + class AuthorMixin(): """Mixin class for models that have authors @@ -15,7 +38,7 @@ class AuthorMixin(): """ author = models.ManyToManyField(User) # Corrected typing - + def get_author_objects(self) -> List[User]: # Updated type hint """Returns list of author objects""" authors = [i for i in self.author.all()] diff --git a/generic/models.py b/generic/models.py index e5d32a4e..a0ca3931 100644 --- a/generic/models.py +++ b/generic/models.py @@ -26,7 +26,7 @@ from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver -from generic.mixins import AuthorMixin +from generic.mixins import AuthorMixin, QuestionMixin from helpers.images import get_image_hash, pretty_print_dicom from rad.settings import REMOTE_URL from django.utils.html import format_html @@ -154,7 +154,7 @@ class Site(models.Model): return self.short_code -class QuestionBase(models.Model, AuthorMixin): +class QuestionBase(models.Model, AuthorMixin, QuestionMixin): authors_only = models.BooleanField( help_text="If true only question authors will be able to view.", default=False, @@ -580,6 +580,15 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin): class Meta: abstract = True + def get_app_name(self): + raise NotImplementedError("You must implement get_app_name in the derived class") + + def get_base_template(self): + return f"{self.get_app_name()}/exams.html" + + def get_link_headers(self): + return "generic/exam_link_headers.html" + def check_user_can_edit(self, user: User): if user.is_superuser: return True diff --git a/generic/templates/generic/exam_link_headers.html b/generic/templates/generic/exam_link_headers.html new file mode 100644 index 00000000..f973b2d8 --- /dev/null +++ b/generic/templates/generic/exam_link_headers.html @@ -0,0 +1,10 @@ +
    +{% if can_edit %} + Edit + \ Delete + \ Clone + \ Authors +{% endif %} +{% if request.user.is_superuser %} + \ Admin Edit +{% endif %} \ No newline at end of file diff --git a/generic/templates/generic/exam_overview_headers.html b/generic/templates/generic/exam_overview_headers.html index dbe291cf..5af693c7 100644 --- a/generic/templates/generic/exam_overview_headers.html +++ b/generic/templates/generic/exam_overview_headers.html @@ -1,12 +1,4 @@ -{% if can_edit %} - Edit - \ Delete - \ Clone - \ Authors -{% endif %} -{% if request.user.is_superuser %} - \ Admin Edit -{% endif %} +{% include "generic/exam_link_headers.html" %}

    Exam: {{ exam.name }}

    {% include 'exam_notes.html' %} @@ -15,7 +7,7 @@ {% endif %} @@ -44,15 +36,15 @@ Exam mode: {{ exam.exam_mode }}
    Open access: {{ exam.open_access }}
    - Exam active: [When checked the exam will be available to take in the test system] + Exam active: [When checked the exam will be available to take in the test system]
    {% if exam.exam_mode %}
    - Publish results: [When checked the exam results will be available to users on this site]
    - {% if app_name == "anatomy" or app_name == "rapids" or app_name == "longs" %} -

    + {% if exam.get_app_name == "anatomy" or exam.get_app_name == "rapids" or exam.get_app_name == "longs" %} +

    {% endif %} {% endif %} diff --git a/longs/models.py b/longs/models.py index 12836d5d..a4e4f418 100644 --- a/longs/models.py +++ b/longs/models.py @@ -131,6 +131,9 @@ class Long(QuestionBase): # question_file = models.FileField(upload_to=question_file_directory_path, blank=True, null=True) + def get_app_name(self): + return "longs" + def get_absolute_url(self): return reverse("longs:question_detail", kwargs={"pk": self.pk}) @@ -472,6 +475,9 @@ class Exam(ExamBase): examcollection = models.ForeignKey(ExamCollection, blank=True, null=True, on_delete=models.SET_NULL, related_name="longs_exams") + def get_app_name(self): + return "longs" + def get_exam_question_json(self, question_id): q = get_object_or_404(Long, pk=question_id) diff --git a/longs/templates/longs/exam_overview.html b/longs/templates/longs/exam_overview.html index 03998bff..a01491b0 100644 --- a/longs/templates/longs/exam_overview.html +++ b/longs/templates/longs/exam_overview.html @@ -1,10 +1,14 @@ {% extends 'longs/exams.html' %} +{% block navigation %} + {{ block.super }} + {% include 'generic/exam_overview_headers.html' %} +{% endblock navigation %} + {% block content %} {% load thumbnail %}
    - {% include 'generic/exam_overview_headers.html' %}
      diff --git a/longs/views.py b/longs/views.py index 68287f5a..3cd2fc31 100755 --- a/longs/views.py +++ b/longs/views.py @@ -13,7 +13,7 @@ from django.core.exceptions import ( from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic.detail import DetailView -from generic.mixins import SuperuserRequiredMixin +from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic import ListView @@ -1130,7 +1130,7 @@ class ExamCreate(ExamCreateBase): form_class = ExamForm -class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): +class ExamUpdate(CheckCanEditMixin, ExamUpdateBase, AuthorOrCheckerRequiredMixin): model = Exam form_class = ExamForm @@ -1142,9 +1142,10 @@ class ExamClone(ExamCloneMixin, ExamCreate): """Clone exam view""" -class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView): +class ExamAuthorUpdate(RevisionMixin, CheckCanEditMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView): model = Exam form_class = ExamAuthorForm + template_name = "author_form.html" #class ExamViewSet(RevisionMixin, viewsets.ModelViewSet): diff --git a/physics/models.py b/physics/models.py index 40461efb..378cd572 100644 --- a/physics/models.py +++ b/physics/models.py @@ -93,6 +93,9 @@ class Question(QuestionBase): #notes = GenericRelation(QuestionNote) + def get_app_name(self): + return "physics" + def __str__(self): return self.stem @@ -194,6 +197,9 @@ class Exam(ExamBase): def get_take_url(self): return reverse("physics:exam_start", kwargs={"pk": self.pk}) + def get_app_name(self): + return "physics" + @reversion.register class UserAnswer(UserAnswerBase): diff --git a/physics/templates/physics/exam_overview.html b/physics/templates/physics/exam_overview.html index 7c035b1a..b8f3ccb9 100644 --- a/physics/templates/physics/exam_overview.html +++ b/physics/templates/physics/exam_overview.html @@ -1,10 +1,13 @@ {% extends 'physics/exams.html' %} +{% block navigation %} + {{ block.super }} + {% include 'generic/exam_overview_headers.html' %} +{% endblock navigation %} {% block content %} {% load thumbnail %}
      - {% include 'generic/exam_overview_headers.html' %} This exam will be available to take here (when active). diff --git a/physics/views.py b/physics/views.py index 302d6430..5a56127c 100644 --- a/physics/views.py +++ b/physics/views.py @@ -3,7 +3,7 @@ from django.utils import timezone from generic.models import CidUser from physics.decorators import user_is_author_or_physics_checker from physics.filters import QuestionFilter, UserAnswerFilter -from generic.mixins import SuperuserRequiredMixin +from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin from physics.tables import QuestionTable, UserAnswerTable from django.shortcuts import render, get_object_or_404, redirect from django.views.decorators.csrf import csrf_exempt @@ -339,7 +339,7 @@ class ExamCreate(ExamCreateBase): form_class = ExamForm -class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): +class ExamUpdate(CheckCanEditMixin, ExamUpdateBase, AuthorOrCheckerRequiredMixin): model = Exam form_class = ExamForm @@ -353,10 +353,11 @@ class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate): class ExamAuthorUpdate( - RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView + RevisionMixin, CheckCanEditMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView ): model = Exam form_class = ExamAuthorForm + template_name = "author_form.html" class QuestionView( diff --git a/rapids/models.py b/rapids/models.py index 3117c7d4..3a9cc8c8 100644 --- a/rapids/models.py +++ b/rapids/models.py @@ -224,6 +224,9 @@ class Rapid(QuestionBase): default=False, help_text="Question has been scrapped and will not be shown" ) + def get_app_name(self): + return "rapids" + def get_absolute_url(self): return reverse("rapids:question_detail", kwargs={"pk": self.pk}) @@ -652,6 +655,10 @@ class Exam(ExamBase): examcollection = models.ForeignKey(ExamCollection, blank=True, null=True, on_delete=models.SET_NULL, related_name="rapids_exams") + + def get_app_name(self): + return "rapids" + def get_normal_abnormal_breakdown(self): # Inefficient but more extendible questions = self.exam_questions.all() diff --git a/rapids/templates/rapids/exam_overview.html b/rapids/templates/rapids/exam_overview.html index 39e16c0d..8a2c6131 100644 --- a/rapids/templates/rapids/exam_overview.html +++ b/rapids/templates/rapids/exam_overview.html @@ -1,10 +1,13 @@ {% extends 'rapids/exams.html' %} +{% block navigation %} + {{ block.super }} + {% include 'generic/exam_overview_headers.html' %} +{% endblock navigation %} {% block content %} {% load thumbnail %}
      - {% include 'generic/exam_overview_headers.html' %}

      diff --git a/rapids/views.py b/rapids/views.py index 7f712e63..5afb8c8c 100755 --- a/rapids/views.py +++ b/rapids/views.py @@ -90,7 +90,7 @@ from .serializers import ( UserAnswerSerializer, ) -from generic.mixins import SuperuserRequiredMixin +from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin from dal import autocomplete @@ -213,12 +213,14 @@ def rapid_clone(request, pk): new_item.pk = None # autogen a new pk (item_id) # new_item.name = "Copy of " + new_item.name #need to change uniques - if request.user not in new_item.get_author_objects() and not request.user.is_superuser: + if ( + request.user not in new_item.get_author_objects() + and not request.user.is_superuser + ): raise PermissionDenied() # or Http404 form = RapidForm(request.POST or None, instance=new_item) - image_formset = ImageFormSet() answer_formset = AnswerFormSet() @@ -240,7 +242,7 @@ def rapid_clone(request, pk): context = { "form": form, "image_formset": image_formset, - "answer_formset": answer_formset + "answer_formset": answer_formset, # other context } @@ -331,6 +333,7 @@ class RapidCreate(RapidCreateBase): # form.instance.author.add(self.request.user.id) # return super().form_valid(form) + class RapidAnswerUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView): model = Rapid form_class = RapidQuestionAnswerForm @@ -353,7 +356,7 @@ class RapidAnswerUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView) self.object = form.save(commit=False) self.object.save() - #form.instance.author.add(self.request.user.id) + # form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) formset = context["answer_formset"] @@ -367,9 +370,8 @@ class RapidAnswerUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView) else: return super().form_invalid(form) -class RapidUpdate( - RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView -): + +class RapidUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView): model = Rapid form_class = RapidForm @@ -728,13 +730,14 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False): ) - class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView): model = Rapid success_url = reverse_lazy("rapids:rapid_view") -GenericExamViews = ExamViews(Exam, Rapid, Answer, UserAnswer, "rapids", "rapid", normalise_score=normaliseScore) +GenericExamViews = ExamViews( + Exam, Rapid, Answer, UserAnswer, "rapids", "rapid", normalise_score=normaliseScore +) class ExamCreate(ExamCreateBase): @@ -742,7 +745,7 @@ class ExamCreate(ExamCreateBase): form_class = ExamForm -class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): +class ExamUpdate(CheckCanEditMixin, ExamUpdateBase, AuthorOrCheckerRequiredMixin): model = Exam form_class = ExamForm @@ -750,12 +753,17 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase): model = Exam + class ExamAuthorUpdate( - RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView + RevisionMixin, + CheckCanEditMixin, + LoginRequiredMixin, + AuthorRequiredMixin, + UpdateView, ): model = Exam form_class = ExamAuthorForm - + template_name = "author_form.html" class UserAnswerView(LoginRequiredMixin, DetailView): @@ -798,24 +806,24 @@ class ExamViewSet(RevisionMixin, viewsets.ModelViewSet): """ user = self.request.user if user.groups.filter(name="rapid_checker").exists(): - return Exam.objects.filter(archive=False).order_by('name') + return Exam.objects.filter(archive=False).order_by("name") - return Exam.objects.filter(author__id=user.id, archive=False).order_by('name') + return Exam.objects.filter(author__id=user.id, archive=False).order_by("name") -#class QuestionAnswerViewSet(viewsets.ModelViewSet): +# class QuestionAnswerViewSet(viewsets.ModelViewSet): # queryset = Answer.objects.all() # .order_by('name') # serializer_class = QuestionAnswerSerializer # permission_classes = [permissions.IsAuthenticated] # # -#class UserAnswerViewSet(viewsets.ModelViewSet): +# class UserAnswerViewSet(viewsets.ModelViewSet): # queryset = UserAnswer.objects.all() # .order_by('name') # serializer_class = UserAnswerSerializer # permission_classes = [permissions.IsAuthenticated] -#class RapidViewSet( +# class RapidViewSet( # RevisionMixin, # mixins.CreateModelMixin, # mixins.RetrieveModelMixin, @@ -823,14 +831,13 @@ class ExamViewSet(RevisionMixin, viewsets.ModelViewSet): # mixins.ListModelMixin, # mixins.DestroyModelMixin, # viewsets.GenericViewSet, -#): +# ): # queryset = Rapid.objects.all() # .order_by('name') # serializer_class = RapidSerializer # permission_classes = [permissions.IsAuthenticated] # pagination_class = StandardResultsSetPagination - # class AddSuggestedAnswer(LoginRequiredMixin, CreateView): # model = QuestionNote # form_class = QuestionNoteForm @@ -968,4 +975,4 @@ class AnswerAutocomplete(autocomplete.Select2QuerySetView): if self.q: qs = qs.filter(answer__istartswith=self.q) - return qs \ No newline at end of file + return qs diff --git a/sbas/models.py b/sbas/models.py index c9f96046..e74fd4a1 100644 --- a/sbas/models.py +++ b/sbas/models.py @@ -108,6 +108,9 @@ class Question(QuestionBase): if self.e_answer: self.e_answer = self.e_answer.strip() + def get_app_name(self): + return "sbas" + def get_absolute_url(self): return reverse("sbas:question_detail", kwargs={"pk": self.pk}) @@ -210,6 +213,9 @@ class Exam(ExamBase): def get_take_url(self): return reverse("sbas:exam_start", kwargs={"pk": self.pk}) + def get_app_name(self): + return "sbas" + @reversion.register class UserAnswer(UserAnswerBase): diff --git a/sbas/templates/sbas/exam_overview.html b/sbas/templates/sbas/exam_overview.html index c50abd32..05996df1 100644 --- a/sbas/templates/sbas/exam_overview.html +++ b/sbas/templates/sbas/exam_overview.html @@ -1,10 +1,14 @@ {% extends 'sbas/exams.html' %} +{% block navigation %} + {{ block.super }} + {% include 'generic/exam_overview_headers.html' %} +{% endblock navigation %} + {% block content %} {% load thumbnail %}
      - {% include 'generic/exam_overview_headers.html' %} This exam will be available to take here (when active). @@ -47,17 +51,17 @@ }) // $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly .done(function (data) { - console.log(data); + console.log(data); - if (data.status == "success") { - toastr.info('Exam state changed.') - } + if (data.status == "success") { + toastr.info('Exam state changed.') + } // show some message according to the response. // For eg. A message box showing that the status has been changed - }) + }) .always(function () { - console.log('[Done]'); - }) + console.log('[Done]'); + }) }) $("#exam-publish-results-switch").on("change", function () { $.ajax({ @@ -71,17 +75,17 @@ }) // $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly .done(function (data) { - console.log(data); + console.log(data); - if (data.status == "success") { - toastr.info('Publish results state changed.') - } + if (data.status == "success") { + toastr.info('Publish results state changed.') + } // show some message according to the response. // For eg. A message box showing that the status has been changed - }) + }) .always(function () { - console.log('[Done]'); - }) + console.log('[Done]'); + }) }) }); diff --git a/sbas/views.py b/sbas/views.py index 64942291..ddda217d 100644 --- a/sbas/views.py +++ b/sbas/views.py @@ -1,5 +1,5 @@ from reversion.views import RevisionMixin -from generic.mixins import SuperuserRequiredMixin +from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin from django.views.generic.detail import DetailView from generic.models import CidUser from sbas.forms import UserAnswerForm, ExamAuthorForm, ExamForm @@ -402,7 +402,7 @@ class ExamCreate(ExamCreateBase): form_class = ExamForm -class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin): +class ExamUpdate(CheckCanEditMixin, ExamUpdateBase, AuthorOrCheckerRequiredMixin): model = Exam form_class = ExamForm @@ -428,11 +428,12 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase): class ExamAuthorUpdate( - RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView + RevisionMixin, CheckCanEditMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView ): model = Exam form_class = ExamAuthorForm + template_name = "author_form.html" class ExamClone(ExamCloneMixin, ExamCreate): """Clone exam view""" diff --git a/templates/author_form.html b/templates/author_form.html index e1e49897..9a20eb76 100644 --- a/templates/author_form.html +++ b/templates/author_form.html @@ -1,10 +1,17 @@ -{% extends "base.html" %} +{% extends object.get_base_template %} {% block js %} {{ form.media }} {% endblock %} + +{% block navigation %} +{{ block.super }} + {% include object.get_link_headers %} +{% endblock navigation %} + + {% block content %}

      Edit Authors

      diff --git a/templates/exam_update_form.html b/templates/exam_update_form.html index 79aea4d2..00145258 100644 --- a/templates/exam_update_form.html +++ b/templates/exam_update_form.html @@ -4,8 +4,15 @@ {{ form.media }} {% endblock %} -{% block content %} +{% block navigation %} + {{ block.super }} + {% if object %} + {% include object.get_link_headers %} + {% endif %} +{% endblock navigation %} + +{% block content %}

      Update Exam

      {% csrf_token %}