mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-10 16:49:41 +00:00
Compare commits
4 Commits
dff5ac2308
...
e836195fef
| Author | SHA1 | Date | |
|---|---|---|---|
| e836195fef | |||
| 68a424d62b | |||
| 5e15b8bb59 | |||
| d26c1b535e |
@@ -3,7 +3,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
from django.contrib.auth.models import Group
|
||||
from assets import models
|
||||
from RIGS import models as rigsmodels
|
||||
|
||||
from training import models as tmodels
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Deletes testing sample data'
|
||||
@@ -31,6 +31,9 @@ class Command(BaseCommand):
|
||||
self.delete_objects(rigsmodels.Payment)
|
||||
self.delete_objects(rigsmodels.RiskAssessment)
|
||||
self.delete_objects(rigsmodels.EventChecklist)
|
||||
self.delete_objects(tmodels.TrainingCategory)
|
||||
self.delete_objects(tmodels.TrainingItem)
|
||||
self.delete_objects(tmodels.TrainingLevel)
|
||||
|
||||
def delete_objects(self, model):
|
||||
for obj in model.objects.all():
|
||||
|
||||
@@ -21,6 +21,7 @@ class Command(BaseCommand):
|
||||
profiles = models.Profile.objects.all()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating rigboard data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -35,6 +36,7 @@ class Command(BaseCommand):
|
||||
self.setup_organisations()
|
||||
self.setup_venues()
|
||||
self.setup_events()
|
||||
print("Done generating rigboard data")
|
||||
|
||||
def setup_people(self):
|
||||
names = ["Regulus Black", "Sirius Black", "Lavender Brown", "Cho Chang", "Vincent Crabbe", "Vincent Crabbe",
|
||||
|
||||
@@ -20,6 +20,7 @@ class Command(BaseCommand):
|
||||
assets = []
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
print("Generating sample assets data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -34,6 +35,7 @@ class Command(BaseCommand):
|
||||
self.create_assets()
|
||||
self.create_connectors()
|
||||
self.create_cables()
|
||||
print("Done generating sample assets data")
|
||||
|
||||
def create_categories(self):
|
||||
choices = ['Case', 'Video', 'General', 'Sound', 'Lighting', 'Rigging']
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<img src="{% static 'imgs/logo.webp' %}" width="40" height="40" alt="TEC's Logo: Serif 'TEC' vertically next to a blue box with the words 'PA and Lighting', surrounded by graduated rings" id="logo">
|
||||
</a>
|
||||
<div class="container" style="padding-left: 0; padding-right: 0;">
|
||||
<div class="row w-100">
|
||||
<div class="row w-100 flex-nowrap">
|
||||
{% block titleheader %}
|
||||
{% endblock %}
|
||||
<button class="navbar-toggler ml-auto" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" onclick="document.getElementById('logo').classList.toggle('d-none');">
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,7 +6,7 @@ from RIGS.models import Profile
|
||||
class SessionLogForm(forms.Form):
|
||||
pass
|
||||
|
||||
|
||||
# TODO Validation that dates cannot be in the future
|
||||
class QualificationForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.TrainingItemQualification
|
||||
@@ -18,3 +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)
|
||||
|
||||
@@ -16,8 +16,10 @@ class Command(BaseCommand):
|
||||
|
||||
categories = []
|
||||
items = []
|
||||
levels = []
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating training data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -28,6 +30,8 @@ class Command(BaseCommand):
|
||||
with transaction.atomic():
|
||||
self.setup_categories()
|
||||
self.setup_items()
|
||||
self.setup_levels()
|
||||
print("Done generating training data")
|
||||
|
||||
def setup_categories(self):
|
||||
names = [(1, "Basic"), (2, "Sound"), (3, "Lighting"), (4, "Rigging"), (5, "Power"), (6, "Haulage")]
|
||||
@@ -43,3 +47,17 @@ 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):
|
||||
self.levels.append(models.TrainingLevel.objects.create(level=models.TrainingLevel.TA, description="Passion will hatred faithful evil suicide noble battle. Truth aversion gains grandeur noble. Dead play gains prejudice god ascetic grandeur zarathustra dead good. Faithful ultimate justice overcome love will mountains inexpedient."))
|
||||
for i,name in models.TrainingLevel.DEPARTMENTS:
|
||||
technician = models.TrainingLevel.objects.create(level=models.TrainingLevel.TECHNICIAN, department=i, description="Moral pinnacle derive ultimate war dead. Strong fearful joy contradict battle christian faithful enlightenment prejudice zarathustra moral.")
|
||||
supervisor = models.TrainingLevel.objects.create(level=models.TrainingLevel.SUPERVISOR, department=i, description="Spirit holiest merciful mountains inexpedient reason value. Suicide ultimate hope.")
|
||||
supervisor.prerequisite_levels.add(technician)
|
||||
for i in range(0, 30):
|
||||
if i % 3 == 0:
|
||||
models.TrainingLevelRequirement.objects.create(level=technician, item=random.choice(self.items), depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
else:
|
||||
models.TrainingLevelRequirement.objects.create(level=supervisor, item=random.choice(self.items), depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
self.levels.append(technician)
|
||||
self.levels.append(supervisor)
|
||||
|
||||
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)
|
||||
name = models.CharField(max_length=50)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Training Categories'
|
||||
|
||||
class TrainingItem(models.Model):
|
||||
reference_number = models.CharField(max_length=3)
|
||||
@@ -26,7 +28,6 @@ class TrainingItem(models.Model):
|
||||
return "{}.{} {}".format(self.category.reference_number, self.reference_number, self.name)
|
||||
|
||||
|
||||
# TODO Validation that dates cannot be in the future
|
||||
class TrainingItemQualification(models.Model):
|
||||
STARTED = 0
|
||||
COMPLETE = 1
|
||||
@@ -44,17 +45,60 @@ class TrainingItemQualification(models.Model):
|
||||
supervisor = models.ForeignKey('Trainee', related_name='qualifications_granted', on_delete=models.RESTRICT)
|
||||
notes = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return "{} in {} on {}".format(self.depth, self.item, self.date)
|
||||
|
||||
|
||||
# Levels
|
||||
# FIXME Common Competencies...
|
||||
class TrainingLevel(models.Model, RevisionMixin):
|
||||
description = models.CharField(max_length=120, blank=True)
|
||||
TA = 0
|
||||
TECHNICIAN = 1
|
||||
SUPERVISOR = 2
|
||||
CHOICES = (
|
||||
(0, 'Technical Assistant'),
|
||||
(1, 'Technician'),
|
||||
(2, 'Supervisor'),
|
||||
(TA, 'Technical Assistant'),
|
||||
(TECHNICIAN, 'Technician'),
|
||||
(SUPERVISOR, '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)
|
||||
prerequisite_levels = models.ManyToManyField('self', related_name='prerequisites', symmetrical=False, blank=True)
|
||||
|
||||
def get_requirements_of_depth(self, depth):
|
||||
return self.requirements.filter(depth=depth)
|
||||
|
||||
@property
|
||||
def started_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.STARTED)
|
||||
|
||||
@property
|
||||
def complete_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.COMPLETE)
|
||||
|
||||
@property
|
||||
def passed_out_requirements(self):
|
||||
return self.get_requirements_of_depth(TrainingItemQualification.PASSED_OUT)
|
||||
|
||||
def __str__(self):
|
||||
if self.department is None: # 2TA
|
||||
return self.get_level_display()
|
||||
else:
|
||||
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):
|
||||
|
||||
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 %}
|
||||
@@ -1,34 +1,69 @@
|
||||
{% extends 'base_rigs.html' %}
|
||||
|
||||
{% block content %}
|
||||
<button type="button" class="btn btn-success btn-sm item-add"
|
||||
data-toggle="modal"
|
||||
data-target="#record_modal">
|
||||
<div class="row my-3 text-right">
|
||||
<a type="button" class="btn btn-success" href="{% url 'edit_record' pk=request.user.pk %}">
|
||||
<i class="fas fa-plus"></i> Add New Training Record
|
||||
</button>
|
||||
<div class="row mb-3">
|
||||
<h2 class="col-12">Training Levels</h2>
|
||||
<p>{{ user.name }} is a...<br></p>
|
||||
<div class="col">
|
||||
<h2><div class="badge badge-success">Sound Supervisor <span class="fas fa-volume-up"></span></div><h2>
|
||||
<h3><div class="badge badge-danger">Power Technician <span class="fas fa-plug"></span></div></h3>
|
||||
<h4><div class="badge badge-primary">Technical Assistant <span class="fas fa-wrench"></span></div></h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<h2 class="col-12">Training Levels</h2>
|
||||
<div class="card-columns">
|
||||
{% for level in levels %}
|
||||
<div class="card my-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>
|
||||
<button class="btn btn-link p-0" type="button" data-toggle="collapse" data-target=".reqs_{{level.pk}}" aria-expanded="false" aria-controls="reqs">
|
||||
Requirements <span class="fas fa-caret-right reqs_{{level.pk}} collapse show"></span><span class="fas fa-caret-down collapse reqs_{{level.pk}}"></span>
|
||||
</button>
|
||||
<div class="collapse reqs_{{level.pk}}">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="table-warning">Training Started</th>
|
||||
<th scope="col" class="table-success">Training Complete</th>
|
||||
<th scope="col" class="table-info">Passed Out</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><ul class="list-unstyled">{% for req in level.started_requirements %}<li>{{ req.item }}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in level.complete_requirements %}<li>{{ req.item }}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in level.passed_out_requirements %}<li>{{ req.item }}</li>{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button class="btn btn-success text-right" disabled>Incomplete</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h2 class="col-12">Training Items</h2><br>
|
||||
{% for category in categories %}
|
||||
<div class="col-md-3">
|
||||
<h2 class="col-12">Training Items</h2><br>
|
||||
<div class="card-columns">
|
||||
{% for category in categories %}
|
||||
<div class="card mb-3">
|
||||
<h3 class="card-header">{{ category.name }}</h3>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for depth in depths %}
|
||||
<li class="list-group-item {% if depth.0 == 0 %}list-group-item-warning{%elif depth.0 == 1%}list-group-item-success{%else%}list-group-item-info{%endif%}">{{depth.1}}</li>
|
||||
<li class="list-group-item">Dummy Item</li>
|
||||
{% for q in object.qualifications_obtained.all %}
|
||||
{% if q.depth == depth.0 and q.item.category == category %}
|
||||
<li class="list-group-item">{{q.item}} ({{q.date}})</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -15,4 +15,5 @@ urlpatterns = [
|
||||
path('trainee/<int:pk>/edit/', views.AddQualification.as_view(),
|
||||
name='edit_record'),
|
||||
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):
|
||||
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')
|
||||
|
||||
Reference in New Issue
Block a user