mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-02 13:22:17 +00:00
Compare commits
2 Commits
88dee9477b
...
6414e68231
| Author | SHA1 | Date | |
|---|---|---|---|
|
6414e68231
|
|||
|
b539312538
|
18
RIGS/migrations/0044_profile_is_supervisor.py
Normal file
18
RIGS/migrations/0044_profile_is_supervisor.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.11 on 2022-01-09 14:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0043_auto_20211027_1519'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='profile',
|
||||
name='is_supervisor',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
25
RIGS/migrations/0045_legacy_supervisors.py
Normal file
25
RIGS/migrations/0045_legacy_supervisors.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 3.2.11 on 2022-01-09 15:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
from training.models import TrainingLevel
|
||||
|
||||
def find_existing_supervisors(apps, schema_editor):
|
||||
Profile = apps.get_model('RIGS', 'Profile')
|
||||
for person in Profile.objects.all():
|
||||
if person.level_qualifications.exclude(confirmed_on=None).select_related('level') \
|
||||
.filter(level__level__gte=TrainingLevel.SUPERVISOR) \
|
||||
.exclude(level__department=TrainingLevel.HAULAGE) \
|
||||
.exclude(level__department__isnull=True).exists():
|
||||
person.is_supervisor = True
|
||||
person.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('RIGS', '0044_profile_is_supervisor'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(find_existing_supervisors, migrations.RunPython.noop)
|
||||
]
|
||||
@@ -27,6 +27,7 @@ class Profile(AbstractUser):
|
||||
# Currently only populated by the admin approval email. TODO: Populate it each time we send any email, might need that...
|
||||
last_emailed = models.DateTimeField(blank=True, null=True)
|
||||
dark_theme = models.BooleanField(default=False)
|
||||
is_supervisor = models.BooleanField(default=False)
|
||||
|
||||
reversion_hide = True
|
||||
|
||||
@@ -56,11 +57,6 @@ class Profile(AbstractUser):
|
||||
def latest_events(self):
|
||||
return self.event_mic.order_by('-start_date').select_related('person', 'organisation', 'venue', 'mic', 'riskassessment', 'invoice').prefetch_related('checklists')
|
||||
|
||||
@cached_property
|
||||
def as_trainee(self):
|
||||
from training.models import Trainee
|
||||
return Trainee.objects.get(pk=self.pk)
|
||||
|
||||
@classmethod
|
||||
def admins(cls):
|
||||
return Profile.objects.filter(email__in=[y for x in settings.ADMINS for y in x])
|
||||
|
||||
@@ -2,4 +2,4 @@ from PyRIGS.decorators import user_passes_test_with_403
|
||||
|
||||
|
||||
def has_perm_or_supervisor(perm, login_url=None, oembed_view=None):
|
||||
return user_passes_test_with_403(lambda u: (hasattr(u, 'as_trainee') and u.as_trainee.is_supervisor) or u.has_perm(perm), login_url=login_url, oembed_view=oembed_view)
|
||||
return user_passes_test_with_403(lambda u: u.is_supervisor or u.has_perm(perm), login_url=login_url, oembed_view=oembed_view)
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.urls import reverse
|
||||
from django.utils.safestring import SafeData, mark_safe
|
||||
|
||||
|
||||
@reversion.register(for_concrete_model=False)
|
||||
@reversion.register(for_concrete_model=False, fields=[], follow=["qualifications_obtained", "level_qualifications"])
|
||||
class Trainee(Profile, RevisionMixin):
|
||||
class Meta:
|
||||
proxy = True
|
||||
@@ -23,13 +23,6 @@ class Trainee(Profile, RevisionMixin):
|
||||
.exclude(level__department=TrainingLevel.HAULAGE) \
|
||||
.exclude(level__department__isnull=True).exists()
|
||||
|
||||
@property
|
||||
def is_supervisor(self):
|
||||
return self.level_qualifications.exclude(confirmed_on=None).select_related('level') \
|
||||
.filter(level__level__gte=TrainingLevel.SUPERVISOR) \
|
||||
.exclude(level__department=TrainingLevel.HAULAGE) \
|
||||
.exclude(level__department__isnull=True).exists()
|
||||
|
||||
@property
|
||||
def is_driver(self):
|
||||
return self.level_qualifications.all().exclude(confirmed_on=None).select_related('level').filter(level__department=TrainingLevel.HAULAGE).exists()
|
||||
@@ -43,6 +36,10 @@ class Trainee(Profile, RevisionMixin):
|
||||
def get_absolute_url(self):
|
||||
return reverse('trainee_detail', kwargs={'pk': self.pk})
|
||||
|
||||
@property
|
||||
def display_id(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class TrainingCategory(models.Model):
|
||||
reference_number = models.IntegerField(unique=True)
|
||||
@@ -55,6 +52,7 @@ class TrainingCategory(models.Model):
|
||||
verbose_name_plural = 'Training Categories'
|
||||
|
||||
|
||||
@reversion.register
|
||||
class TrainingItem(models.Model):
|
||||
reference_number = models.IntegerField()
|
||||
category = models.ForeignKey('TrainingCategory', related_name='items', on_delete=models.CASCADE)
|
||||
@@ -80,8 +78,8 @@ class TrainingItem(models.Model):
|
||||
ordering = ['category__reference_number', 'reference_number']
|
||||
|
||||
|
||||
@reversion.register(follow=['trainee'])
|
||||
class TrainingItemQualification(models.Model):
|
||||
@reversion.register
|
||||
class TrainingItemQualification(models.Model, RevisionMixin):
|
||||
STARTED = 0
|
||||
COMPLETE = 1
|
||||
PASSED_OUT = 2
|
||||
@@ -248,7 +246,7 @@ class TrainingLevelRequirement(models.Model, RevisionMixin):
|
||||
unique_together = ["level", "item"]
|
||||
|
||||
|
||||
@reversion.register(follow=['trainee'])
|
||||
@reversion.register
|
||||
class TrainingLevelQualification(models.Model, RevisionMixin):
|
||||
trainee = models.ForeignKey('Trainee', related_name='level_qualifications', on_delete=models.CASCADE)
|
||||
level = models.ForeignKey('TrainingLevel', on_delete=models.CASCADE)
|
||||
@@ -261,6 +259,11 @@ class TrainingLevelQualification(models.Model, RevisionMixin):
|
||||
def get_icon(self):
|
||||
return self.level.get_icon
|
||||
|
||||
def clean(self):
|
||||
if level.level >= TrainingLevel.SUPERVISOR and level.department != TrainingLevel.HAULAGE:
|
||||
trainee.is_supervisor = True
|
||||
trainee.save()
|
||||
|
||||
def __str__(self):
|
||||
if self.level.is_common_competencies:
|
||||
return "{} is qualified in the {}".format(self.trainee, self.level)
|
||||
|
||||
@@ -22,6 +22,13 @@
|
||||
{% block content %}
|
||||
{% if form.errors %}
|
||||
{% include 'form_errors.html' %}
|
||||
<script src="{% static 'js/autocompleter.js' %}"></script>
|
||||
<script>
|
||||
//Has to be done here or the pickers disappear on modal error
|
||||
$('document').ready(function(){
|
||||
$(document).find(".selectpicker").selectpicker().each(function(){initPicker($(this))});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<form id="requirement-form" action="{{ form.action|default:request.path }}" method="post">{% csrf_token %}
|
||||
{% render_field form.level|attr:'hidden' value=form.level.initial %}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<div class="form-group form-row">
|
||||
<label for="supervisor" class="col-sm-2 col-form-label">Supervisor</label>
|
||||
<select name="supervisor" id="supervisor_id" class="form-control selectpicker custom-select col-sm-10" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required>
|
||||
<select name="supervisor" id="supervisor_id" class="form-control selectpicker custom-select col-sm-10" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials&filters=is_supervisor" required>
|
||||
{% if object.supervisor %}
|
||||
<option value="{{object.supervisor.pk}}" selected>{{object.supervisor}}</option>
|
||||
{% endif %}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if request.user.as_trainee.is_supervisor or perms.training.add_traininglevelrequirement %}
|
||||
{% if request.user.is_supervisor or perms.training.add_traininglevelrequirement %}
|
||||
<div class="col-sm-12 text-right pr-0">
|
||||
<a type="button" class="btn btn-success mb-3" href="{% url 'add_requirement' pk=object.pk %}" id="requirement_button">
|
||||
<span class="fas fa-plus"></span> Add New Requirement
|
||||
@@ -79,9 +79,9 @@
|
||||
{% endfor %}
|
||||
<tr><th colspan="3" class="text-center">{{object}}</th></tr>
|
||||
<tr>
|
||||
<td><ul class="list-unstyled">{% for req in object.started_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 0 %} {% if request.user.as_trainee.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 1 %} {% if request.user.as_trainee.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 2 %} {% if request.user.as_trainee.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline"" href="{% url 'remove_requirement' pk=req.pk %}" title="Delete requirement"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.started_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 0 %} {% if request.user.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 1 %} {% if request.user.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% user_has_qualification u req.item 2 %} {% if request.user.is_supervisor or perms.training.delete_traininglevelrequirement %}<a type="button" class="btn btn-link tn-sm p-0 align-baseline"" href="{% url 'remove_requirement' pk=req.pk %}" title="Delete requirement"><span class="fas fa-trash-alt text-danger"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% if request.user.as_trainee.is_supervisor or perms.training.add_trainingitemqualification %}
|
||||
{% if request.user.is_supervisor or perms.training.add_trainingitemqualification %}
|
||||
<a type="button" class="btn btn-success" href="{% url 'add_qualification' object.pk %}" id="add_record">
|
||||
<span class="fas fa-plus"></span> Add New Training Record
|
||||
</a>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<th>Date</th>
|
||||
<th>Supervisor</th>
|
||||
<th>Notes</th>
|
||||
{% if request.user.as_trainee.is_supervisor or perms.training.change_trainingitemqualification %}
|
||||
{% if request.user.is_supervisor or perms.training.change_trainingitemqualification %}
|
||||
<th></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
@@ -32,7 +32,7 @@
|
||||
<td>{{ object.date }}</td>
|
||||
<td><a href="{{ object.supervisor.get_absolute_url}}">{{ object.supervisor }}</a></td>
|
||||
<td>{{ object.notes }}</td>
|
||||
{% if request.user.as_trainee.is_supervisor or perms.training.change_trainingitemqualification %}
|
||||
{% if request.user.is_supervisor or perms.training.change_trainingitemqualification %}
|
||||
<td>{% button 'edit' 'edit_qualification' trainee.pk %}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
@@ -46,4 +46,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col text-right">
|
||||
{% include 'partials/last_edited.html' with target="trainee_history" object=trainee %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -121,6 +121,12 @@ class AddQualification(generic.CreateView, ModalURLMixin):
|
||||
model = models.TrainingItemQualification
|
||||
form_class = forms.QualificationForm
|
||||
|
||||
@transaction.atomic()
|
||||
@reversion.create_revision()
|
||||
def form_valid(self, form, *args, **kwargs):
|
||||
reversion.add_to_revision(form.cleaned_data['trainee'])
|
||||
return super().form_valid(form, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(AddQualification, self).get_context_data(**kwargs)
|
||||
context["depths"] = models.TrainingItemQualification.CHOICES
|
||||
@@ -156,6 +162,12 @@ class EditQualification(generic.UpdateView):
|
||||
kwargs['pk'] = self.kwargs['pk']
|
||||
return kwargs
|
||||
|
||||
@transaction.atomic()
|
||||
@reversion.create_revision()
|
||||
def form_valid(self, form, *args, **kwargs):
|
||||
reversion.add_to_revision(form.cleaned_data['trainee'])
|
||||
return super().form_valid(form, *args, **kwargs)
|
||||
|
||||
|
||||
class AddLevelRequirement(generic.CreateView, ModalURLMixin):
|
||||
template_name = "add_level_requirement.html"
|
||||
@@ -179,7 +191,6 @@ class AddLevelRequirement(generic.CreateView, ModalURLMixin):
|
||||
@reversion.create_revision()
|
||||
def form_valid(self, form, *args, **kwargs):
|
||||
reversion.add_to_revision(form.cleaned_data['level'])
|
||||
reversion.set_comment("Level requirement added")
|
||||
return super().form_valid(form, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -206,13 +217,13 @@ class ConfirmLevel(generic.RedirectView):
|
||||
@transaction.atomic()
|
||||
@reversion.create_revision()
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
level_qualification, created = models.TrainingLevelQualification.objects.get_or_create(trainee=models.Trainee.objects.get(pk=kwargs['pk']), level=models.TrainingLevel.objects.get(pk=kwargs['level_pk']))
|
||||
trainee = models.Trainee.objects.get(pk=kwargs['pk'])
|
||||
level_qualification, created = models.TrainingLevelQualification.objects.get_or_create(trainee=trainee, level=models.TrainingLevel.objects.get(pk=kwargs['level_pk']))
|
||||
|
||||
if created:
|
||||
level_qualification.confirmed_by = self.request.user
|
||||
level_qualification.confirmed_on = timezone.now()
|
||||
level_qualification.save()
|
||||
|
||||
reversion.add_to_revision(level_qualification.trainee)
|
||||
reversion.set_user(self.request.user)
|
||||
reversion.add_to_revision(trainee)
|
||||
return reverse_lazy('trainee_detail', kwargs={'pk': kwargs['pk']})
|
||||
|
||||
@@ -72,7 +72,7 @@ class FieldComparison(object):
|
||||
|
||||
|
||||
class ModelComparison(object):
|
||||
def __init__(self, old=None, new=None, version=None, follow=False, excluded_keys=[]):
|
||||
def __init__(self, old=None, new=None, version=None, follow=False, excluded_keys=['date_joined']):
|
||||
# recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects
|
||||
try:
|
||||
self.fields = old._meta.get_fields()
|
||||
@@ -122,7 +122,7 @@ class ModelComparison(object):
|
||||
old_item_versions = self.version.parent.revision.version_set.exclude(content_type=item_type)
|
||||
new_item_versions = self.version.revision.version_set.exclude(content_type=item_type).exclude(content_type=ContentType.objects.get_for_model(models.EventAuthorisation))
|
||||
|
||||
comparisonParams = {'excluded_keys': ['id', 'event', 'order', 'checklist', 'level', '_order', 'last_login']}
|
||||
comparisonParams = {'excluded_keys': ['id', 'event', 'order', 'checklist', 'level', '_order', 'date_joined']}
|
||||
|
||||
# Build some dicts of what we have
|
||||
item_dict = {} # build a list of items, key is the item_pk
|
||||
@@ -170,7 +170,7 @@ class RIGSVersionManager(VersionQuerySet):
|
||||
for model in model_array:
|
||||
content_types.append(ContentType.objects.get_for_model(model))
|
||||
|
||||
return self.filter(content_type__in=content_types).select_related("revision").order_by(
|
||||
return self.filter(content_type__in=content_types).select_related("revision",).order_by(
|
||||
"-revision__date_created")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user