Add basic validation of item prerequisites

Currently throws the worlds most unhelpful error message...
This commit is contained in:
2022-02-23 16:01:00 +00:00
parent 41f5a23ef0
commit 3d36d986a4
5 changed files with 56 additions and 11 deletions

View File

@@ -16,6 +16,13 @@ class QualificationForm(forms.ModelForm):
self.fields['trainee'].initial = Profile.objects.get(pk=pk) self.fields['trainee'].initial = Profile.objects.get(pk=pk)
self.fields['date'].widget.format = '%Y-%m-%d' self.fields['date'].widget.format = '%Y-%m-%d'
def clean(self):
cleaned_data = super().clean()
item = cleaned_data.get('item')
trainee = cleaned_data.get('trainee')
if not item.user_has_requirements(trainee):
self.add_error('item', 'Missing prerequisites')
def clean_date(self): def clean_date(self):
date = self.cleaned_data['date'] date = self.cleaned_data['date']
if date > date.today(): if date > date.today():

View File

@@ -104,19 +104,19 @@ class Command(BaseCommand):
obj, created = models.TrainingItem.objects.update_or_create( obj, created = models.TrainingItem.objects.update_or_create(
pk=int(child.find('ID').text), pk=int(child.find('ID').text),
reference_number=number, reference_number=number,
name=name, description=name,
category=category, category=category,
active=active active=active
) )
except IntegrityError: except IntegrityError:
print("Training Item {}.{} {} has a duplicate reference number".format(category.reference_number, number, name)) print(f"Training Item {category.reference_number}.{number} {name} has a duplicate reference number")
if created: if created:
tally[1] += 1 tally[1] += 1
else: else:
tally[0] += 1 tally[0] += 1
print('Training Items - Updated: {}, Created: {}'.format(tally[0], tally[1])) print(f'Training Items - Updated: {tally[0]}, Created: {tally[1]}')
def import_TrainingItemQualification(self): def import_TrainingItemQualification(self):
tally = [0, 0, 0] tally = [0, 0, 0]
@@ -129,9 +129,9 @@ class Command(BaseCommand):
("Competency_Assessed", models.TrainingItemQualification.PASSED_OUT), ] ("Competency_Assessed", models.TrainingItemQualification.PASSED_OUT), ]
for (depth, depth_index) in depths: for (depth, depth_index) in depths:
if child.find('{}_Date'.format(depth)) is not None: if child.find(f'{depth}_Date') is not None:
if child.find('{}_Assessor_ID'.format(depth)) is None: if child.find(f'{depth}_Assessor_ID') is None:
print("Training Record #{} had no supervisor. Assigning System User.".format(child.find('ID').text)) print(f"Training Record #{child.find('ID').text} had no supervisor. Assigning System User.")
supervisor = Profile.objects.get(first_name="God") supervisor = Profile.objects.get(first_name="God")
continue continue
supervisor = Profile.objects.get(pk=self.id_map[child.find('{}_Assessor_ID'.format(depth)).text]) supervisor = Profile.objects.get(pk=self.id_map[child.find('{}_Assessor_ID'.format(depth)).text])

View File

@@ -0,0 +1,22 @@
# Generated by Django 3.2.12 on 2022-02-23 15:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('training', '0004_rename_name_trainingitem_description'),
]
operations = [
migrations.AlterModelOptions(
name='trainingcategory',
options={'ordering': ['reference_number'], 'verbose_name_plural': 'Training Categories'},
),
migrations.AddField(
model_name='trainingitem',
name='prerequisites',
field=models.ManyToManyField(blank=True, to='training.TrainingItem'),
),
]

View File

@@ -75,6 +75,7 @@ class TrainingCategory(models.Model):
class Meta: class Meta:
verbose_name_plural = 'Training Categories' verbose_name_plural = 'Training Categories'
ordering = ['reference_number']
class TrainingItemManager(QueryablePropertiesManager): class TrainingItemManager(QueryablePropertiesManager):
@@ -92,6 +93,7 @@ class TrainingItem(models.Model):
category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.CASCADE) category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.CASCADE)
description = models.CharField(max_length=50) description = models.CharField(max_length=50)
active = models.BooleanField(default=True) active = models.BooleanField(default=True)
prerequisites = models.ManyToManyField('self', symmetrical=False, blank=True)
objects = TrainingItemManager() objects = TrainingItemManager()
@@ -124,6 +126,13 @@ class TrainingItem(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('item_list') return reverse('item_list')
def has_prereqs(self):
return self.prerequisites.all().exists()
def user_has_requirements(self, user):
# Always true if there are no prerequisites, otherwise get a set of prerequsite IDs and check if they are a subset of the set of qualification IDs
return not self.has_prereqs() or set(self.prerequisites.values_list('pk', flat=True)).issubset(set(user.qualifications_obtained.values_list('item', flat=True)))
@staticmethod @staticmethod
def user_has_qualification(item, user, depth): def user_has_qualification(item, user, depth):
return user.qualifications_obtained.only('item', 'depth').filter(item=item, depth__gte=depth).exists() return user.qualifications_obtained.only('item', 'depth').filter(item=item, depth__gte=depth).exists()

View File

@@ -13,11 +13,18 @@
<div class="card-body"> <div class="card-body">
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
{% for item in category.items.all %} {% for item in category.items.all %}
{% if item.active %} <li class="list-group-item {% if not item.active%}text-warning{%endif%}">{{ item }}
<li class="list-group-item">{{ item }}</li> {% if item.prerequisites.exists %}
{% elif request.user.is_superuser %} <div class="ml-3 font-italic">
<li class="list-group-item text-warning">{{ item }}</li> <p class="text-info mb-0">Prerequisites:</p>
{% endif %} <ul>
{% for p in item.prerequisites.all %}
<li>{{p}}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</li>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>