diff --git a/training/admin.py b/training/admin.py index 9d6906ed..8a965144 100644 --- a/training/admin.py +++ b/training/admin.py @@ -3,6 +3,6 @@ from training import models from reversion.admin import VersionAdmin #admin.site.register(models.Trainee, VersionAdmin) -admin.site.register(models.TrainingLevel, VersionAdmin) admin.site.register(models.TrainingCategory, VersionAdmin) admin.site.register(models.TrainingItem, VersionAdmin) +admin.site.register(models.TrainingLevel, VersionAdmin) diff --git a/training/forms.py b/training/forms.py index 961c877b..c7e3028f 100644 --- a/training/forms.py +++ b/training/forms.py @@ -18,4 +18,13 @@ class QualificationForm(forms.ModelForm): super(QualificationForm, self).__init__(*args, **kwargs) self.fields['trainee'].initial = Profile.objects.get(pk=pk) - + +class RequirementForm(forms.ModelForm): + class Meta: + model = models.TrainingLevelRequirement + fields = '__all__' + + def __init__(self, *args, **kwargs): + pk = kwargs.pop('pk', None) + super(RequirementForm, self).__init__(*args, **kwargs) + self.fields['level'].initial = models.TrainingLevel.objects.get(pk=pk) diff --git a/training/management/commands/generateSampleTrainingData.py b/training/management/commands/generateSampleTrainingData.py index 7fa68615..dd4f1bd5 100644 --- a/training/management/commands/generateSampleTrainingData.py +++ b/training/management/commands/generateSampleTrainingData.py @@ -28,6 +28,7 @@ class Command(BaseCommand): with transaction.atomic(): self.setup_categories() self.setup_items() + self.setup_levels() def setup_categories(self): names = [(1, "Basic"), (2, "Sound"), (3, "Lighting"), (4, "Rigging"), (5, "Power"), (6, "Haulage")] @@ -43,3 +44,6 @@ class Command(BaseCommand): for i,name in enumerate(names): item = models.TrainingItem.objects.create(category=random.choice(self.categories), reference_number=random.randint(0, 100), name=name) self.items.append(item) + + def setup_levels(self): + pass diff --git a/training/migrations/0002_auto_20210706_0053.py b/training/migrations/0002_auto_20210706_0053.py new file mode 100644 index 00000000..24c5a644 --- /dev/null +++ b/training/migrations/0002_auto_20210706_0053.py @@ -0,0 +1,42 @@ +# Generated by Django 3.1.5 on 2021-07-05 23:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('training', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='trainingcategory', + options={'verbose_name_plural': 'Training Categories'}, + ), + migrations.AddField( + model_name='traininglevel', + name='description', + field=models.CharField(blank=True, max_length=120), + ), + migrations.AddField( + model_name='traininglevel', + name='prerequisite_levels', + field=models.ManyToManyField(blank=True, related_name='prerequisites', to='training.TrainingLevel'), + ), + migrations.AlterField( + model_name='traininglevel', + name='department', + field=models.IntegerField(choices=[(0, 'Sound'), (1, 'Lighting'), (2, 'Power'), (3, 'Rigging'), (4, 'Haulage')], null=True), + ), + migrations.CreateModel( + name='TrainingLevelRequirement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('depth', models.IntegerField(verbose_name=((0, 'Training Started'), (1, 'Training Complete'), (2, 'Passed Out')))), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to='training.trainingitem')), + ('level', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='requirements', to='training.traininglevel')), + ], + ), + ] diff --git a/training/models.py b/training/models.py index e09e351c..30b6a788 100644 --- a/training/models.py +++ b/training/models.py @@ -16,6 +16,8 @@ class TrainingCategory(models.Model): reference_number = models.CharField(max_length=3) name = models.CharField(max_length=50) + class Meta: + verbose_name_plural = 'Training Categories' class TrainingItem(models.Model): reference_number = models.CharField(max_length=3) @@ -49,16 +51,34 @@ class TrainingItemQualification(models.Model): # Levels class TrainingLevel(models.Model, RevisionMixin): - description = models.CharField(max_length=120) + description = models.CharField(max_length=120, blank=True) CHOICES = ( (0, 'Technical Assistant'), (1, 'Technician'), (2, 'Supervisor'), ) - department = models.CharField(max_length=50, null=True) # N.B. Technical Assistant does not have a department + DEPARTMENTS = ( + (0, 'Sound'), + (1, 'Lighting'), + (2, 'Power'), + (3, 'Rigging'), + (4, 'Haulage'), + ) + department = models.IntegerField(choices=DEPARTMENTS, null=True) # N.B. Technical Assistant does not have a department level = models.IntegerField(choices=CHOICES) - - # FIXME Common Competencies... have levels able to depend on other ones - but supervisors need to depend on both common and technican? + prerequisite_levels = models.ManyToManyField('self', related_name='prerequisites', symmetrical=False, blank=True) + + def __str__(self): + return "{} {}".format(self.get_department_display(), self.get_level_display()) + +class TrainingLevelRequirement(models.Model): + level = models.ForeignKey('TrainingLevel', related_name='requirements', on_delete=models.RESTRICT) + item = models.ForeignKey('TrainingItem', on_delete=models.RESTRICT) + depth = models.IntegerField(TrainingItemQualification.CHOICES) + + def __str__(self): + return "{} in {}".format(TrainingItemQualification.CHOICES[self.depth][1], self.item) + class TrainingLevelQualification(models.Model): trainee = models.ForeignKey('Trainee', related_name='levels', on_delete=models.RESTRICT) diff --git a/training/templates/edit_training_level.html b/training/templates/edit_training_level.html new file mode 100644 index 00000000..8fcf2e4c --- /dev/null +++ b/training/templates/edit_training_level.html @@ -0,0 +1,39 @@ +{% extends 'base_rigs.html' %} + +{% load static %} +{% load widget_tweaks %} + +{% block css %} + {{ block.super }} + +{% endblock %} + +{% block preload_js %} + {{ block.super }} + +{% endblock %} + +{% block js %} + {{ block.super }} + + +{% endblock %} + +{% block content %} +{% if form.errors %} + {% include 'form_errors.html' %} +{% endif %} +
{% csrf_token %} + {% render_field form.level|attr:'hidden' value=form.trainee.initial %} +
+ + +
+
+ + {% render_field form.depth|add_class:'form-control custom-select selectpicker col-sm'|attr:'required' %} +
+ +
+{% endblock %} diff --git a/training/templates/trainee_detail.html b/training/templates/trainee_detail.html index d742e495..39931d26 100644 --- a/training/templates/trainee_detail.html +++ b/training/templates/trainee_detail.html @@ -7,7 +7,28 @@

