Somewhat optimised SQL on level detail

This commit is contained in:
2021-12-28 21:35:21 +00:00
parent 732affa0b2
commit 14b73f6f50
6 changed files with 15 additions and 37 deletions

12
Pipfile
View File

@@ -19,7 +19,7 @@ cssselect = "~=1.1.0"
cssutils = "~=1.0.2" cssutils = "~=1.0.2"
dj-database-url = "~=0.5.0" dj-database-url = "~=0.5.0"
dj-static = "~=0.0.6" dj-static = "~=0.0.6"
Django = "~=3.1.12" Django = "~=3.2"
django-debug-toolbar = "~=3.2" django-debug-toolbar = "~=3.2"
django-filter = "~=2.4.0" django-filter = "~=2.4.0"
django-ical = "~=1.7.1" django-ical = "~=1.7.1"
@@ -89,14 +89,8 @@ pytest-django = "*"
pluggy = "*" pluggy = "*"
pytest-splinter = "*" pytest-splinter = "*"
pytest = "*" pytest = "*"
pytest-xdist = {extras = [ "psutil",], version = "*"}
PyPOM = {extras = [ "splinter",], version = "*"}
[requires] [requires]
python_version = "3.9" python_version = "3.9"
[dev-packages.pytest-xdist]
extras = [ "psutil",]
version = "*"
[dev-packages.PyPOM]
extras = [ "splinter",]
version = "*"

View File

@@ -260,3 +260,5 @@ USE_GRAVATAR = True
TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf" TERMS_OF_HIRE_URL = "http://www.nottinghamtec.co.uk/terms.pdf"
AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk' AUTHORISATION_NOTIFICATION_ADDRESS = 'productions@nottinghamtec.co.uk'
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

View File

@@ -18,10 +18,7 @@ class Trainee(Profile, RevisionMixin):
return [level for level in TrainingLevel.objects.all() if level.percentage_complete(self) > 0] return [level for level in TrainingLevel.objects.all() if level.percentage_complete(self) > 0]
def level_qualifications(self, only_confirmed=False): def level_qualifications(self, only_confirmed=False):
levels = self.levels.all() return self.levels.all().filter(confirmed_on__isnull=only_confirmed).select_related('level')
if only_confirmed:
levels = levels.exclude(confirmed_on__isnull=True)
return levels.select_related('level')
@property @property
def is_supervisor(self): def is_supervisor(self):
@@ -38,8 +35,7 @@ class Trainee(Profile, RevisionMixin):
return self.qualifications_obtained.filter(depth=depth).select_related('item', 'trainee', 'supervisor') return self.qualifications_obtained.filter(depth=depth).select_related('item', 'trainee', 'supervisor')
def is_user_qualified_in(self, item, required_depth): 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 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
return qual is not None and qual.depth >= required_depth
def get_absolute_url(self): def get_absolute_url(self):
return reverse('trainee_detail', kwargs={'pk': self.pk}) return reverse('trainee_detail', kwargs={'pk': self.pk})
@@ -93,8 +89,8 @@ class TrainingItemQualification(models.Model):
(PASSED_OUT, 'Passed Out'), (PASSED_OUT, 'Passed Out'),
) )
item = models.ForeignKey('TrainingItem', on_delete=models.RESTRICT) 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) depth = models.IntegerField(choices=CHOICES)
trainee = models.ForeignKey('Trainee', related_name='qualifications_obtained', on_delete=models.RESTRICT)
date = models.DateField() date = models.DateField()
# TODO Remember that some training is external. Support for making an organisation the trainer? # 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) 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): def passed_out_requirements(self):
return self.get_requirements_of_depth(TrainingItemQualification.PASSED_OUT) return self.get_requirements_of_depth(TrainingItemQualification.PASSED_OUT)
def get_related_level(self, dif): def percentage_complete(self, user):
if (level == 0 and dif < 0) or (level == 2 and dif > 0): needed_qualifications = self.requirements.all().select_related('item')
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()
relavant_qualifications = 0.0 relavant_qualifications = 0.0
# TODO Efficiency... # TODO Efficiency...
for req in needed_qualifications: for req in needed_qualifications:

View File

@@ -70,7 +70,7 @@
<div class="alert alert-warning mx-auto">No qualifications in any levels yet...did someone forget to fill out the paperwork?</div> <div class="alert alert-warning mx-auto">No qualifications in any levels yet...did someone forget to fill out the paperwork?</div>
{% endfor %} {% endfor %}
</ul> </ul>
<h3>In Progress</h3> <h3 class="col-12 pt-2">In Progress</h3>
<div class="card-columns"> <div class="card-columns">
{% for level in started_levels %} {% for level in started_levels %}
{% percentage_complete level object as completion %} {% percentage_complete level object as completion %}

View File

@@ -26,7 +26,7 @@
<th scope="row" class="align-middle" id="cell_name">{{ object.item }}</th> <th scope="row" class="align-middle" id="cell_name">{{ object.item }}</th>
<td class="table-{% colour_from_depth object.depth %}">{{ object.get_depth_display }}</td> <td class="table-{% colour_from_depth object.depth %}">{{ object.get_depth_display }}</td>
<td>{{ object.date }}</td> <td>{{ object.date }}</td>
<td>{{ object.supervisor }}</td> <td><a href="{{ object.supervisor.get_absolute_url}}">{{ object.supervisor }}</a></td>
<td>{{ object.notes }}</td> <td>{{ object.notes }}</td>
</tr> </tr>
{% empty %} {% empty %}

View File

@@ -7,7 +7,7 @@ from PyRIGS.views import OEmbedView, is_ajax, ModalURLMixin
from training import models, forms from training import models, forms
from django.utils import timezone from django.utils import timezone
from django.db import transaction 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 from users import views
@@ -37,12 +37,8 @@ class TraineeDetail(views.ProfileDetail):
else: else:
context["page_title"] = "{}'s Training Record".format(self.object.first_name + " " + self.object.last_name) context["page_title"] = "{}'s Training Record".format(self.object.first_name + " " + self.object.last_name)
context["started_levels"] = self.object.started_levels() 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') 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 return context
@@ -101,7 +97,7 @@ class TraineeList(generic.ListView):
# not an integer # not an integer
pass 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): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)