From 14b73f6f502956624e86b8a1e654b859dff633cc Mon Sep 17 00:00:00 2001 From: Arona Jones Date: Tue, 28 Dec 2021 21:35:21 +0000 Subject: [PATCH] Somewhat optimised SQL on level detail --- Pipfile | 12 +++--------- PyRIGS/settings.py | 2 ++ training/models.py | 24 +++++------------------ training/templates/trainee_detail.html | 2 +- training/templates/trainee_item_list.html | 2 +- training/views.py | 10 +++------- 6 files changed, 15 insertions(+), 37 deletions(-) diff --git a/Pipfile b/Pipfile index 91dafe8d..bb66abe2 100644 --- a/Pipfile +++ b/Pipfile @@ -19,7 +19,7 @@ cssselect = "~=1.1.0" cssutils = "~=1.0.2" dj-database-url = "~=0.5.0" dj-static = "~=0.0.6" -Django = "~=3.1.12" +Django = "~=3.2" django-debug-toolbar = "~=3.2" django-filter = "~=2.4.0" django-ical = "~=1.7.1" @@ -89,14 +89,8 @@ pytest-django = "*" pluggy = "*" pytest-splinter = "*" pytest = "*" +pytest-xdist = {extras = [ "psutil",], version = "*"} +PyPOM = {extras = [ "splinter",], version = "*"} [requires] python_version = "3.9" - -[dev-packages.pytest-xdist] -extras = [ "psutil",] -version = "*" - -[dev-packages.PyPOM] -extras = [ "splinter",] -version = "*" diff --git a/PyRIGS/settings.py b/PyRIGS/settings.py index 417c208d..0d3e9eba 100644 --- a/PyRIGS/settings.py +++ b/PyRIGS/settings.py @@ -260,3 +260,5 @@ USE_GRAVATAR = True TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf" AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk' + +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' diff --git a/training/models.py b/training/models.py index 7e4ad7fe..1a439a09 100644 --- a/training/models.py +++ b/training/models.py @@ -18,10 +18,7 @@ class Trainee(Profile, RevisionMixin): return [level for level in TrainingLevel.objects.all() if level.percentage_complete(self) > 0] def level_qualifications(self, only_confirmed=False): - levels = self.levels.all() - if only_confirmed: - levels = levels.exclude(confirmed_on__isnull=True) - return levels.select_related('level') + return self.levels.all().filter(confirmed_on__isnull=only_confirmed).select_related('level') @property def is_supervisor(self): @@ -38,8 +35,7 @@ class Trainee(Profile, RevisionMixin): return self.qualifications_obtained.filter(depth=depth).select_related('item', 'trainee', 'supervisor') def is_user_qualified_in(self, item, required_depth): - qual = self.qualifications_obtained.filter(item=item).first() # this is a somewhat ghetto version of get_or_none - return qual is not None and qual.depth >= required_depth + return self.qualifications_obtained.values('item', 'depth').filter(item=item).filter(depth__gte=required_depth).first() is not None # this is a somewhat ghetto version of get_or_none def get_absolute_url(self): return reverse('trainee_detail', kwargs={'pk': self.pk}) @@ -93,8 +89,8 @@ class TrainingItemQualification(models.Model): (PASSED_OUT, 'Passed Out'), ) item = models.ForeignKey('TrainingItem', on_delete=models.RESTRICT) - trainee = models.ForeignKey('Trainee', related_name='qualifications_obtained', on_delete=models.RESTRICT) depth = models.IntegerField(choices=CHOICES) + trainee = models.ForeignKey('Trainee', related_name='qualifications_obtained', on_delete=models.RESTRICT) date = models.DateField() # TODO Remember that some training is external. Support for making an organisation the trainer? supervisor = models.ForeignKey('Trainee', related_name='qualifications_granted', on_delete=models.RESTRICT) @@ -189,18 +185,8 @@ class TrainingLevel(models.Model, RevisionMixin): def passed_out_requirements(self): return self.get_requirements_of_depth(TrainingItemQualification.PASSED_OUT) - def get_related_level(self, dif): - if (level == 0 and dif < 0) or (level == 2 and dif > 0): - return None - return TrainingLevel.objects.get(department=self.department, level=self.level+dif) - - def get_common_competencies(self): - if is_common_competencies: - return self - return TrainingLevel.objects.get(level=self.level, department=None) - - def percentage_complete(self, user): # FIXME - needed_qualifications = self.requirements.all().select_related() + def percentage_complete(self, user): + needed_qualifications = self.requirements.all().select_related('item') relavant_qualifications = 0.0 # TODO Efficiency... for req in needed_qualifications: diff --git a/training/templates/trainee_detail.html b/training/templates/trainee_detail.html index 7fc7eb8c..a6ffd128 100644 --- a/training/templates/trainee_detail.html +++ b/training/templates/trainee_detail.html @@ -70,7 +70,7 @@
No qualifications in any levels yet...did someone forget to fill out the paperwork?
{% endfor %} -

In Progress

+

In Progress

{% for level in started_levels %} {% percentage_complete level object as completion %} diff --git a/training/templates/trainee_item_list.html b/training/templates/trainee_item_list.html index 2561ea1b..7e61288a 100644 --- a/training/templates/trainee_item_list.html +++ b/training/templates/trainee_item_list.html @@ -26,7 +26,7 @@ {{ object.item }} {{ object.get_depth_display }} {{ object.date }} - {{ object.supervisor }} + {{ object.supervisor }} {{ object.notes }} {% empty %} diff --git a/training/views.py b/training/views.py index 5fdbf75c..23ebaf34 100644 --- a/training/views.py +++ b/training/views.py @@ -7,7 +7,7 @@ from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin from training import models, forms from django.utils import timezone from django.db import transaction -from django.db.models import Q, Count +from django.db.models import Q, Count, OuterRef, F, Subquery, Window from users import views @@ -37,12 +37,8 @@ class TraineeDetail(views.ProfileDetail): else: context["page_title"] = "{}'s Training Record".format(self.object.first_name + " " + self.object.last_name) context["started_levels"] = self.object.started_levels() - context["completed_levels"] = self.object.level_qualifications() + context["completed_levels"] = self.object.level_qualifications().select_related('level') context["categories"] = models.TrainingCategory.objects.all().prefetch_related('items') - choices = models.TrainingItemQualification.CHOICES - context["depths"] = choices - for i in [x for x, _ in choices]: - context[str(i)] = self.object.get_records_of_depth(i) return context @@ -101,7 +97,7 @@ class TraineeList(generic.ListView): # not an integer pass - return self.model.objects.filter(filter).annotate(num_qualifications=Count('qualifications_obtained')).order_by('-num_qualifications').prefetch_related('levels', 'qualifications_obtained') + return self.model.objects.filter(filter).annotate(num_qualifications=Count('qualifications_obtained')).order_by('-num_qualifications').prefetch_related('levels', 'qualifications_obtained', 'qualifications_obtained__item') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs)