From a2907fa285b701658c15c17198978dcf29cf91ef Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 28 Oct 2024 16:30:13 +0000 Subject: [PATCH] numerous supervisor upgrades --- .../0024_exam_results_supervisor_visible.py | 18 +++ ...secollection_results_supervisor_visible.py | 18 +++ generic/forms.py | 1 + ...19_examuserstatus_share_with_supervisor.py | 18 +++ ...erstatus_share_with_supervisor_and_more.py | 22 +++ generic/models.py | 7 + generic/templates/generic/base.html | 1 + .../templates/generic/base_supervisor.html | 23 +++ .../generic/exam_overview_headers.html | 1 + .../generic/supervisor_overview.html | 43 ++++++ .../generic/supervisor_request_account.html | 21 +++ .../templates/generic/supervisor_trainee.html | 38 +++++ generic/urls.py | 16 ++ generic/views.py | 142 +++++++++++++++++- .../0031_exam_results_supervisor_visible.py | 18 +++ .../0017_exam_results_supervisor_visible.py | 18 +++ rad/views.py | 28 +--- .../0016_exam_results_supervisor_visible.py | 18 +++ .../0017_exam_results_supervisor_visible.py | 18 +++ templates/index.html | 3 + templates/user_score_header.html | 10 ++ 21 files changed, 455 insertions(+), 27 deletions(-) create mode 100644 anatomy/migrations/0024_exam_results_supervisor_visible.py create mode 100644 atlas/migrations/0063_casecollection_results_supervisor_visible.py create mode 100644 generic/migrations/0019_examuserstatus_share_with_supervisor.py create mode 100644 generic/migrations/0020_remove_examuserstatus_share_with_supervisor_and_more.py create mode 100755 generic/templates/generic/base_supervisor.html create mode 100644 generic/templates/generic/supervisor_overview.html create mode 100644 generic/templates/generic/supervisor_request_account.html create mode 100644 generic/templates/generic/supervisor_trainee.html create mode 100644 longs/migrations/0031_exam_results_supervisor_visible.py create mode 100644 physics/migrations/0017_exam_results_supervisor_visible.py create mode 100644 rapids/migrations/0016_exam_results_supervisor_visible.py create mode 100644 sbas/migrations/0017_exam_results_supervisor_visible.py diff --git a/anatomy/migrations/0024_exam_results_supervisor_visible.py b/anatomy/migrations/0024_exam_results_supervisor_visible.py new file mode 100644 index 00000000..0cfa63e2 --- /dev/null +++ b/anatomy/migrations/0024_exam_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('anatomy', '0023_exam_exam_open_access'), + ] + + operations = [ + migrations.AddField( + model_name='exam', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/atlas/migrations/0063_casecollection_results_supervisor_visible.py b/atlas/migrations/0063_casecollection_results_supervisor_visible.py new file mode 100644 index 00000000..930623f3 --- /dev/null +++ b/atlas/migrations/0063_casecollection_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('atlas', '0062_alter_casecollection_markers'), + ] + + operations = [ + migrations.AddField( + model_name='casecollection', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/generic/forms.py b/generic/forms.py index a280c8ea..7106109a 100755 --- a/generic/forms.py +++ b/generic/forms.py @@ -201,6 +201,7 @@ class ExamFormMixin: "active", "publish_results", "archive", + "results_supervisor_visible" # "cid_user_groups", # "user_user_groups", # "author", diff --git a/generic/migrations/0019_examuserstatus_share_with_supervisor.py b/generic/migrations/0019_examuserstatus_share_with_supervisor.py new file mode 100644 index 00000000..5557f480 --- /dev/null +++ b/generic/migrations/0019_examuserstatus_share_with_supervisor.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 13:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generic', '0018_alter_supervisor_user'), + ] + + operations = [ + migrations.AddField( + model_name='examuserstatus', + name='share_with_supervisor', + field=models.BooleanField(default=False, help_text='If true the exam status and results will be available to a users supervisor'), + ), + ] diff --git a/generic/migrations/0020_remove_examuserstatus_share_with_supervisor_and_more.py b/generic/migrations/0020_remove_examuserstatus_share_with_supervisor_and_more.py new file mode 100644 index 00000000..20c27066 --- /dev/null +++ b/generic/migrations/0020_remove_examuserstatus_share_with_supervisor_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.2 on 2024-10-28 13:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('generic', '0019_examuserstatus_share_with_supervisor'), + ] + + operations = [ + migrations.RemoveField( + model_name='examuserstatus', + name='share_with_supervisor', + ), + migrations.AddField( + model_name='ciduserexam', + name='share_with_supervisor', + field=models.BooleanField(default=False, help_text='If true the exam status and results will be available to a users supervisor'), + ), + ] diff --git a/generic/models.py b/generic/models.py index 91b12377..bf2f0894 100644 --- a/generic/models.py +++ b/generic/models.py @@ -602,6 +602,11 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin): default=False, ) + results_supervisor_visible = models.BooleanField( + help_text="If true the results will be visible to supervisors without candidate approval.", + default=False, + ) + class Meta: abstract = True @@ -1463,6 +1468,8 @@ class CidUserExam(models.Model): # TODO switch to json field? results_emailed_status = models.CharField(max_length=255, blank=True) + share_with_supervisor = models.BooleanField(default=False, help_text="If true the exam status and results will be available to a users supervisor") + def __str__(self) -> str: if self.cid_user is None: try: diff --git a/generic/templates/generic/base.html b/generic/templates/generic/base.html index bde61e8c..a42ffbcf 100755 --- a/generic/templates/generic/base.html +++ b/generic/templates/generic/base.html @@ -21,6 +21,7 @@ Cids (exams) / Cid Groups / User Groups / + Trainees / Manage Users / Manage Supervisors / Collections diff --git a/generic/templates/generic/base_supervisor.html b/generic/templates/generic/base_supervisor.html new file mode 100755 index 00000000..cf0c291f --- /dev/null +++ b/generic/templates/generic/base_supervisor.html @@ -0,0 +1,23 @@ +{% extends 'base.html' %} + +{% block title %} + Supervisor +{% endblock %} + +{% block css %} +{% endblock %} + +{% block js %} +{% endblock %} + + + +{% block content %} +{% endblock %} +{% block navigation %} + Supervisor: + {% if request.user.is_authenticated %} + Overview / + + {% endif %} +{% endblock %} diff --git a/generic/templates/generic/exam_overview_headers.html b/generic/templates/generic/exam_overview_headers.html index 3161ec17..2b8fc48f 100644 --- a/generic/templates/generic/exam_overview_headers.html +++ b/generic/templates/generic/exam_overview_headers.html @@ -113,6 +113,7 @@ Open access: {{ exam.open_access }}
Author(s): {% for author in exam.author.all %} {{ author }}{% if not forloop.last %}, {% endif %} {% endfor %}
+Supervisor visible: {{exam.results_supervisor_visible}}
Extra diff --git a/generic/templates/generic/supervisor_overview.html b/generic/templates/generic/supervisor_overview.html new file mode 100644 index 00000000..cadfc000 --- /dev/null +++ b/generic/templates/generic/supervisor_overview.html @@ -0,0 +1,43 @@ +{% extends 'generic/base_supervisor.html' %} + +{% load crispy_forms_tags %} + +{% block content %} + +

Supervisor: {{supervisor.name}}

+
+ Help +

As a supervisor you have the ability to view results on the platform for your associated trainees.

+

Some exams / collection results will be automatically available for you to review, others may require the trainee to allow access

+

If your trainees are incorrect please contact ross.kruger@nhs.net

+ +
+ + +

Trainees

+You have the following trainee(s): + + + +{% endblock %} + + +{% block css %} + + +{% endblock css %} + + diff --git a/generic/templates/generic/supervisor_request_account.html b/generic/templates/generic/supervisor_request_account.html new file mode 100644 index 00000000..3316a2b7 --- /dev/null +++ b/generic/templates/generic/supervisor_request_account.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} + +{% load crispy_forms_tags %} +{% block css %} +{% endblock %} + +{% block content %} + +This form allows you to create an account if you are a supervisor. To do so please enter your email address. + +
+ + +
+
+ +{% endblock %} + diff --git a/generic/templates/generic/supervisor_trainee.html b/generic/templates/generic/supervisor_trainee.html new file mode 100644 index 00000000..7d04af3a --- /dev/null +++ b/generic/templates/generic/supervisor_trainee.html @@ -0,0 +1,38 @@ +{% extends 'generic/base_supervisor.html' %} + +{% load crispy_forms_tags %} +{% block css %} +{% endblock %} + +{% block content %} + +

Trainee: {{trainee.first_name}} {{trainee.last_name}}

+email: {{trainee.email}} + + +

Results

+
+ {% if all_exams %} + The following exam results for the trainee are available. + {% for exam_type, exams in all_exams %} + {% if exams %} +

{{exam_type|title}}

+
    + {% for exam in exams %} +
  • + + + + {{exam}} {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}
  • + {% endfor %} +
+ {% endif %} + {% endfor %} + {% else %} + No results are available for this trainee. +{% endif %} +
+ + +{% endblock %} + diff --git a/generic/urls.py b/generic/urls.py index 023265a6..8e17b889 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -166,6 +166,17 @@ urlpatterns = [ path("cids//details", views.cid_details, name="cid_details"), path("cids/create/email", views.create_cid_email, name="create_cid_email"), path("supervisor", views.SupervisorList.as_view(), name="supervisor"), + path("supervisor/request_account", views.supervisor_request_account, name="supervisor_request_account"), + path( + "supervisor//overview", + views.supervisor_overview, + name="supervisor_overview", + ), + path( + "supervisor//trainee//", + views.supervisor_trainee, + name="supervisor_trainee", + ), path( "supervisor/", views.SupervisorDetail.as_view(), @@ -342,6 +353,11 @@ def generic_exam_urls(generic_exam_view: GenericExamViews): generic_exam_view.exam_scores_user_admin, name="exam_scores_user_admin", ), + path( + "exam//scores//user/supervisor", + generic_exam_view.exam_scores_user_supervisor, + name="exam_scores_user_supervisor", + ), path( "exam//toggle_active", generic_exam_view.exam_toggle_active, diff --git a/generic/views.py b/generic/views.py index b732d082..4e0258ba 100644 --- a/generic/views.py +++ b/generic/views.py @@ -98,12 +98,14 @@ from rapids.models import ( ExamQuestionDetail as RapidsExamQuestionDetail, ) from rapids.models import Exam as RapidsExam +from rapids.models import UserAnswer as RapidsUserAnswer from longs.models import ( Long as LongQuestion, LongSeries, ExamQuestionDetail as LongsExamQuestionDetail, ) from longs.models import Exam as LongsExam +from longs.models import UserAnswer as LongsUserAnswer from anatomy.models import ( AnatomyQuestion as AnatomyQuestion, ExamQuestionDetail as AnatomyExamQuestionDetail, @@ -113,12 +115,15 @@ from sbas.models import ( Question as SbasQuestion, ExamQuestionDetail as SbasExamQuestionDetail, ) +from anatomy.models import UserAnswer as AnatomyUserAnswer from sbas.models import Exam as SbasExam +from sbas.models import UserAnswer as SbasUserAnswer from physics.models import ( Question as PhysicsQuestion, ExamQuestionDetail as PhysicsExamQuestionDetail, ) from physics.models import Exam as PhysicsExam +from physics.models import UserAnswer as PhysicsUserAnswer from django.db.models import Case, When from django.conf import settings @@ -164,6 +169,34 @@ class CidManagerRequiredMixin(UserPassesTestMixin): # raise PermissionDenied() # or Http404 +def get_user_exams(user, supervisor_view=False): + EXAM_ANSWER_MAP = { + "physics": (PhysicsUserAnswer, PhysicsExam), + "anatomy": (AnatomyUserAnswer, AnatomyExam), + "rapids": (RapidsUserAnswer, RapidsExam), + "longs": (LongsUserAnswer, LongsExam), + "sbas": (SbasUserAnswer, SbasExam), + } + kwargs = { + "exam_mode": True, + "archive": False + } + + if supervisor_view: + kwargs["results_supervisor_visible"] = True + + exams = [] + for exam_type in EXAM_ANSWER_MAP: + UserAnswer, Exam = EXAM_ANSWER_MAP[exam_type] + exam_answers = UserAnswer.objects.filter(user=user) + exam_ids = exam_answers.values_list("exam").distinct() + exams_to_add = Exam.objects.filter(id__in=exam_ids, **kwargs) + if exams_to_add: + exams.append((exam_type, exams_to_add)) + + return exams + + def get_exam_model_from_app_name(app_name: str) -> ExamBase: EXAM_MAP = { "physics": PhysicsExam, @@ -494,6 +527,9 @@ class ExamViews(View, LoginRequiredMixin): if (exam.open_access and exam.active) or user in exam.get_author_objects(): return True + if exam.cid_user_exam.filter(user_user=user): + return True + if marker and user in exam.markers.all(): return True @@ -1521,7 +1557,9 @@ class ExamViews(View, LoginRequiredMixin): exams = self.Exam.objects.all() filter = self.ExtraExamFilter(request.GET, queryset=exams) else: - exams = self.Exam.objects.filter(author__id=request.user.id) | self.Exam.objects.filter(open_access=True) + exams = self.Exam.objects.filter( + author__id=request.user.id + ) | self.Exam.objects.filter(open_access=True) filter = self.BasicExamFilter(request.GET, queryset=exams) return render( @@ -2044,21 +2082,38 @@ class ExamViews(View, LoginRequiredMixin): user = User.objects.get(id=user_id) return self.exam_scores_cid_user(request, pk, user=user) + def exam_scores_user_supervisor(self, request, pk, user_id): + user = User.objects.get(id=user_id) + if not request.user.supervisor == user.userprofile.supervisor: + raise PermissionDenied + + return self.exam_scores_cid_user(request, pk, user=user, supervisor=request.user) + @method_decorator(user_passes_test(lambda u: u.is_superuser)) def exam_scores_cid_user_admin(self, request, pk, cid): user = CidUser.objects.get(cid=cid) return self.exam_scores_cid_user(request, pk, cid, user.passcode) - def exam_scores_cid_user(self, request, pk, cid=None, passcode="", user=None): + def exam_scores_cid_user(self, request, pk, cid=None, passcode="", user=None, supervisor=None): exam: ExamBase = get_object_or_404(self.Exam, pk=pk) if not exam.exam_mode: raise Http404("Packet not in exam mode") - if not exam.check_cid_user(cid, passcode, request.user): - if not self.check_user_access(request.user, pk): - # raise PermissionDenied - raise Http404("Error accessing exam") + if supervisor is None: + if not exam.check_cid_user(cid, passcode, request.user): + #TODO clear this up + # Should users be able to access results for exams they do not have access to (if they have results)? + if not self.check_user_access(request.user, pk): + # raise PermissionDenied + #if request.user.supervisor == user.userprofile.supervisor: + # pass + + # check for supervisor access + raise Http404("Error accessing exam") + else: + if user is None: + raise Exception("user must be defined") if user is not None: request.user = user @@ -2176,6 +2231,8 @@ class ExamViews(View, LoginRequiredMixin): case _: max_score = len(questions) + cid_user_exam = exam.cid_user_exam.filter(user_user=user).first() + template_context = { "exam": exam, "cid": cid, @@ -2187,6 +2244,8 @@ class ExamViews(View, LoginRequiredMixin): "max_score": max_score, "answers_and_marks": answers_and_marks, "view_all_results": view_all_results, + "supervisor": supervisor, + "cid_user_exam": cid_user_exam, } if self.normalise_score is not None: @@ -3754,3 +3813,74 @@ class UpdateQuestionMixin(RedirectMixin, RevisionMixin, UpdateView): if self.request.user in obj.get_author_objects(): return context raise PermissionDenied() # or Http404 + + +def supervisor_overview(request, pk): + supervisor = get_object_or_404(Supervisor, pk=pk) + + if not request.user.supervisor == supervisor: + raise PermissionDenied() + + trainees = User.objects.filter(userprofile__supervisor=supervisor) + + return render( + request, + "generic/supervisor_overview.html", + {"supervisor": supervisor, "trainees": trainees}, + ) + + +def supervisor_trainee(request, pk, trainee_id): + supervisor = get_object_or_404(Supervisor, pk=pk) + + if not request.user.supervisor == supervisor: + raise PermissionDenied() + + # We do this to check that the supervisor can (should) see the trainee results + trainee = User.objects.get(userprofile__supervisor=supervisor, pk=trainee_id) + + exams = get_user_exams(trainee, supervisor_view=True) + + print(trainee) + + return render( + request, + "generic/supervisor_trainee.html", + {"supervisor": supervisor, "trainee": trainee, "all_exams": exams}, + ) + +def supervisor_request_account(request): + if request.method == "POST": + email = request.POST.get("email") + try: + validate_email(email) + except ValidationError: + return HttpResponse(f"Invalid email address ({email})") + + try: + supervisor = Supervisor.objects.get(email=email) + except Supervisor.DoesNotExist: + return HttpResponse(f"Invalid email address ({email})") + + if supervisor.user: + return HttpResponse(format_html("User already exists. Reset your password here", reverse("password_reset"))) + + try: + user_account = User.objects.get(email=email) + + except User.DoesNotExist: + + try: + first, last = supervisor.name.split(" ", 1) + except ValueError: + first, last = supervisor.name, "" + user_account = User.objects.create_user(email=email, username=email, password=secrets.token_hex(nbytes=16), first_name=first, last_name=last) + + supervisor.user = user_account + supervisor.save() + + return HttpResponse(format_html("Account created, you now need to reset your password here", reverse("password_reset"))) + + else: + + return render(request, "generic/supervisor_request_account.html", {}) diff --git a/longs/migrations/0031_exam_results_supervisor_visible.py b/longs/migrations/0031_exam_results_supervisor_visible.py new file mode 100644 index 00000000..8aae300c --- /dev/null +++ b/longs/migrations/0031_exam_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('longs', '0030_exam_exam_open_access'), + ] + + operations = [ + migrations.AddField( + model_name='exam', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/physics/migrations/0017_exam_results_supervisor_visible.py b/physics/migrations/0017_exam_results_supervisor_visible.py new file mode 100644 index 00000000..ff8e689a --- /dev/null +++ b/physics/migrations/0017_exam_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('physics', '0016_exam_exam_open_access'), + ] + + operations = [ + migrations.AddField( + model_name='exam', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/rad/views.py b/rad/views.py index 8f439db2..ea7b03d9 100644 --- a/rad/views.py +++ b/rad/views.py @@ -7,7 +7,7 @@ from atlas.models import CaseCollection, CidReportAnswer from generic.decorators import user_is_cid_user_manager from generic.filters import UserUserFilter from generic.tables import UserUserTable -from generic.views import CidManagerRequiredMixin, RedirectMixin, get_question_and_content_type +from generic.views import CidManagerRequiredMixin, RedirectMixin, get_question_and_content_type, get_user_exams from django.core.exceptions import PermissionDenied, ObjectDoesNotExist from django.shortcuts import render, get_object_or_404, redirect from django.views.decorators.csrf import csrf_exempt @@ -39,9 +39,8 @@ from django.http import Http404, JsonResponse from django.http import HttpResponseRedirect, HttpResponse -from physics.models import UserAnswer as PhysicsUserAnswer, Question +from physics.models import UserAnswer as PhysicsUserAnswer from physics.models import Exam as PhysicsExam -from physics.models import Question as SbasQuestion from anatomy.models import UserAnswer as AnatomyUserAnswer from anatomy.models import Exam as AnatomyExam @@ -60,7 +59,6 @@ from longs.models import Long from sbas.models import UserAnswer as SbasUserAnswer from sbas.models import Exam as SbasExam -from sbas.models import Question as SbasQuestion from anatomy.views import GenericExamViews as AnatomyExamViews from rapids.views import GenericExamViews as RapidsExamsViews @@ -148,6 +146,7 @@ def user_scores_admin(request, user_id): return user_scores(request, user=user) + @login_required def user_scores(request, user=None): # exam = get_object_or_404(Exam, pk=pk) @@ -164,23 +163,7 @@ def user_scores(request, user=None): user = request.user admin = False - EXAM_ANSWER_MAP = { - "physics": (PhysicsUserAnswer, PhysicsExam), - "anatomy": (AnatomyUserAnswer, AnatomyExam), - "rapids": (RapidsUserAnswer, RapidsExam), - "longs": (LongsUserAnswer, LongsExam), - "sbas": (SbasUserAnswer, SbasExam), - } - kwargs = {"exam_mode": True, "archive": False} - - exams = [] - for exam_type in EXAM_ANSWER_MAP: - UserAnswer, Exam = EXAM_ANSWER_MAP[exam_type] - exam_answers = UserAnswer.objects.filter(user=user) - exam_ids = exam_answers.values_list("exam").distinct() - exams_to_add = Exam.objects.filter(id__in=exam_ids, **kwargs) - if exams_to_add: - exams.append((exam_type, exams_to_add)) + exams = get_user_exams(user) # case_collections = request.user.casecollection_exams.all() @@ -737,6 +720,9 @@ def accounts_bulk_create_check(request): user = json.loads(request.POST.get("user")) + if "email" not in user: + return HttpResponse(format_html("{}", "No email provided")) + if User.objects.filter(username=user["email"]).exists(): return HttpResponse(format_html("{}", "User already exists")) diff --git a/rapids/migrations/0016_exam_results_supervisor_visible.py b/rapids/migrations/0016_exam_results_supervisor_visible.py new file mode 100644 index 00000000..18e3852e --- /dev/null +++ b/rapids/migrations/0016_exam_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rapids', '0015_alter_rapid_normal'), + ] + + operations = [ + migrations.AddField( + model_name='exam', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/sbas/migrations/0017_exam_results_supervisor_visible.py b/sbas/migrations/0017_exam_results_supervisor_visible.py new file mode 100644 index 00000000..ffe81a60 --- /dev/null +++ b/sbas/migrations/0017_exam_results_supervisor_visible.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-10-28 11:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sbas', '0016_exam_exam_open_access'), + ] + + operations = [ + migrations.AddField( + model_name='exam', + name='results_supervisor_visible', + field=models.BooleanField(default=False, help_text='If true the results will be visible to supervisors without candidate approval.'), + ), + ] diff --git a/templates/index.html b/templates/index.html index 2f55f505..24809246 100644 --- a/templates/index.html +++ b/templates/index.html @@ -32,6 +32,9 @@ {% endif %} + {% if request.user.supervisor %} + Supervisor overview + {% endif %}

diff --git a/templates/user_score_header.html b/templates/user_score_header.html index 2aab81dc..937f1a7e 100644 --- a/templates/user_score_header.html +++ b/templates/user_score_header.html @@ -1,5 +1,6 @@ {% if view_all_results %} +