mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-25 09:22:21 +00:00
Basic UX for adding requirements to training levels
This commit is contained in:
@@ -3,6 +3,6 @@ from training import models
|
|||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
#admin.site.register(models.Trainee, VersionAdmin)
|
#admin.site.register(models.Trainee, VersionAdmin)
|
||||||
admin.site.register(models.TrainingLevel, VersionAdmin)
|
|
||||||
admin.site.register(models.TrainingCategory, VersionAdmin)
|
admin.site.register(models.TrainingCategory, VersionAdmin)
|
||||||
admin.site.register(models.TrainingItem, VersionAdmin)
|
admin.site.register(models.TrainingItem, VersionAdmin)
|
||||||
|
admin.site.register(models.TrainingLevel, VersionAdmin)
|
||||||
|
|||||||
@@ -18,4 +18,13 @@ class QualificationForm(forms.ModelForm):
|
|||||||
super(QualificationForm, self).__init__(*args, **kwargs)
|
super(QualificationForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['trainee'].initial = Profile.objects.get(pk=pk)
|
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)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class Command(BaseCommand):
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.setup_categories()
|
self.setup_categories()
|
||||||
self.setup_items()
|
self.setup_items()
|
||||||
|
self.setup_levels()
|
||||||
|
|
||||||
def setup_categories(self):
|
def setup_categories(self):
|
||||||
names = [(1, "Basic"), (2, "Sound"), (3, "Lighting"), (4, "Rigging"), (5, "Power"), (6, "Haulage")]
|
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):
|
for i,name in enumerate(names):
|
||||||
item = models.TrainingItem.objects.create(category=random.choice(self.categories), reference_number=random.randint(0, 100), name=name)
|
item = models.TrainingItem.objects.create(category=random.choice(self.categories), reference_number=random.randint(0, 100), name=name)
|
||||||
self.items.append(item)
|
self.items.append(item)
|
||||||
|
|
||||||
|
def setup_levels(self):
|
||||||
|
pass
|
||||||
|
|||||||
42
training/migrations/0002_auto_20210706_0053.py
Normal file
42
training/migrations/0002_auto_20210706_0053.py
Normal file
@@ -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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -16,6 +16,8 @@ class TrainingCategory(models.Model):
|
|||||||
reference_number = models.CharField(max_length=3)
|
reference_number = models.CharField(max_length=3)
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name_plural = 'Training Categories'
|
||||||
|
|
||||||
class TrainingItem(models.Model):
|
class TrainingItem(models.Model):
|
||||||
reference_number = models.CharField(max_length=3)
|
reference_number = models.CharField(max_length=3)
|
||||||
@@ -49,16 +51,34 @@ class TrainingItemQualification(models.Model):
|
|||||||
|
|
||||||
# Levels
|
# Levels
|
||||||
class TrainingLevel(models.Model, RevisionMixin):
|
class TrainingLevel(models.Model, RevisionMixin):
|
||||||
description = models.CharField(max_length=120)
|
description = models.CharField(max_length=120, blank=True)
|
||||||
CHOICES = (
|
CHOICES = (
|
||||||
(0, 'Technical Assistant'),
|
(0, 'Technical Assistant'),
|
||||||
(1, 'Technician'),
|
(1, 'Technician'),
|
||||||
(2, 'Supervisor'),
|
(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)
|
level = models.IntegerField(choices=CHOICES)
|
||||||
|
prerequisite_levels = models.ManyToManyField('self', related_name='prerequisites', symmetrical=False, blank=True)
|
||||||
# FIXME Common Competencies... have levels able to depend on other ones - but supervisors need to depend on both common and technican?
|
|
||||||
|
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):
|
class TrainingLevelQualification(models.Model):
|
||||||
trainee = models.ForeignKey('Trainee', related_name='levels', on_delete=models.RESTRICT)
|
trainee = models.ForeignKey('Trainee', related_name='levels', on_delete=models.RESTRICT)
|
||||||
|
|||||||
39
training/templates/edit_training_level.html
Normal file
39
training/templates/edit_training_level.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{% extends 'base_rigs.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block preload_js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script src="{% static 'js/selects.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||||
|
<script src="{% static 'js/tooltip.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% if form.errors %}
|
||||||
|
{% include 'form_errors.html' %}
|
||||||
|
{% endif %}
|
||||||
|
<form id="requirement-form" action="{{ form.action|default:request.path }}" method="post">{% csrf_token %}
|
||||||
|
{% render_field form.level|attr:'hidden' value=form.trainee.initial %}
|
||||||
|
<div class="form-group form-row">
|
||||||
|
<label for="item_id" class="col-sm-2 col-form-label">Item</label>
|
||||||
|
<select name="item" id="item_id" class="form-control selectpicker custom-select col-sm-10" data-live-search="true" data-sourceurl="{% url 'api_secure' model='training_item' %}" required>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-row">
|
||||||
|
<label for="depth" class="col-sm-2 col-form-label">Depth</label>
|
||||||
|
{% render_field form.depth|add_class:'form-control custom-select selectpicker col-sm'|attr:'required' %}
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-primary">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
@@ -7,7 +7,28 @@
|
|||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<h2 class="col-12">Training Levels</h2>
|
<h2 class="col-12">Training Levels</h2>
|
||||||
{% for level in levels %}
|
{% for level in levels %}
|
||||||
|
<div class="card m-3">
|
||||||
|
<h3 class="card-header"><a href="{% url 'level_edit' level.pk %}">{{ level }}</a></h3>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="progress mb-2">
|
||||||
|
<div class="progress-bar progress-bar-striped" role="progressbar" style="width: 25%" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">25% complete</div>
|
||||||
|
</div>
|
||||||
|
<p>{{ level.description }}</p>
|
||||||
|
<p>Requirements:
|
||||||
|
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#reqs" aria-expanded="false" aria-controls="reqs">
|
||||||
|
<span class="fas fa-arrow-down"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse" id="reqs">
|
||||||
|
{% for req in level.requirements.all %}
|
||||||
|
<li>{{req}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<button class="btn btn-success" disabled>Incomplete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ urlpatterns = [
|
|||||||
path('trainee/<int:pk>/edit/', views.AddQualification.as_view(),
|
path('trainee/<int:pk>/edit/', views.AddQualification.as_view(),
|
||||||
name='edit_record'),
|
name='edit_record'),
|
||||||
path('session/', views.SessionLog.as_view(), name='session_log'),
|
path('session/', views.SessionLog.as_view(), name='session_log'),
|
||||||
|
path('level/<int:pk>/edit/', views.AddLevelRequirement.as_view(), name='level_edit'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class TraineeDetail(views.ProfileDetail):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(TraineeDetail, self).get_context_data(**kwargs)
|
context = super(TraineeDetail, self).get_context_data(**kwargs)
|
||||||
context["page_title"] = "{}'s Training Record".format(self.object)
|
context["page_title"] = "{}'s Training Record".format(self.object)
|
||||||
|
context["levels"] = models.TrainingLevel.objects.all()
|
||||||
context["categories"] = models.TrainingCategory.objects.all()
|
context["categories"] = models.TrainingCategory.objects.all()
|
||||||
choices = models.TrainingItemQualification.CHOICES
|
choices = models.TrainingItemQualification.CHOICES
|
||||||
context["depths"] = choices
|
context["depths"] = choices
|
||||||
@@ -66,3 +67,22 @@ class AddQualification(generic.CreateView):
|
|||||||
kwargs = super(AddQualification, self).get_form_kwargs()
|
kwargs = super(AddQualification, self).get_form_kwargs()
|
||||||
kwargs['pk'] = self.kwargs['pk']
|
kwargs['pk'] = self.kwargs['pk']
|
||||||
return kwargs
|
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')
|
||||||
|
|||||||
Reference in New Issue
Block a user