Training Levels

{% for level in levels %} - +
+

{{ level }}

+
+
+
25% complete
+
+

{{ level.description }}

+

Requirements: + +

+ {% for req in level.requirements.all %} +
  • {{req}}
  • + {% endfor %} +

    +
    +
    + +
    {% endfor %}
    diff --git a/training/urls.py b/training/urls.py index 88d3bbbd..6c2071e3 100644 --- a/training/urls.py +++ b/training/urls.py @@ -15,4 +15,5 @@ urlpatterns = [ path('trainee//edit/', views.AddQualification.as_view(), name='edit_record'), path('session/', views.SessionLog.as_view(), name='session_log'), + path('level//edit/', views.AddLevelRequirement.as_view(), name='level_edit'), ] diff --git a/training/views.py b/training/views.py index 585b6c88..6e90f5aa 100644 --- a/training/views.py +++ b/training/views.py @@ -25,6 +25,7 @@ class TraineeDetail(views.ProfileDetail): def get_context_data(self, **kwargs): context = super(TraineeDetail, self).get_context_data(**kwargs) context["page_title"] = "{}'s Training Record".format(self.object) + context["levels"] = models.TrainingLevel.objects.all() context["categories"] = models.TrainingCategory.objects.all() choices = models.TrainingItemQualification.CHOICES context["depths"] = choices @@ -66,3 +67,22 @@ class AddQualification(generic.CreateView): kwargs = super(AddQualification, self).get_form_kwargs() kwargs['pk'] = self.kwargs['pk'] return kwargs + + +class AddLevelRequirement(generic.CreateView): + template_name = "edit_training_level.html" + model = models.TrainingLevelRequirement + form_class = forms.RequirementForm + + def get_context_data(self, **kwargs): + context = super(AddLevelRequirement, self).get_context_data(**kwargs) + context["page_title"] = "Add Requirements to Training Level {}".format(models.TrainingLevel.objects.get(pk=self.kwargs['pk'])) + return context + + def get_form_kwargs(self): + kwargs = super(AddLevelRequirement, self).get_form_kwargs() + kwargs['pk'] = self.kwargs['pk'] + return kwargs + + def get_success_url(self): + return reverse_lazy('trainee_detail')