mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-02-11 17:19:42 +00:00
Compare commits
4 Commits
af987c1ebb
...
0c4228da57
| Author | SHA1 | Date | |
|---|---|---|---|
|
0c4228da57
|
|||
|
246a52d19e
|
|||
|
8b10aaf700
|
|||
|
4d0d4f02aa
|
@@ -10,7 +10,6 @@ from django.utils.safestring import SafeData, mark_safe
|
||||
from django.utils.text import normalize_newlines
|
||||
|
||||
from RIGS import models
|
||||
from training import models as tmodels
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ class Command(BaseCommand):
|
||||
self.setup_categories()
|
||||
self.setup_items()
|
||||
self.setup_levels()
|
||||
self.setup_supervisor()
|
||||
print("Done generating training data")
|
||||
|
||||
def setup_categories(self):
|
||||
@@ -66,3 +67,12 @@ class Command(BaseCommand):
|
||||
models.TrainingLevelRequirement.objects.create(level=supervisor, item=item, depth=random.choice(models.TrainingItemQualification.CHOICES)[0])
|
||||
self.levels.append(technician)
|
||||
self.levels.append(supervisor)
|
||||
|
||||
def setup_supervisor(self):
|
||||
supervisor = models.Profile.objects.create(username="supervisor", first_name="Super", last_name="Visor",
|
||||
initials="SV",
|
||||
email="supervisor@example.com", is_active=True,
|
||||
is_staff=True)
|
||||
supervisor.set_password('supervisor')
|
||||
supervisor.save()
|
||||
models.TrainingLevelQualification.objects.create(trainee=supervisor, level=self.levels[-1], confirmed_on=timezone.now())
|
||||
|
||||
@@ -12,8 +12,9 @@ class Trainee(Profile):
|
||||
def is_supervisor(self):
|
||||
# FIXME Efficiency
|
||||
for level_qualification in self.levels.select_related('level').all():
|
||||
if confirmed_on is not None and level_qualification.level.level >= TrainingLevel.SUPERVISOR:
|
||||
if level_qualification.confirmed_on is not None and level_qualification.level.level >= TrainingLevel.SUPERVISOR:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_records_of_depth(self, depth):
|
||||
return self.qualifications_obtained.filter(depth=depth).select_related('item', 'trainee', 'supervisor')
|
||||
@@ -71,9 +72,18 @@ class TrainingItemQualification(models.Model):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save()
|
||||
for level in TrainingLevel.objects.all(): # Mm yes efficiency
|
||||
for level in TrainingLevel.objects.all(): # Mm yes efficiency FIXME
|
||||
if level.user_has_requirements(self.trainee):
|
||||
level_qualification = TrainingLevelQualification.objects.create(trainee=self.trainee, level=level)
|
||||
level_qualification = TrainingLevelQualification.objects.get_or_create(trainee=self.trainee, level=level)
|
||||
|
||||
@classmethod
|
||||
def get_colour_from_depth(obj, depth):
|
||||
if depth == 0:
|
||||
return "warning"
|
||||
elif depth == 1:
|
||||
return "success"
|
||||
else:
|
||||
return "info"
|
||||
|
||||
class Meta:
|
||||
unique_together = ["trainee", "item", "depth"]
|
||||
|
||||
@@ -15,30 +15,40 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<h4 class="card-header">Users with this level</h4>
|
||||
<div class="card-body">
|
||||
<p>Users with this level...{% lorem %}</p>
|
||||
<div class="card-columns">
|
||||
{% for user in users_with %}
|
||||
<div class="card w-50">
|
||||
<img src="{{user.profile_picture}}" class="card-img-top" />
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{user}}</h5>
|
||||
<p>Qualified on... </p>
|
||||
</div>
|
||||
<div class="card-footer text-right pr-1"><a href="{% url 'profile_detail' user.pk %}" class="btn btn-primary btn-sm"><span class="fas fa-user"></span> View Profile</a></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h4 class="card-header">Level Requirements</h4>
|
||||
<div class="card-body">
|
||||
<table class="table card-body">
|
||||
<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 object.started_requirements %}<li>{{ req.item }} <a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a></li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} <a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a></li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} <a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a></li>{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table class="table card-body">
|
||||
<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 object.started_requirements %}<li>{{ req.item }} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{% endif %}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.complete_requirements %}<li>{{ req.item }} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{% endif %}</li>{% endfor %}</ul></td>
|
||||
<td><ul class="list-unstyled">{% for req in object.passed_out_requirements %}<li>{{ req.item }} {% if request.user.is_supervisor or perms.training.change_traininglevel %}<a type="button" class="btn btn-danger btn-sm" href="{% url 'remove_requirement' pk=req.pk %}"><span class="fas fa-times-circle"></span></a>{%endif%}</li>{% endfor %}</ul></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% include 'partials/last_edited.html' with target="traininglevel_history" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
{% load user_has_qualification from tags %}
|
||||
{% load percentage_complete from tags %}
|
||||
{% load user_level_if_present from tags %}
|
||||
{% load colour_from_depth from tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="col-sm-12 text-right">
|
||||
@@ -12,7 +13,14 @@
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<h2 class="col-12">Training Levels</h2>
|
||||
<div class="alert alert-info" role="alert">Technical Assistant is conferred automatically when the item requirements are met. Technician status is also automatic, but notification of status should be made at the next general meeting, at which point 'approval' should be granted on the system. Supervisor status is <em>not automatic</em> and until signed off at a general meeting, does not count.<sup>Correct as of 7th July 2021, check the Training Policy.</sup></div>
|
||||
<div class="alert alert-info" role="alert">
|
||||
<ul>
|
||||
<li>Technical Assistant is conferred automatically when the item requirements are met.</li>
|
||||
<li>Technician status is also automatic, but notification of status should be made at the next general meeting, at which point 'approval' should be granted on the system.</li>
|
||||
<li>Supervisor status is <em>not automatic</em> and until signed off at a general meeting, does not count.</li>
|
||||
</ul>
|
||||
<sup>Correct as of 7th July 2021, check the Training Policy.</sup>
|
||||
</div>
|
||||
<div class="card-columns">
|
||||
{% for level in levels %}
|
||||
<div class="card my-3">
|
||||
@@ -47,20 +55,20 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="card-footer text-right">
|
||||
{% user_level_if_present object level as level_qualification %}
|
||||
{% if level_qualification %}
|
||||
{% if level_qualification.confirmed_by is None %}
|
||||
{% if request.user.is_supervisor or request.user.is_superuser %}
|
||||
<a class="btn btn-info" href="{% url 'confirm_level' object.pk level.pk %}">Confirm</a>
|
||||
{% else %}
|
||||
<button class="btn btn-warning text-right" disabled>Awaiting Confirmation</button>
|
||||
<button class="btn btn-warning" disabled>Awaiting Confirmation</button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="btn btn-success active">Confirmed <small>by {{ level_qualification.confirmed_by }}</small></button>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button class="btn btn-danger text-right" disabled>Incomplete</button>
|
||||
<button class="btn btn-danger" disabled>Incomplete</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,16 +76,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h2 class="col-12">Training Items</h2><br>
|
||||
<h3 class="col-12">Key: <span class="badge badge-warning">Training Started</span> <span class="badge badge-success">Training Complete</span> <span class="badge badge-info">Passed Out</span></h3>
|
||||
<div class="card-columns">
|
||||
<h2 class="col-10">Training Items</h2><a href="{% url 'trainee_item_detail' object.pk %}" class="btn btn-info col-2"><span class="fas fa-info-circle"></span> View Detailed Record</a><br/>
|
||||
<div class="alert alert-info" role="alert"><h3 class="col-12">Key: <span class="badge badge-warning">Training Started</span> <span class="badge badge-success">Training Complete</span> <span class="badge badge-info">Passed Out</span></h3></div>
|
||||
<div class="card-deck">
|
||||
{% for category in categories %}
|
||||
<div class="card mb-3">
|
||||
<h3 class="card-header">{{ category }}</h3>
|
||||
<div class="list-group list-group-flush">
|
||||
{% for q in object.qualifications_obtained.all %}
|
||||
{% if q.item.category == category %}
|
||||
<li class="list-group-item {% if q.depth == 0 %}list-group-item-warning{%elif q.depth == 1%}list-group-item-success{%else%}list-group-item-info{%endif%}">{{q.item}} ({{q.date}})</li>
|
||||
<li class="list-group-item list-group-item-{% colour_from_depth q.depth %}">{{q.item}} ({{q.date}})</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
42
training/templates/trainee_item_list.html
Normal file
42
training/templates/trainee_item_list.html
Normal file
@@ -0,0 +1,42 @@
|
||||
{% extends 'base_training.html' %}
|
||||
|
||||
{% load url_replace from filters %}
|
||||
{% load paginator from filters %}
|
||||
{% load linkornone from filters %}
|
||||
{% load button from filters %}
|
||||
{% load colour_from_depth from tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Training Item</th>
|
||||
<th>Depth</th>
|
||||
<th>Date</th>
|
||||
<th>Supervisor</th>
|
||||
<th>Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr id="row_item">
|
||||
<th scope="row" class="align-middle" id="cell_name">{{ object.item }}</th>
|
||||
<td class="table-{% colour_from_depth object.depth %}">{{ object.get_depth_display }}</td>
|
||||
<td>{{ object.date }}</td>
|
||||
<td>{{ object.supervisor }}</td>
|
||||
<td>{{ object.notes }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr class="table-warning">
|
||||
<td colspan="6" class="text-center">Nothing found</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -15,15 +15,18 @@
|
||||
<tr>
|
||||
<th scope="col">Name<a href="?{% orderby request 'orderBy' 'name' %}"><span class="caret"></span></a></th>
|
||||
<th>Supervisor?</th>
|
||||
<th>-</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
<tr id="row_item">
|
||||
<th scope="row" class="align-middle" id="cell_name">{{ object.name }}</th>
|
||||
<td>No</td>
|
||||
<td><a class="btn btn-info" href="{% url 'trainee_detail' pk=object.pk %}"><span class="fas fa-eye"></span> View Training Record</a></td>
|
||||
<th scope="row" class="align-middle" id="cell_name">{{ object.name }} {% if request.user.pk == object.pk %}<span class="fas fa-user text-success"></span>{%endif%}</th>
|
||||
<td {% if object.is_supervisor %}class="table-success"{%endif%}>{{ object.is_supervisor|yesno|title }}</td>
|
||||
<td>
|
||||
<a class="btn btn-info" href="{% url 'trainee_detail' pk=object.pk %}"><span class="fas fa-eye"></span> View Training Record</a>
|
||||
<a href="{% url 'trainee_item_detail' pk=object.pk %}" class="btn btn-info"><span class="fas fa-info-circle"></span> View Detailed Record</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr class="table-warning">
|
||||
|
||||
@@ -23,3 +23,6 @@ def user_level_if_present(user, level):
|
||||
def percentage_complete(level, user):
|
||||
return level.percentage_complete(user)
|
||||
|
||||
@register.simple_tag
|
||||
def colour_from_depth(depth):
|
||||
return models.TrainingItemQualification.get_colour_from_depth(depth)
|
||||
|
||||
@@ -20,4 +20,5 @@ urlpatterns = [
|
||||
path('level/<int:pk>/add_requirement/', views.AddLevelRequirement.as_view(), name='add_requirement'),
|
||||
path('level/remove_requirement/<int:pk>/', views.RemoveRequirement.as_view(), name='remove_requirement'),
|
||||
path('trainee/<int:pk>/level/<int:level_pk>/confirm', views.ConfirmLevel.as_view(), name='confirm_level'),
|
||||
path('trainee/<int:pk>/item_record', views.TraineeItemDetail.as_view(), name='trainee_item_detail'),
|
||||
]
|
||||
|
||||
@@ -27,7 +27,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["page_title"] = "{}'s Training Record".format(self.object.first_name + " " + self.object.last_name)
|
||||
context["levels"] = models.TrainingLevel.objects.all()
|
||||
context["categories"] = models.TrainingCategory.objects.all().prefetch_related('items')
|
||||
choices = models.TrainingItemQualification.CHOICES
|
||||
@@ -37,6 +37,19 @@ class TraineeDetail(views.ProfileDetail):
|
||||
return context
|
||||
|
||||
|
||||
class TraineeItemDetail(generic.ListView):
|
||||
model = models.TrainingItemQualification
|
||||
template_name = 'trainee_item_list.html'
|
||||
|
||||
def get_queryset(self):
|
||||
return models.Trainee.objects.get(pk=self.kwargs['pk']).qualifications_obtained.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Detailed Training Record for {}".format(models.Trainee.objects.get(pk=self.kwargs['pk']))
|
||||
return context
|
||||
|
||||
|
||||
class TraineeList(generic.ListView):
|
||||
model = models.Trainee
|
||||
template_name = 'trainee_list.html'
|
||||
@@ -115,6 +128,7 @@ class LevelDetail(generic.DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["page_title"] = "Training Level {}".format(self.object)
|
||||
context["users_with"] = map(lambda qual: qual.trainee, models.TrainingLevelQualification.objects.filter(level=self.object))
|
||||
return context
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ class Command(BaseCommand):
|
||||
hs_group = None
|
||||
|
||||
def handle(self, *args, **options):
|
||||
print("Generating sample user data")
|
||||
from django.conf import settings
|
||||
|
||||
if not (settings.DEBUG or settings.STAGING):
|
||||
@@ -32,6 +33,7 @@ class Command(BaseCommand):
|
||||
self.setup_groups()
|
||||
self.setup_useful_profiles()
|
||||
self.setup_generic_profiles()
|
||||
print("Done generating sample user data")
|
||||
|
||||
def setup_groups(self):
|
||||
self.keyholder_group = Group.objects.create(name='Keyholders')
|
||||
|
||||
Reference in New Issue
Block a user