Compare commits

...

21 Commits

Author SHA1 Message Date
b151e1fcf3 Checkin only requires login (no perms) and block users from editing other checkins at Django level 2023-05-18 13:34:32 +01:00
013922bd90 Add data migration for crew/vehicles 2023-05-18 13:10:09 +01:00
f72b611e77 pep8 2023-05-17 12:23:17 +01:00
8da90ba670 Implement codedoctor suggestions 2023-05-17 09:45:46 +01:00
0d1beeaead Minor fixes 2023-05-16 17:22:42 +01:00
51ff06fadd Add power record status chip, checklist status chip displays number of checklists 2023-05-16 16:01:27 +01:00
06137ab180 Register check in model with the admin site 2023-05-16 14:45:54 +01:00
e7dd8f4cf9 Clicking check out does not redirect the user 2023-05-16 14:42:56 +01:00
2fb8b8c81b UI work 2023-05-15 13:40:15 +01:00
dd02df4a17 Checkin button turns into checkout button where applicable 2023-05-15 01:31:56 +01:00
8cc40fe9fa Minor improvement to homepage UI 2023-05-15 01:28:50 +01:00
904ef1f180 Add homepage checkin for events happening now 2023-05-15 01:26:54 +01:00
417ec8ff3d Basic checkin/out logic complete 2023-05-15 01:12:04 +01:00
97dac51a52 Add ability to edit checkins, more validation 2023-05-15 00:36:57 +01:00
fc8bb2dc4b Fix tests after form split 2023-05-14 22:26:58 +01:00
67d2f80815 Merge branch 'master' into checkin 2023-05-14 22:14:04 +01:00
15a9a03200 Further work on checkin, add role field etc 2023-05-10 18:24:56 +01:00
676cee164b Very initial version of checkin form 2023-05-09 21:37:25 +01:00
cccb84e4dc Remove old 'vehicle/crew' stuff 2023-05-09 20:47:41 +01:00
742836486e Revamp H&S overview, remove individual lists.
They were not a good thing.
2023-05-08 19:59:58 +01:00
e774b06ac4 Split power related parts of event checklist into a seperate form 2023-05-08 19:46:35 +01:00
30 changed files with 1179 additions and 870 deletions

View File

@@ -48,6 +48,7 @@ class Index(generic.TemplateView): # Displays the current rig count along with
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['rig_count'] = models.Event.objects.rig_count() context['rig_count'] = models.Event.objects.rig_count()
context['now'] = models.Event.objects.events_in_bounds(timezone.now(), timezone.now())
return context return context

View File

@@ -20,6 +20,7 @@ admin.site.register(models.VatRate, VersionAdmin)
admin.site.register(models.Event, VersionAdmin) admin.site.register(models.Event, VersionAdmin)
admin.site.register(models.EventItem, VersionAdmin) admin.site.register(models.EventItem, VersionAdmin)
admin.site.register(models.Invoice, VersionAdmin) admin.site.register(models.Invoice, VersionAdmin)
admin.site.register(models.EventCheckIn)
@transaction.atomic() # Copied from django-extensions. GenericForeignKey support removed as unnecessary. @transaction.atomic() # Copied from django-extensions. GenericForeignKey support removed as unnecessary.
@@ -206,3 +207,8 @@ class RiskAssessmentAdmin(VersionAdmin):
@admin.register(models.EventChecklist) @admin.register(models.EventChecklist)
class EventChecklistAdmin(VersionAdmin): class EventChecklistAdmin(VersionAdmin):
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by') list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
@admin.register(models.PowerTestRecord)
class EventChecklistAdmin(VersionAdmin):
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')

View File

@@ -44,7 +44,7 @@ class EventForm(forms.ModelForm):
return simplejson.dumps(items) return simplejson.dumps(items)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['items_json'].initial = self._get_items_json self.fields['items_json'].initial = self._get_items_json
self.fields['start_date'].widget.format = '%Y-%m-%d' self.fields['start_date'].widget.format = '%Y-%m-%d'
@@ -200,86 +200,41 @@ class EventChecklistForm(forms.ModelForm):
related_models = { related_models = {
'venue': models.Venue, 'venue': models.Venue,
'power_mic': models.Profile,
} }
# Two possible formats
def parsedatetime(self, date_string):
try:
return timezone.make_aware(datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%S'))
except ValueError:
return timezone.make_aware(datetime.strptime(date_string, '%Y-%m-%dT%H:%M'))
# There's probably a thousand better ways to do this, but this one is mine
def clean(self):
vehicles = {key: val for key, val in self.data.items()
if key.startswith('vehicle')}
for key in vehicles:
pk = int(key.split('_')[1])
driver_key = 'driver_' + str(pk)
if (self.data[driver_key] == ''):
raise forms.ValidationError('Add a driver to vehicle ' + str(pk), code='vehicle_mismatch')
else:
try:
item = models.EventChecklistVehicle.objects.get(pk=pk)
except models.EventChecklistVehicle.DoesNotExist:
item = models.EventChecklistVehicle()
item.vehicle = vehicles['vehicle_' + str(pk)]
item.driver = models.Profile.objects.get(pk=self.data[driver_key])
item.full_clean('checklist')
# item does not have a database pk yet as it isn't saved
self.items['v' + str(pk)] = item
crewmembers = {key: val for key, val in self.data.items()
if key.startswith('crewmember')}
other_fields = ['start', 'role', 'end']
for key in crewmembers:
pk = int(key.split('_')[1])
for field in other_fields:
value = self.data[f'{field}_{pk}']
if value == '':
raise forms.ValidationError(f'Add a {field} to crewmember {pk}', code=f'{field}_mismatch')
try:
item = models.EventChecklistCrew.objects.get(pk=pk)
except models.EventChecklistCrew.DoesNotExist:
item = models.EventChecklistCrew()
item.crewmember = models.Profile.objects.get(pk=self.data['crewmember_' + str(pk)])
item.start = self.parsedatetime(self.data['start_' + str(pk)])
item.role = self.data['role_' + str(pk)]
item.end = self.parsedatetime(self.data['end_' + str(pk)])
item.full_clean('checklist')
# item does not have a database pk yet as it isn't saved
self.items['c' + str(pk)] = item
return super(EventChecklistForm, self).clean()
def save(self, commit=True):
checklist = super(EventChecklistForm, self).save(commit=False)
if (commit):
# Remove all existing, to be recreated from the form
checklist.vehicles.all().delete()
checklist.crew.all().delete()
checklist.save()
for key in self.items:
item = self.items[key]
reversion.add_to_revision(item)
# finish and save new database items
item.checklist = checklist
item.full_clean()
item.save()
self.items.clear()
return checklist
class Meta: class Meta:
model = models.EventChecklist model = models.EventChecklist
fields = '__all__' fields = '__all__'
exclude = ['reviewed_at', 'reviewed_by'] exclude = ['reviewed_at', 'reviewed_by']
class PowerTestRecordForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.__class__ == forms.NullBooleanField:
# Only display yes/no to user, the 'none' is only ever set in the background
field.widget = forms.CheckboxInput()
class Meta:
model = models.PowerTestRecord
fields = '__all__'
exclude = ['reviewed_at', 'reviewed_by']
class EventCheckInForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['time'].initial = timezone.now()
self.fields['role'].initial = "Crew"
class Meta:
model = models.EventCheckIn
fields = '__all__'
exclude = ['end_time']
class EditCheckInForm(forms.ModelForm):
class Meta:
model = models.EventCheckIn
fields = '__all__'

View File

@@ -278,7 +278,7 @@ class Command(BaseCommand):
suspended_structures=bool(random.getrandbits(1)), suspended_structures=bool(random.getrandbits(1)),
outside=bool(random.getrandbits(1))) outside=bool(random.getrandbits(1)))
if i == 0 or random.randint(0, 1) > 0: # Event 1 and 1 in 10 have a Checklist if i == 0 or random.randint(0, 1) > 0: # Event 1 and 1 in 10 have a Checklist
models.EventChecklist.objects.create(event=new_event, power_mic=random.choice(self.profiles), models.EventChecklist.objects.create(event=new_event,
safe_parking=bool(random.getrandbits(1)), safe_parking=bool(random.getrandbits(1)),
safe_packing=bool(random.getrandbits(1)), safe_packing=bool(random.getrandbits(1)),
exits=bool(random.getrandbits(1)), exits=bool(random.getrandbits(1)),
@@ -287,6 +287,4 @@ class Command(BaseCommand):
ear_plugs=bool(random.getrandbits(1)), ear_plugs=bool(random.getrandbits(1)),
hs_location="Locked away safely", hs_location="Locked away safely",
extinguishers_location="Somewhere, I forgot", extinguishers_location="Somewhere, I forgot",
earthing=bool(random.getrandbits(1)),
pat=bool(random.getrandbits(1)),
date=timezone.now(), venue=random.choice(self.venues)) date=timezone.now(), venue=random.choice(self.venues))

View File

@@ -0,0 +1,71 @@
# Generated by Django 3.2.16 on 2023-05-08 15:58
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import versioning.versioning
def migrate_old_data(apps, schema_editor):
EventChecklist = apps.get_model('RIGS', 'EventChecklist')
PowerTestRecord = apps.get_model('RIGS', 'PowerTestRecord')
for ec in EventChecklist.objects.all():
# New highscore for the most pythonic BS I've ever written.
PowerTestRecord.objects.create(event=ec.event, **{i.name:getattr(ec, i.attname) for i in PowerTestRecord._meta.get_fields() if not (i.is_relation or i.auto_created)})
def revert(apps, schema_editor):
apps.get_model('RIGS', 'PowerTestRecord').objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0045_alter_profile_is_approved'),
]
operations = [
migrations.CreateModel(
name='PowerTestRecord',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='power_tests', to='RIGS.event')),
('notes', models.TextField(blank=True, default='')),
('venue', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='RIGS.venue')),
('reviewed_at', models.DateTimeField(null=True)),
('rcds', models.BooleanField(blank=True, help_text='RCDs installed where needed and tested?', null=True)),
('supply_test', models.BooleanField(blank=True, help_text='Electrical supplies tested?<br><small>(using socket tester)</small>', null=True)),
('earthing', models.BooleanField(blank=True, help_text='Equipment appropriately earthed?<br><small>(truss, stage, generators etc)</small>', null=True)),
('pat', models.BooleanField(blank=True, help_text='All equipment in PAT period?', null=True)),
('source_rcd', models.BooleanField(blank=True, help_text='Source RCD protected?<br><small>(if cable is more than 3m long) </small>', null=True)),
('labelling', models.BooleanField(blank=True, help_text='Appropriate and clear labelling on distribution and cabling?', null=True)),
('fd_voltage_l1', models.IntegerField(blank=True, help_text='L1 - N', null=True, verbose_name='First Distro Voltage L1-N')),
('fd_voltage_l2', models.IntegerField(blank=True, help_text='L2 - N', null=True, verbose_name='First Distro Voltage L2-N')),
('fd_voltage_l3', models.IntegerField(blank=True, help_text='L3 - N', null=True, verbose_name='First Distro Voltage L3-N')),
('fd_phase_rotation', models.BooleanField(blank=True, help_text='Phase Rotation<br><small>(if required)</small>', null=True, verbose_name='Phase Rotation')),
('fd_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('fd_pssc', models.IntegerField(blank=True, help_text='Prospective Short Circuit Current', null=True, verbose_name='PSCC')),
('w1_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w1_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w1_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w1_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('w2_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w2_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w2_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w2_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('w3_description', models.CharField(blank=True, default='', help_text='Description', max_length=255)),
('w3_polarity', models.BooleanField(blank=True, help_text='Polarity Checked?', null=True)),
('w3_voltage', models.IntegerField(blank=True, help_text='Voltage', null=True)),
('w3_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (Z<small>S</small>)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
('all_rcds_tested', models.BooleanField(blank=True, help_text='All circuit RCDs tested?<br><small>(using test button)</small>', null=True)),
('public_sockets_tested', models.BooleanField(blank=True, help_text='Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>', null=True)),
('reviewed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Reviewer')),
],
options={
'abstract': False,
'ordering': ['event'],
'permissions': [('review_power', 'Can review Power Test Records')],
},
bases=(models.Model, versioning.versioning.RevisionMixin),
),
migrations.RunPython(migrate_old_data, reverse_code=revert),
]

View File

@@ -0,0 +1,41 @@
# Generated by Django 3.2.19 on 2023-05-17 08:44
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
def migrate_old_data(apps, schema_editor):
EventChecklist = apps.get_model('RIGS', 'EventChecklist')
EventCheckIn = apps.get_model('RIGS', 'EventCheckIn')
for ec in EventChecklist.objects.all():
for crew in ec.crew.all():
vehicle = ec.vehicles.get(driver=crew.crewmember) or None
EventCheckIn.objects.create(event=ec.event, person=crew.crewmember, role=crew.role, time=crew.start, end_time=crew.end, vehicle=vehicle.vehicle)
def revert(apps, schema_editor):
apps.get_model('RIGS', 'EventCheckIn').objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0046_create_powertests'),
]
operations = [
migrations.CreateModel(
name='EventCheckIn',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('time', models.DateTimeField()),
('role', models.CharField(blank=True, max_length=50)),
('vehicle', models.CharField(blank=True, max_length=100)),
('end_time', models.DateTimeField(blank=True, null=True)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='crew', to='RIGS.event')),
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checkins', to=settings.AUTH_USER_MODEL)),
],
),
migrations.RunPython(migrate_old_data, reverse_code=revert),
]

View File

@@ -0,0 +1,156 @@
# Generated by Django 3.2.19 on 2023-05-18 11:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0047_auto_20230517_0944'),
]
operations = [
migrations.RemoveField(
model_name='eventchecklistvehicle',
name='checklist',
),
migrations.RemoveField(
model_name='eventchecklistvehicle',
name='driver',
),
migrations.RemoveField(
model_name='eventchecklist',
name='all_rcds_tested',
),
migrations.RemoveField(
model_name='eventchecklist',
name='earthing',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_phase_rotation',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_pssc',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l1',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l2',
),
migrations.RemoveField(
model_name='eventchecklist',
name='fd_voltage_l3',
),
migrations.RemoveField(
model_name='eventchecklist',
name='labelling',
),
migrations.RemoveField(
model_name='eventchecklist',
name='pat',
),
migrations.RemoveField(
model_name='eventchecklist',
name='power_mic',
),
migrations.RemoveField(
model_name='eventchecklist',
name='public_sockets_tested',
),
migrations.RemoveField(
model_name='eventchecklist',
name='rcds',
),
migrations.RemoveField(
model_name='eventchecklist',
name='source_rcd',
),
migrations.RemoveField(
model_name='eventchecklist',
name='supply_test',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w1_voltage',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w2_voltage',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_description',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_earth_fault',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_polarity',
),
migrations.RemoveField(
model_name='eventchecklist',
name='w3_voltage',
),
migrations.AddField(
model_name='powertestrecord',
name='power_mic',
field=models.ForeignKey(blank=True, help_text='Who is the Power MIC?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='checklists', to=settings.AUTH_USER_MODEL, verbose_name='Power MIC'),
),
migrations.AlterField(
model_name='eventchecklist',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='powertestrecord',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AlterField(
model_name='riskassessment',
name='reviewed_at',
field=models.DateTimeField(blank=True, null=True),
),
migrations.DeleteModel(
name='EventChecklistCrew',
),
migrations.DeleteModel(
name='EventChecklistVehicle',
),
]

View File

@@ -81,6 +81,10 @@ class Profile(AbstractUser):
def __str__(self): def __str__(self):
return self.name return self.name
def current_event(self):
q = EventCheckIn.objects.filter(person=self, end_time=None)
return q.latest('time') if q.exists() else None
class ContactableManager(models.Manager): class ContactableManager(models.Manager):
def search(self, query=None): def search(self, query=None):
@@ -405,7 +409,15 @@ class Event(models.Model, RevisionMixin):
@property @property
def hs_done(self): def hs_done(self):
return self.riskassessment is not None and len(self.checklists.all()) > 0 return self.riskassessment is not None and self.has_checklist and self.has_power
@property
def has_checklist(self):
return self.checklists.exists()
@property
def has_power(self):
return self.power_tests.exists()
@property @property
def has_start_time(self): def has_start_time(self):
@@ -478,6 +490,15 @@ class Event(models.Model, RevisionMixin):
else: else:
return bool(self.purchase_order) return bool(self.purchase_order)
@property
def can_check_in(self):
earliest = self.earliest_time
if isinstance(self.earliest_time, datetime.date):
earliest = datetime.datetime.combine(self.start_date, datetime.time(00, 00))
tz = pytz.timezone(settings.TIME_ZONE)
earliest = tz.localize(earliest)
return not self.dry_hire and earliest <= timezone.now()
objects = EventManager() objects = EventManager()
def get_absolute_url(self): def get_absolute_url(self):
@@ -689,8 +710,21 @@ def validate_url(value):
raise ValidationError('URL must point to a location on the TEC Sharepoint') raise ValidationError('URL must point to a location on the TEC Sharepoint')
class ReviewableModel(models.Model):
reviewed_at = models.DateTimeField(null=True, blank=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
class Meta:
abstract = True
@cached_property
def fieldz(self):
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
@reversion.register @reversion.register
class RiskAssessment(models.Model, RevisionMixin): class RiskAssessment(ReviewableModel, RevisionMixin):
SMALL = (0, 'Small') SMALL = (0, 'Small')
MEDIUM = (1, 'Medium') MEDIUM = (1, 'Medium')
LARGE = (2, 'Large') LARGE = (2, 'Large')
@@ -738,10 +772,6 @@ class RiskAssessment(models.Model, RevisionMixin):
# Blimey that was a lot of options # Blimey that was a lot of options
reviewed_at = models.DateTimeField(null=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
supervisor_consulted = models.BooleanField(null=True) supervisor_consulted = models.BooleanField(null=True)
expected_values = { expected_values = {
@@ -778,10 +808,6 @@ class RiskAssessment(models.Model, RevisionMixin):
('review_riskassessment', 'Can review Risk Assessments') ('review_riskassessment', 'Can review Risk Assessments')
] ]
@cached_property
def fieldz(self):
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
@property @property
def event_size(self): def event_size(self):
# Confirm event size. Check all except generators, since generators entails outside # Confirm event size. Check all except generators, since generators entails outside
@@ -795,6 +821,12 @@ class RiskAssessment(models.Model, RevisionMixin):
def get_event_size_display(self): def get_event_size_display(self):
return self.SIZES[self.event_size][1] + " Event" return self.SIZES[self.event_size][1] + " Event"
def __str__(self):
return f"{self.pk} | {self.event}"
def get_absolute_url(self):
return reverse('ra_detail', kwargs={'pk': self.pk})
@property @property
def activity_feed_string(self): def activity_feed_string(self):
return str(self.event) return str(self.event)
@@ -803,20 +835,12 @@ class RiskAssessment(models.Model, RevisionMixin):
def name(self): def name(self):
return str(self) return str(self)
def get_absolute_url(self):
return reverse('ra_detail', kwargs={'pk': self.pk})
def __str__(self): @reversion.register
return f"{self.pk} | {self.event}" class EventChecklist(ReviewableModel, RevisionMixin):
@reversion.register(follow=['vehicles', 'crew'])
class EventChecklist(models.Model, RevisionMixin):
event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE) event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE)
# General # General
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
venue = models.ForeignKey('Venue', on_delete=models.CASCADE) venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
date = models.DateField() date = models.DateField()
@@ -830,6 +854,32 @@ class EventChecklist(models.Model, RevisionMixin):
hs_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of Safety Bag/Box") hs_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of Safety Bag/Box")
extinguishers_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of fire extinguishers") extinguishers_location = models.CharField(blank=True, default='', max_length=255, help_text="Location of fire extinguishers")
inverted_fields = []
class Meta:
ordering = ['event']
permissions = [
('review_eventchecklist', 'Can review Event Checklists')
]
def __str__(self):
return f"{self.pk} - {self.event}"
@property
def activity_feed_string(self):
return str(self.event)
def get_absolute_url(self):
return reverse('ec_detail', kwargs={'pk': self.pk})
@reversion.register
class PowerTestRecord(ReviewableModel, RevisionMixin):
event = models.ForeignKey('Event', related_name='power_tests', on_delete=models.CASCADE)
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='checklists',
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
venue = models.ForeignKey('Venue', on_delete=models.CASCADE)
notes = models.TextField(blank=True, default='')
# Small Electrical Checks # Small Electrical Checks
rcds = models.BooleanField(blank=True, null=True, help_text="RCDs installed where needed and tested?") rcds = models.BooleanField(blank=True, null=True, help_text="RCDs installed where needed and tested?")
supply_test = models.BooleanField(blank=True, null=True, help_text="Electrical supplies tested?<br><small>(using socket tester)</small>") supply_test = models.BooleanField(blank=True, null=True, help_text="Electrical supplies tested?<br><small>(using socket tester)</small>")
@@ -865,58 +915,40 @@ class EventChecklist(models.Model, RevisionMixin):
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>") all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?<br><small>(using test button)</small>")
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>") public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?<br><small>(using socket tester)</small>")
reviewed_at = models.DateTimeField(null=True)
reviewed_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True,
verbose_name="Reviewer", on_delete=models.CASCADE)
inverted_fields = []
class Meta: class Meta:
ordering = ['event'] ordering = ['event']
permissions = [ permissions = [
('review_eventchecklist', 'Can review Event Checklists') ('review_power', 'Can review Power Test Records')
] ]
@cached_property def __str__(self):
def fieldz(self): return f"{self.pk} - {self.event}"
return [n.name for n in list(self._meta.get_fields()) if n.name != 'reviewed_at' and n.name != 'reviewed_by' and not n.is_relation and not n.auto_created]
@property @property
def activity_feed_string(self): def activity_feed_string(self):
return str(self.event) return str(self.event)
def get_absolute_url(self):
return reverse('ec_detail', kwargs={'pk': self.pk}) class EventCheckIn(models.Model):
event = models.ForeignKey('Event', related_name='crew', on_delete=models.CASCADE)
person = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checkins', on_delete=models.CASCADE)
time = models.DateTimeField()
role = models.CharField(max_length=50, blank=True)
vehicle = models.CharField(max_length=100, blank=True)
end_time = models.DateTimeField(null=True, blank=True)
def __str__(self): def __str__(self):
return f"{self.pk} - {self.event}" return f"{self.person} on {self.event}"
@reversion.register
class EventChecklistVehicle(models.Model, RevisionMixin):
checklist = models.ForeignKey('EventChecklist', related_name='vehicles', blank=True, on_delete=models.CASCADE)
vehicle = models.CharField(max_length=255)
driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='vehicles', on_delete=models.CASCADE)
reversion_hide = True
def __str__(self):
return f"{self.vehicle} driven by {self.driver}"
@reversion.register
class EventChecklistCrew(models.Model, RevisionMixin):
checklist = models.ForeignKey('EventChecklist', related_name='crew', blank=True, on_delete=models.CASCADE)
crewmember = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='crewed', on_delete=models.CASCADE)
role = models.CharField(max_length=255)
start = models.DateTimeField()
end = models.DateTimeField()
reversion_hide = True
def clean(self): def clean(self):
if self.start > self.end: sass = " Please invent time travel and retry."
raise ValidationError('Unless you\'ve invented time travel, crew can\'t finish before they have started.') if self.time > timezone.now():
raise ValidationError("May not check in in the future." + sass)
if self.end_time and self.end_time < self.time:
raise ValidationError("May not check out before you've checked in." + sass)
def __str__(self): def get_absolute_url(self):
return f"{self.crewmember} ({self.role})" return reverse('event_detail', kwargs={'pk': self.event_id})
def active(self):
return end_time is not None

View File

@@ -34,16 +34,7 @@
</div> </div>
</li> </li>
{% if perms.RIGS.view_riskassessment %} {% if perms.RIGS.view_riskassessment %}
<li class="nav-item dropdown"> <li class="nav-item"><a class="nav-link" href="{% url 'hs_list' %}">H&S</a></li>
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownHS" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
H&S
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownHS">
<a class="dropdown-item" href="{% url 'hs_list' %}"><span class="fas fa-eye"></span> Overview</a>
<a class="dropdown-item" href="{% url 'ra_list' %}"><span class="fas fa-file-medical"></span> Risk Assessments</a>
<a class="dropdown-item" href="{% url 'ec_list' %}"><span class="fas fa-tasks"></span> Event Checklists</a>
</div>
</li>
{% endif %} {% endif %}
{% if perms.RIGS.view_invoice %} {% if perms.RIGS.view_invoice %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">

View File

@@ -1,6 +1,8 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load markdown_tags %} {% load markdown_tags %}
{% load button from filters %}
{% load static %}
{% block content %} {% block content %}
<div class="row my-3 py-3"> <div class="row my-3 py-3">
@@ -52,6 +54,43 @@
</div> </div>
</div> </div>
</div> </div>
{% if event.can_check_in %}
<div class="col-sm-12">
<div class="card mt-3">
<div class="card-header">Crew Record</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Vehicle</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
<th scope="col">{% if request.user.pk is event.mic.pk %}<a href="{% url 'event_checkin_override' event.pk %}" class="btn btn-sm btn-success"><span class="fas fa-plus"></span> Add</a>{% endif %}</th>
</tr>
</thead>
<tbody id="crewmembers">
{% for crew in object.crew.all %}
<tr>
<td>{{crew.person}}</td>
<td>{{crew.vehicle|default:"None"}}</td>
<td>{{crew.time}}</td>
<td>{{crew.role}}</td>
<td>{% if crew.end_time %}{{crew.end_time}}{% else %}<span class="text-success fas fa-clock" data-toggle="tooltip" title="This person is currently checked into this event"></span>{% endif %}</td>
<td>{% if crew.end_time %}{% if crew.person.pk == request.user.pk or event.mic.pk == request.user.pk %}{% button 'edit' 'edit_checkin' crew.pk clazz='btn-sm modal-href' %}{% endif %}{%endif%}</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center bg-warning">Apparently this event happened by magic...</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</div>
{% if not request.is_ajax and perms.RIGS.view_event %} {% if not request.is_ajax and perms.RIGS.view_event %}
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
{% include 'partials/event_detail_buttons.html' %} {% include 'partials/event_detail_buttons.html' %}

View File

@@ -30,21 +30,7 @@
</a> </a>
{% endif %} {% endif %}
</dd> </dd>
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
<dd class="col-6">
{% if object.power_mic %}
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
{% else %}
None
{% endif %}
</dd>
</dl> </dl>
<p>List vehicles and their drivers</p>
<ul>
{% for i in object.vehicles.all %}
<li>{{i}}</li>
{% endfor %}
</ul>
</div> </div>
</div> </div>
</div> </div>
@@ -82,165 +68,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="card mb-3">
<div class="card-header">Crew Record</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Crewmember</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
</tr>
</thead>
<tbody id="crewmemberst">
{% for crew in object.crew.all %}
<tr>
<td>{{crew.crewmember}}</td>
<td>{{crew.start}}</td>
<td>{{crew.role}}</td>
<td>{{crew.end}}</td>
</tr>
{% empty %}
<tr>
<td colspan="4" class="text-center bg-warning">Apparently this event happened by magic...</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card mb-3">
<div class="card-header">Power {% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
<div class="card-body">
{% if object.event.riskassessment.event_size == 0 %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
<dd class="col-2">
{{ object.rcds|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
<dd class="col-2">
{{ object.supply_test|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
{% else %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
<dd class="col-2">
{{ object.source_rcd|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'labelling'|safe }}</dt>
<dd class="col-2">
{{ object.labelling|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
<hr>
<p>Tests at first distro</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th>{{ object|help_text:'fd_voltage_l1' }}</th>
<th>{{ object|help_text:'fd_voltage_l2' }}</th>
<th>{{ object|help_text:'fd_voltage_l3' }}</th>
</tr>
<tr>
<td>{{ object.fd_voltage_l1 }}</td>
<td>{{ object.fd_voltage_l2 }}</td>
<td>{{ object.fd_voltage_l3 }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_phase_rotation'|safe }}</th>
<td colspan="3">{{ object.fd_phase_rotation|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_earth_fault'|safe}}</th>
<td colspan="3">{{ object.fd_earth_fault }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_pssc'}}</th>
<td colspan="3">{{ object.fd_pssc }}</td>
</tr>
</tbody>
</table>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ object|help_text:'w1_description'|safe}}</th>
<td>{{ object.w1_description }}</td>
<td>{{ object.w2_description|default:'' }}</td>
<td>{{ object.w3_description|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_polarity'|safe}}</th>
<td>{{ object.w1_polarity|yesnoi }}</td>
<td>{{ object.w2_polarity|default:''|yesnoi }}</td>
<td>{{ object.w3_polarity|default:''|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_voltage'|safe}}</th>
<td>{{ object.w1_voltage }}</td>
<td>{{ object.w2_voltage|default:'' }}</td>
<td>{{ object.w3_voltage|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_earth_fault'|safe}}</th>
<td>{{ object.w1_earth_fault }}</td>
<td>{{ object.w2_earth_fault|default:'' }}</td>
<td>{{ object.w3_earth_fault|default:'' }}</td>
</tr>
</tbody>
</table>
<hr>
<dl class="row">
<dt class="col-10">{{ object|help_text:'all_rcds_tested'|safe }}</dt>
<dd class="col-2">
{{ object.all_rcds_tested|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'public_sockets_tested'|safe }}</dt>
<dd class="col-2">
{{ object.public_sockets_tested|yesnoi }}
</dd>
</dl>
<hr>
{% include 'partials/ec_power_info.html' %}
{% endif %}
</div>
</div>
<div class="col-12 text-right"> <div class="col-12 text-right">
{% button 'edit' url='ec_edit' pk=object.pk %} {% button 'edit' url='ec_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.pk text="Event" %} {% button 'view' url='event_detail' pk=object.pk text="Event" %}

View File

@@ -13,57 +13,19 @@
{% block preload_js %} {% block preload_js %}
{{ block.super }} {{ block.super }}
<script src="{% static 'js/selects.js' %}"></script> <script src="{% static 'js/selects.js' %}"></script>
<script src="{% static 'js/interaction.js' %}"></script>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
{{ block.super }} {{ block.super }}
<script src="{% static 'js/autocompleter.js' %}"></script> <script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
<script>
$(document).ready(function () {
$('button[data-action=add]').on('click', function (event) {
event.preventDefault();
let target = $($(this).attr('data-target'));
let newID = Number(target.attr('data-pk'));
let newRow = $($(this).attr('data-clone'))
.clone().attr('style', "")
.attr('id', function(i, val){
return val.split("_")[0] + '_' + newID;
})
.appendTo(target);
newRow.find('select,input').attr('name', function(i, val){
return val.split("_")[0] + '_' + newID;
})//Disabled is to prevent the hidden row being sent to the form
.removeAttr('disabled');
newRow.find('button[data-action=delete]').attr('data-id', newID);
newRow.find('select').addClass('selectpicker');
newRow.find('.selectpicker').selectpicker('refresh');
$(".selectpicker").each(function(){initPicker($(this))});
initDatetime();
$(target).attr('data-pk', newID - 1);
});
$(document).on('click', 'button[data-action=delete]', function(event) {
event.preventDefault();
$(this).closest('tr').remove();
});
//Somewhat rudimentary way of ensuring people fill in completely (if it hits the database validation the whole table row disappears when the page reloads...)
//the not is to avoid adding it to some of bootstrap-selects extra crap
$('#vehiclest,#crewmemberst').on('change', 'select,input', function () {
$(this).closest('tr').find("select,input").not(':input[type=search]').attr('required', 'true');
});
});
</script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-12"> <div class="col-12">
{% include 'form_errors.html' %} {% include 'form_errors.html' %}
{% if edit %}
<form role="form" method="POST" action="{% url 'ec_edit' pk=object.pk %}"> <form role="form" method="POST" action="{% if edit %}{% url 'ec_edit' pk=object.pk %}{% else %}{% url 'event_ec' pk=event.pk %}{% endif %}">
{% else %}
<form role="form" method="POST" action="{% url 'event_ec' pk=event.pk %}">
{% endif %}
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}" <input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/> value="{{event.pk}}"/>
{% csrf_token %} {% csrf_token %}
@@ -94,7 +56,7 @@
<div class="form-group form-row" id="{{ form.venue.id_for_label }}-group"> <div class="form-group form-row" id="{{ form.venue.id_for_label }}-group">
<label for="{{ form.venue.id_for_label }}" <label for="{{ form.venue.id_for_label }}"
class="col-4 col-form-label">{{ form.venue.label }}</label> class="col-4 col-form-label">{{ form.venue.label }}</label>
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}"> <select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %} {% if venue %}
<option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option> <option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option>
{% elif event.venue %} {% elif event.venue %}
@@ -102,52 +64,6 @@
{% endif %} {% endif %}
</select> </select>
</div> </div>
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
<label for="{{ form.power_mic.id_for_label }}"
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
{% if power_mic %}
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
{% elif event.riskassessment.power_mic %}
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
{% endif %}
</select>
</div>
<p class="pt-3 font-weight-bold">List vehicles and their drivers</p>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Vehicle</th>
<th scope="col">Driver</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="vehiclest" data-pk="-1">
<tr id="vehicles_new" style="display: none;">
<td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
<td><select data-container="body" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true"></select></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
</tr>
{% for i in object.vehicles.all %}
<tr id="vehicles_{{i.pk}}">
<td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
<td>
<select data-container="body" name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if i.driver != '' %}
<option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
{% endif %}
</select>
</td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{i.pk}}' data-action='delete' data-target='#vehicle'><span class="fas fa-times"></span></button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="text-right">
<button type="button" class="btn btn-secondary" id="vehicle-add" data-action='add' data-target='#vehiclest' data-clone='#vehicles_new'><span class="fas fa-plus"></span> Add Vehicle</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -175,176 +91,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row my-3">
<div class="col-12">
<div class="card">
<div class="card-header">Crew Record</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Person</th>
<th scope="col">Start Time</th>
<th scope="col">Role</th>
<th scope="col">End Time</th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="crewmemberst" data-pk="-1">
<tr id="crew_new" style="display: none;">
<td>
<select name="crewmember_new" class="form-control" data-container="body" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" disabled="true"></select>
</td>
<td style="min-width: 15ch"><input name="start_new" type="datetime-local" class="form-control" value="{{ i.start }}" disabled=""/></td>
<td style="min-width: 15ch"><input name="role_new" type="text" class="form-control" value="{{ i.role }}" disabled="true"/></td>
<td style="min-width: 15ch"><input name="end_new" type="datetime-local" class="form-control" value="{{ i.end }}" disabled="true" /></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
</tr>
{% for crew in object.crew.all %}
<tr id="crew_{{crew.pk}}">
<td>
<select data-container="body" name="crewmember_{{crew.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if crew.crewmember != '' %}
<option value="{{crew.crewmember.pk}}" selected="selected">{{ crew.crewmember.name }}</option>
{% endif %}
</select>
</td>
<td><input name="start_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.start|date:'Y-m-d' }}T{{ crew.start|date:'H:i:s' }}"/></td>
<td><input name="role_{{crew.pk}}" type="text" class="form-control" value="{{ crew.role }}"/></td>
<td><input name="end_{{crew.pk}}" type="datetime-local" class="form-control" value="{{ crew.end|date:'Y-m-d' }}T{{ crew.end|date:'H:i:s' }}"/></td>
<td><button type="button" class="btn btn-danger btn-sm mt-1" data-id='{{crew.pk}}' data-action='delete' data-target='#crewmember'><span class="fas fa-times"></span></button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<div class="text-right">
<button type="button" class="btn btn-secondary" data-action='add' data-target='#crewmemberst' data-clone='#crew_new'><span class="fas fa-plus"></span> Add Crewmember</button>
</div>
</div>
</div>
</div>
</div>
{% if event.riskassessment.event_size == 0 %}
<div class="row my-3" id="size-0">
<div class="col-12">
<div class="card border-success">
<div class="card-header">Electrical Checks <small>for Small TEC Events <6kVA (approx. 26A)</small></div>
<div class="card-body">
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
</div>
</div>
</div>
</div>
{% else %}
<div class="row my-3" id="size-1">
<div class="col-12">
{% if event.riskassessment.event_size == 1 %}
<div class="card border-warning">
<div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div>
<div class="card-body">
{% else %}
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
{% endif %}
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
<hr>
<p>Tests at first distro</p>
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
</tr>
<tr>
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{form.w1_description.help_text|safe}}</th>
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
<td>{% render_field form.w1_polarity %}</td>
<td>{% render_field form.w2_polarity %}</td>
<td>{% render_field form.w3_polarity %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr/>
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
{% include 'partials/ec_power_info.html' %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row mt-3"> <div class="row mt-3">
<div class="col-sm-12 text-right"> <div class="col-sm-12 text-right">
{% button 'submit' %} {% button 'submit' %}

View File

@@ -0,0 +1,105 @@
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
{% load widget_tweaks %}
{% load static %}
{% load button from filters %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static 'css/selects.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'css/easymde.min.css' %}">
{% endblock %}
{% block preload_js %}
{{ block.super }}
<script src="{% static 'js/selects.js' %}"></script>
<script src="{% static 'js/easymde.min.js' %}"></script>
{% endblock %}
{% block js %}
{{ block.super }}
<script src="{% static 'js/autocompleter.js' %}"></script>
<script src="{% static 'js/interaction.js' %}"></script>
<script src="{% static 'js/tooltip.js' %}"></script>
{% endblock %}
{% block content %}
<div class="col-12">
{% include 'form_errors.html' %}
<form id="checkin" role="form" method="POST" action="{{ form.action|default:request.path }}">
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/>
{% if not request.is_ajax and self.request.user.pk is form.event.mic.pk %}
<div class="form-group">
<label for="{{ form.person.id_for_label }}"
class="col-sm-4 col-form-label">{{ form.person.label }}</label>
<div class="col-sm-8">
<select id="{{ form.person.id_for_label }}" name="{{ form.person.name }}" class="px-0 selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if person %}
<option value="{{form.person.value}}" selected="selected" >{{ person.name }}</option>
{% endif %}
</select>
</div>
</div>
{% else %}
<input type="hidden" name="{{ form.person.name }}" id="{{ form.person.id_for_label }}"
value="{{request.user.pk}}"/>
{% endif %}
{% csrf_token %}
<div class="form-group">
<label for="{{ form.time.id_for_label }}"
class="col-sm-4 col-form-label">Start Time</label>
<div class="col-sm-8">
{% render_field form.time class+="form-control" %}
</div>
</div>
<div class="form-group">
<label for="{{ form.role.id_for_label }}" class="col col-form-label">Role</label>
<div class="row pl-3">
<div class="col-md-6 col-sm-12">
<button type="button" class="btn btn-primary" onclick="document.getElementById('id_role').value='MIC'">MIC</button>
<button type="button" class="btn btn-danger" onclick="document.getElementById('id_role').value='Power MIC'">Power MIC</button>
<button type="button" class="btn btn-info" onclick="document.getElementById('id_role').value='Crew'">Crew</button>
</div>
<div class="col-md-6 mt-2">
{% render_field form.role class+="form-control" placeholder="Other (enter text)" %}
</div>
</div>
</div>
<div class="form-group">
<label for="{{ form.vehicle.id_for_label }}" class="col col-form-label">Vehicle (if applicable)</label>
<div class="row pl-3">
<div class="col-md-6 col-sm-12">
<button type="button" class="btn btn-primary" onclick="document.getElementById('id_vehicle').value='Virgil'"><span class="fas fa-truck-moving"></span> Virgil</button>
<button type="button" class="btn btn-secondary" onclick="document.getElementById('id_vehicle').value='Virgil + Erms'"><span class="fas fa-trailer"></span><span class="fas fa-truck-moving"></span> Virgil + Erms</button>
</div>
<div class="col-md-6 mt-2">
{% render_field form.vehicle class+="form-control" placeholder="Other (enter text)" %}
</div>
</div>
</div>
{% if edit or manual %}
<div class="form-group">
<label for="{{ form.end_time.id_for_label }}"
class="col-sm-4 col-form-label">End Time</label>
<div class="col-sm-8">
{% render_field form.end_time class+="form-control" %}
</div>
</div>
{% endif %}
{% if not request.is_ajax %}
<div class="row mt-3">
<div class="col-sm-12 text-right">
{% button 'submit' %}
</div>
</div>
{% endif %}
</form>
</div>
{% endblock %}
{% block footer %}
<div class="col-sm-12 text-right pr-0">
<button type="submit" class="btn btn-primary" title="Save" form="checkin"
><span class="fas fa-save align-middle"></span> <span class="d-none d-sm-inline align-middle">Save</span></button>
</div>
{% endblock %}

View File

@@ -12,6 +12,7 @@
<th scope="col">Dates</th> <th scope="col">Dates</th>
<th scope="col">RA</th> <th scope="col">RA</th>
<th scope="col">Checklists</th> <th scope="col">Checklists</th>
<th scope="col">Power Records</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -35,6 +36,14 @@
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span <a href="{% url 'event_ec' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
class="d-none d-sm-inline">Create</span></a> class="d-none d-sm-inline">Create</span></a>
</td> </td>
<td>
{% for record in event.power_tests.all %}
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
<br/>
{% endfor %}
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
</td>
</tr> </tr>
{% empty %} {% empty %}
<tr class="bg-warning text-dark"> <tr class="bg-warning text-dark">

View File

@@ -1,59 +0,0 @@
{% extends 'base_rigs.html' %}
{% load paginator from filters %}
{% load help_text from filters %}
{% load verbose_name from filters %}
{% load get_field from filters %}
{% block title %}{{ title }} List{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h2>{{title}} List</h2>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="table-responsive">
<table class="table mb-0 table-sm">
<thead>
<tr>
<th scope="col">Event</th>
{# mmm hax #}
{% if object_list.0 != None %}
{% for field in object_list.0.fieldz %}
<th scope="col">{{ object_list.0|verbose_name:field|title }}</th>
{% endfor %}
{% endif %}
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr class="{% if object.reviewed_by %}table-success{%endif%}">
{# General #}
<th scope="row"><a href="{% url 'event_detail' object.event.pk %}">{{ object.event }}</a><br><small>{{ object.event.get_status_display }}</small></th>
{% for field in object_list.0.fieldz %}
<td>{{ object|get_field:field }}</td>
{% endfor %}
{# Buttons #}
<td>
{% include 'partials/hs_status.html' %}
</td>
</tr>
{% empty %}
<tr class="bg-warning">
<td colspan="6">Nothing found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% if is_paginated %}
<div class="row justify-content-center">
{% paginator %}
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,175 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% load help_text from filters %}
{% load profile_by_index from filters %}
{% load yesnoi from filters %}
{% load button from filters %}
{% block content %}
<div class="row">
<div class="col-12 text-right my-3">
{% button 'edit' url='pt_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.event.pk text="Event" %}
{% include 'partials/review_status.html' with perm=perms.RIGS.review_power review='pt_review' %}
</div>
</div>
<div class="card mb-3">
<div class="card-header">{% include 'partials/event_size.html' with object=object.event.riskassessment %}</div>
<div class="card-body">
<dl class="row">
<dt class="col-6">{{ object|help_text:'power_mic' }}</dt>
<dd class="col-6">
{% if object.power_mic %}
<a href="{% url 'profile_detail' object.power_mic.pk %}">{{ object.power_mic.name }}</a>
{% else %}
None
{% endif %}
</dd>
<dt class="col-6">Venue</dt>
<dd class="col-6">
{% if object.venue %}
<a href="{% url 'venue_detail' object.venue.pk %}" class="modal-href">
{{ object.venue }}
</a>
{% endif %}
</dd>
<dt class="col-6">Notes</dt>
<dd class="col-6">
{{ object.notes }}
</dd>
</dl>
{% if object.event.riskassessment.event_size == 0 %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'rcds'|safe }}</dt>
<dd class="col-2">
{{ object.rcds|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'supply_test'|safe }}</dt>
<dd class="col-2">
{{ object.supply_test|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
{% else %}
<dl class="row">
<dt class="col-10">{{ object|help_text:'source_rcd'|safe }}</dt>
<dd class="col-2">
{{ object.source_rcd|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'labelling'|safe }}</dt>
<dd class="col-2">
{{ object.labelling|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'earthing'|safe }}</dt>
<dd class="col-2">
{{ object.earthing|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'pat'|safe }}</dt>
<dd class="col-2">
{{ object.pat|yesnoi }}
</dd>
</dl>
<hr>
<p>Tests at first distro</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th>{{ object|help_text:'fd_voltage_l1' }}</th>
<th>{{ object|help_text:'fd_voltage_l2' }}</th>
<th>{{ object|help_text:'fd_voltage_l3' }}</th>
</tr>
<tr>
<td>{{ object.fd_voltage_l1 }}</td>
<td>{{ object.fd_voltage_l2 }}</td>
<td>{{ object.fd_voltage_l3 }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_phase_rotation'|safe }}</th>
<td colspan="3">{{ object.fd_phase_rotation|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_earth_fault'|safe}}</th>
<td colspan="3">{{ object.fd_earth_fault }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'fd_pssc'}}</th>
<td colspan="3">{{ object.fd_pssc }}</td>
</tr>
</tbody>
</table>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{ object|help_text:'w1_description'|safe}}</th>
<td>{{ object.w1_description }}</td>
<td>{{ object.w2_description|default:'' }}</td>
<td>{{ object.w3_description|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_polarity'|safe}}</th>
<td>{{ object.w1_polarity|yesnoi }}</td>
<td>{{ object.w2_polarity|default:''|yesnoi }}</td>
<td>{{ object.w3_polarity|default:''|yesnoi }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_voltage'|safe}}</th>
<td>{{ object.w1_voltage }}</td>
<td>{{ object.w2_voltage|default:'' }}</td>
<td>{{ object.w3_voltage|default:'' }}</td>
</tr>
<tr>
<th scope="row">{{ object|help_text:'w1_earth_fault'|safe}}</th>
<td>{{ object.w1_earth_fault }}</td>
<td>{{ object.w2_earth_fault|default:'' }}</td>
<td>{{ object.w3_earth_fault|default:'' }}</td>
</tr>
</tbody>
</table>
<hr>
<dl class="row">
<dt class="col-10">{{ object|help_text:'all_rcds_tested'|safe }}</dt>
<dd class="col-2">
{{ object.all_rcds_tested|yesnoi }}
</dd>
<dt class="col-10">{{ object|help_text:'public_sockets_tested'|safe }}</dt>
<dd class="col-2">
{{ object.public_sockets_tested|yesnoi }}
</dd>
</dl>
<hr>
{% include 'partials/ec_power_info.html' %}
{% endif %}
</div>
</div>
<div class="col-12 text-right">
{% button 'edit' url='ec_edit' pk=object.pk %}
{% button 'view' url='event_detail' pk=object.pk text="Event" %}
{% include 'partials/review_status.html' with perm=perms.RIGS.review_eventchecklist review='ec_review' %}
</div>
<div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="eventchecklist_history" %}
</div>
{% endblock %}

View File

@@ -0,0 +1,203 @@
{% extends request.is_ajax|yesno:'base_ajax.html,base_rigs.html' %}
{% load widget_tweaks %}
{% load static %}
{% load help_text from filters %}
{% load profile_by_index from filters %}
{% load button from filters %}
{% block css %}
{{ block.super }}
<link rel="stylesheet" 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 %}
<div class="col-12">
{% include 'form_errors.html' %}
{% if edit %}
<form role="form" method="POST" action="{% url 'pt_edit' pk=object.pk %}">
{% else %}
<form role="form" method="POST" action="{% url 'event_pt' pk=event.pk %}">
{% endif %}
<input type="hidden" name="{{ form.event.name }}" id="{{ form.event.id_for_label }}"
value="{{event.pk}}"/>
{% csrf_token %}
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">Event Information</div>
<div class="card-body">
<dl class="row">
<dt class="col-4">Event Date</dt>
<dd class="col-8">{{ event.start_date}}{%if event.end_date %}-{{ event.end_date}}{%endif%}</dd>
<dt class="col-4">Event Name</dt>
<dd class="col-8">{{ event.name }}</dd>
<dt class="col-4">Client</dt>
<dd class="col-8">{{ event.person }}</dd>
<dt class="col-4">Event Size</dt>
<dd class="col-8">{% include 'partials/event_size.html' with object=event.riskassessment %}</dd>
</dl>
<hr>
<div class="form-group form-row" id="{{ form.power_mic.id_for_label }}-group">
<label for="{{ form.power_mic.id_for_label }}"
class="col-4 col-form-label">{{ form.power_mic.help_text }}</label>
<select id="{{ form.power_mic.id_for_label }}" name="{{ form.power_mic.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" required="true">
{% if power_mic %}
<option value="{{power_mic.pk}}" selected="selected">{{ power_mic.name }}</option>
{% elif event.riskassessment.power_mic %}
<option value="{{event.riskassessment.power_mic.pk}}" selected="selected">{{ event.riskassessment.power_mic.name }}</option>
{% endif %}
</select>
</div>
<div class="form-group form-row" id="{{ form.venue.id_for_label }}-group">
<label for="{{ form.venue.id_for_label }}"
class="col-4 col-form-label">{{ form.venue.label }}</label>
<select id="{{ form.venue.id_for_label }}" name="{{ form.venue.name }}" class="form-control selectpicker col-8" data-live-search="true" data-sourceurl="{% url 'api_secure' model='venue' %}">
{% if venue %}
<option value="{{venue.pk}}" selected="selected">{{ venue.name }}</option>
{% elif event.venue %}
<option value="{{event.venue.pk}}" selected="selected">{{ event.venue.name }}</option>
{% endif %}
</select>
</div>
<label for="{{ form.notes.id_for_label }}">Notes</label>
{% render_field form.notes class+="form-control" %}
</div>
</div>
</div>
</div>
{% if event.riskassessment.event_size == 0 %}
<div class="row my-3" id="size-0">
<div class="col-12">
<div class="card border-success">
<div class="card-header">Electrical Checks <small>for Small TEC Events <6kVA (approx. 26A)</small></div>
<div class="card-body">
{% include 'partials/checklist_checkbox.html' with formitem=form.rcds %}
{% include 'partials/checklist_checkbox.html' with formitem=form.supply_test %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
</div>
</div>
</div>
</div>
{% else %}
<div class="row my-3" id="size-1">
<div class="col-12">
{% if event.riskassessment.event_size == 1 %}
<div class="card border-warning">
<div class="card-header">Electrical Checks <small>for Medium TEC Events </small></div>
<div class="card-body">
{% else %}
<div class="card border-danger">
<div class="card-header">Electrical Checks <small>for Large TEC Events</small></div>
<div class="card-body">
<div class="alert alert-danger"><strong>Here be dragons. Ensure you have appeased the Power Gods before continuing... (If you didn't check with a Supervisor, <em>you cannot continue your event!</em>)</strong></div>
{% endif %}
{% include 'partials/checklist_checkbox.html' with formitem=form.source_rcd %}
{% include 'partials/checklist_checkbox.html' with formitem=form.labelling %}
{% include 'partials/checklist_checkbox.html' with formitem=form.earthing %}
{% include 'partials/checklist_checkbox.html' with formitem=form.pat %}
<hr>
<p>Tests at first distro</p>
<div class="table-responsive">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" colspan="3" class="text-center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" rowspan="2">Voltage<br><small>(cube meter)</small></th>
<th class="text-center">{{ form.fd_voltage_l1.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l2.help_text }}</th>
<th class="text-center">{{ form.fd_voltage_l3.help_text }}</th>
</tr>
<tr>
<td>{% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_phase_rotation.help_text|safe}}</th>
<td colspan="3">{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_earth_fault.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_earth_fault class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.fd_pssc.help_text|safe}}</th>
<td colspan="3">{% render_field form.fd_pssc class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr>
<p>Tests at 'Worst Case' points (at least 1 point required)</p>
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr>
<th scope="col" class="text-center">Test</th>
<th scope="col" class="text-center">Point 1</th>
<th scope="col" class="text-center">Point 2</th>
<th scope="col" class="text-center">Point 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">{{form.w1_description.help_text|safe}}</th>
<td>{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %}</td>
<td>{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_polarity.help_text|safe}}</th>
<td>{% render_field form.w1_polarity %}</td>
<td>{% render_field form.w2_polarity %}</td>
<td>{% render_field form.w3_polarity %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_voltage.help_text|safe}}</th>
<td>{% render_field form.w1_voltage class+="form-control" %}</td>
<td>{% render_field form.w2_voltage class+="form-control" %}</td>
<td>{% render_field form.w3_voltage class+="form-control" %}</td>
</tr>
<tr>
<th scope="row">{{form.w1_earth_fault.help_text|safe}}</th>
<td>{% render_field form.w1_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w2_earth_fault class+="form-control" %}</td>
<td>{% render_field form.w3_earth_fault class+="form-control" %}</td>
</tr>
</tbody>
</table>
</div>
<hr/>
{% include 'partials/checklist_checkbox.html' with formitem=form.all_rcds_tested %}
{% include 'partials/checklist_checkbox.html' with formitem=form.public_sockets_tested %}
{% include 'partials/ec_power_info.html' %}
</div>
</div>
</div>
</div>
{% endif %}
<div class="row mt-3">
<div class="col-sm-12 text-right">
{% button 'submit' %}
</div>
</div>
</form>
</div>
{% endblock %}

View File

@@ -47,6 +47,6 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p><strong>Voltage Drop on Circuit:</strong> 5% (approx. 12v)</p> <p><strong>Voltage Drop on Circuit:</strong> &#8804;5% (approx. 12v)</p>
</div> </div>
</div> </div>

View File

@@ -49,5 +49,13 @@
{% endif %} {% endif %}
<a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> <span class="d-none d-sm-inline">Subhire Insurance Form</span></a> <a href="https://docs.google.com/forms/d/e/1FAIpQLSf-TBOuJZCTYc2L8DWdAaC3_Werq0ulsUs8-6G85I6pA9WVsg/viewform" class="btn btn-danger"><span class="fas fa-file-invoice-dollar"></span> <span class="d-none d-sm-inline">Subhire Insurance Form</span></a>
{% if event.can_check_in %}
{% if request.user.current_event %}
<a href="{% url 'event_checkout' %}" class="btn btn-warning">Check Out</a>
{% else %}
<a href="{% url 'event_checkin' event.pk %}" class="btn btn-success modal-href"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a>
{% endif %}
{% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@@ -15,19 +15,21 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if not event.dry_hire %} {% if not event.dry_hire %}
{% if event.riskassessment %} {% if event.has_checklist %}
<a href="{{ event.riskassessment.get_absolute_url }}"><span class="badge badge-success">RA: <span class="fas fa-check{% if event.riskassessment.reviewed_by %}-double{%endif%}"></span></a> <a href="{{ event.riskassessment.get_absolute_url }}"><span class="badge badge-success">RA: <span class="fas fa-check{% if event.riskassessment.reviewed_by %}-double{%endif%}"></span></a>
{% else %} {% else %}
<span class="badge badge-danger">RA: <span class="fas fa-times"></span></span> <span class="badge badge-danger">RA: <span class="fas fa-times"></span></span>
{% endif %} {% endif %}
{% endif %} {% if event.has_checklist %}
{% if not event.dry_hire %} <span class="badge badge-success">Checklist: <span class="fas fa-check"></span> {% if event.checklists.count > 1 %}({{event.checklists.count}}){% endif %}</span>
{% if event.hs_done %}
{# TODO Display status of all checklists #}
<span class="badge badge-success">Checklist: <span class="fas fa-check"></span></span>
{% else %} {% else %}
<span class="badge badge-danger">Checklist: <span class="fas fa-times"></span></span> <span class="badge badge-danger">Checklist: <span class="fas fa-times"></span></span>
{% endif %} {% endif %}
{% if event.has_power %}
<span class="badge badge-success">Power Record: <span class="fas fa-check"></span> {% if event.power_tests.count > 1 %}({{event.power_tests.count}}){% endif %}</span>
{% else %}
<span class="badge badge-danger">Power Record: <span class="fas fa-times"></span></span>
{% endif %}
{% endif %} {% endif %}
{% if perms.RIGS.view_invoice %} {% if perms.RIGS.view_invoice %}
{% if event.invoice %} {% if event.invoice %}

View File

@@ -15,5 +15,13 @@
{% endfor %} {% endfor %}
<a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span <a href="{% url 'event_ec' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a> class="hidden-xs">Create</span></a>
<hr>
<h5>Power Test Records:</h5>
{% for record in event.power_tests.all %}
{% include 'partials/hs_status.html' with event=event object=record view='pt_detail' edit='pt_edit' create='event_pt' review='pt_review' perm=perms.RIGS.review_power %}
<br/>
{% endfor %}
<a href="{% url 'event_pt' event.pk %}" class="btn btn-info mt-2"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create</span></a>
</div> </div>
</div> </div>

View File

@@ -43,15 +43,22 @@ def venue(db):
@pytest.fixture # TODO parameterise with Event sizes @pytest.fixture # TODO parameterise with Event sizes
def checklist(basic_event, venue, admin_user, ra): def checklist(basic_event, venue, admin_user, ra):
checklist = models.EventChecklist.objects.create(event=basic_event, power_mic=admin_user, safe_parking=False, checklist = models.EventChecklist.objects.create(event=basic_event, safe_parking=False,
safe_packing=False, exits=False, trip_hazard=False, warning_signs=False, safe_packing=False, exits=False, trip_hazard=False, warning_signs=False,
ear_plugs=False, hs_location="Locked away safely", ear_plugs=False, hs_location="Locked away safely",
extinguishers_location="Somewhere, I forgot", earthing=False, pat=False, extinguishers_location="Somewhere, I forgot",
date=timezone.now(), venue=venue) date=timezone.now(), venue=venue)
yield checklist yield checklist
checklist.delete() checklist.delete()
@pytest.fixture
def power_test(basic_event, venue, admin_user, ra):
power_test = models.PowerTestRecord.objects.create(event=basic_event, venue=venue)
yield power_test
power_test.delete()
@pytest.fixture @pytest.fixture
def many_events(db, admin_user, scope="class"): def many_events(db, admin_user, scope="class"):
many_events = { many_events = {

View File

@@ -230,11 +230,6 @@ class CreateEventChecklist(FormPage):
URL_TEMPLATE = 'event/{event_id}/checklist' URL_TEMPLATE = 'event/{event_id}/checklist'
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]") _submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
_add_vehicle_locator = (By.XPATH, "//button[contains(., 'Vehicle')]")
_add_crew_locator = (By.XPATH, "//button[contains(., 'Crew')]")
_vehicle_row_locator = ('xpath', "//tr[@id[starts-with(., 'vehicle') and not(contains(.,'new'))]]")
_crew_row_locator = ('xpath', "//tr[@id[starts-with(., 'crew') and not(contains(.,'new'))]]")
form_items = { form_items = {
'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')), 'safe_parking': (regions.CheckBox, (By.ID, 'id_safe_parking')),
@@ -245,6 +240,20 @@ class CreateEventChecklist(FormPage):
'ear_plugs': (regions.CheckBox, (By.ID, 'id_ear_plugs')), 'ear_plugs': (regions.CheckBox, (By.ID, 'id_ear_plugs')),
'hs_location': (regions.TextBox, (By.ID, 'id_hs_location')), 'hs_location': (regions.TextBox, (By.ID, 'id_hs_location')),
'extinguishers_location': (regions.TextBox, (By.ID, 'id_extinguishers_location')), 'extinguishers_location': (regions.TextBox, (By.ID, 'id_extinguishers_location')),
}
@property
def success(self):
return '{event_id}' not in self.driver.current_url
class CreatePowerTestRecord(FormPage):
URL_TEMPLATE = 'event/{event_id}/power'
_submit_locator = (By.XPATH, "//button[@type='submit' and contains(., 'Save')]")
_power_mic_selector = (By.XPATH, "//div[select[@id='id_power_mic']]")
form_items = {
'rcds': (regions.CheckBox, (By.ID, 'id_rcds')), 'rcds': (regions.CheckBox, (By.ID, 'id_rcds')),
'supply_test': (regions.CheckBox, (By.ID, 'id_supply_test')), 'supply_test': (regions.CheckBox, (By.ID, 'id_supply_test')),
'earthing': (regions.CheckBox, (By.ID, 'id_earthing')), 'earthing': (regions.CheckBox, (By.ID, 'id_earthing')),
@@ -263,58 +272,10 @@ class CreateEventChecklist(FormPage):
'w1_earth_fault': (regions.TextBox, (By.ID, 'id_w1_earth_fault')), 'w1_earth_fault': (regions.TextBox, (By.ID, 'id_w1_earth_fault')),
} }
def add_vehicle(self):
self.find_element(*self._add_vehicle_locator).click()
def add_crew(self):
self.find_element(*self._add_crew_locator).click()
@property @property
def power_mic(self): def power_mic(self):
return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector)) return regions.BootstrapSelectElement(self, self.find_element(*self._power_mic_selector))
@property
def vehicles(self):
return [self.VehicleRow(self, el) for el in self.find_elements(*self._vehicle_row_locator)]
class VehicleRow(Region):
_name_locator = ('xpath', ".//input")
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
@property
def name(self):
return regions.TextBox(self, self.root.find_element(*self._name_locator))
@property
def vehicle(self):
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
@property
def crew(self):
return [self.CrewRow(self, el) for el in self.find_elements(*self._crew_row_locator)]
class CrewRow(Region):
_select_locator = ('xpath', ".//div[contains(@class,'bootstrap-select')]/..")
_start_time_locator = ('xpath', ".//input[@name[starts-with(., 'start') and not(contains(.,'new'))]]")
_end_time_locator = ('xpath', ".//input[@name[starts-with(., 'end') and not(contains(.,'new'))]]")
_role_locator = ('xpath', ".//input[@name[starts-with(., 'role') and not(contains(.,'new'))]]")
@property
def crewmember(self):
return regions.BootstrapSelectElement(self, self.root.find_element(*self._select_locator))
@property
def start_time(self):
return regions.DateTimePicker(self, self.root.find_element(*self._start_time_locator))
@property
def end_time(self):
return regions.DateTimePicker(self, self.root.find_element(*self._end_time_locator))
@property
def role(self):
return regions.TextBox(self, self.root.find_element(*self._role_locator))
@property @property
def success(self): def success(self):
return '{event_id}' not in self.driver.current_url return '{event_id}' not in self.driver.current_url

View File

@@ -676,14 +676,6 @@ def small_ec(page, admin_user):
page.ear_plugs = True page.ear_plugs = True
page.hs_location = "The Moon" page.hs_location = "The Moon"
page.extinguishers_location = "With the rest of the fire" page.extinguishers_location = "With the rest of the fire"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
page.earthing = True
page.rcds = True
page.supply_test = True
page.pat = True
def test_ec_create_small(logged_in_browser, live_server, admin_user, ra): def test_ec_create_small(logged_in_browser, live_server, admin_user, ra):
@@ -704,14 +696,15 @@ def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra)
page.ear_plugs = True page.ear_plugs = True
page.hs_location = "Death Valley" page.hs_location = "Death Valley"
page.extinguishers_location = "With the rest of the fire" page.extinguishers_location = "With the rest of the fire"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
# Gotta scroll to make the button clickable # Gotta scroll to make the button clickable
logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") logged_in_browser.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
page.submit()
assert page.success
def test_power_checklist(logged_in_browser, live_server, admin_user, power_test, medium_ra):
page = pages.CreatePowerTestRecord(logged_in_browser.driver, live_server.url, event_id=medium_ra.event.pk).open()
page.earthing = True page.earthing = True
page.pat = True page.pat = True
page.source_rcd = True page.source_rcd = True
@@ -726,56 +719,15 @@ def test_ec_create_medium(logged_in_browser, live_server, admin_user, medium_ra)
page.w1_polarity = True page.w1_polarity = True
page.w1_voltage = 240 page.w1_voltage = 240
page.w1_earth_fault = "0.42" page.w1_earth_fault = "0.42"
# If we do this first the search fails, for ... reasons
page.power_mic.search(admin_user.name)
page.power_mic.toggle()
assert not page.power_mic.is_open
page.submit() page.submit()
assert page.success assert page.success
def test_ec_create_vehicle(logged_in_browser, live_server, admin_user, checklist):
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
small_ec(page, admin_user)
page.add_vehicle()
assert len(page.vehicles) == 1
vehicle_name = 'Brian'
page.vehicles[0].name.set_value(vehicle_name)
# Appears we're moving too fast for javascript...
t.sleep(1)
page.vehicles[0].vehicle.search(admin_user.first_name)
t.sleep(1)
page.submit()
assert page.success
# Check data is correct
checklist.refresh_from_db()
vehicle = models.EventChecklistVehicle.objects.get(checklist=checklist.pk)
assert vehicle_name == vehicle.vehicle
# TODO Test validation of end before start
def test_ec_create_crew(logged_in_browser, live_server, admin_user, checklist):
page = pages.EditEventChecklist(logged_in_browser.driver, live_server.url, pk=checklist.pk).open()
small_ec(page, admin_user)
page.add_crew()
assert len(page.crew) == 1
role = "MIC"
start_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 9, 0))
end_time = timezone.make_aware(datetime.datetime(2015, 1, 1, 10, 30))
crew = page.crew[0]
t.sleep(2)
crew.crewmember.search(admin_user.first_name)
t.sleep(2)
crew.role.set_value(role)
crew.start_time.set_value(start_time)
crew.end_time.set_value(end_time)
page.submit()
assert page.success
# Check data is correct
crew_obj = models.EventChecklistCrew.objects.get(checklist=checklist.pk)
assert admin_user.pk == crew_obj.crewmember.pk
assert role == crew_obj.role
assert start_time == crew_obj.start
assert end_time == crew_obj.end
# TODO Can I loop through all the boolean fields and test them at once? # TODO Can I loop through all the boolean fields and test them at once?
def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event): def test_ra_creation(logged_in_browser, live_server, admin_user, basic_event):
page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open() page = pages.CreateRiskAssessment(logged_in_browser.driver, live_server.url, event_id=basic_event.pk).open()

View File

@@ -79,10 +79,8 @@ urlpatterns = [
name='ra_detail'), name='ra_detail'),
path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()), path('event/ra/<int:pk>/edit/', permission_required_with_403('RIGS.change_riskassessment')(views.EventRiskAssessmentEdit.as_view()),
name='ra_edit'), name='ra_edit'),
path('event/ra/list', permission_required_with_403('RIGS.view_riskassessment')(views.EventRiskAssessmentList.as_view()), path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.MarkReviewed.as_view()),
name='ra_list'), name='ra_review', kwargs={'model': 'RiskAssessment'}),
path('event/ra/<int:pk>/review/', permission_required_with_403('RIGS.review_riskassessment')(views.EventRiskAssessmentReview.as_view()),
name='ra_review'),
path('event/ra/<int:pk>/print/', permission_required_with_403('RIGS.view_riskassessment')(views.RAPrint.as_view()), name='ra_print'), path('event/ra/<int:pk>/print/', permission_required_with_403('RIGS.view_riskassessment')(views.RAPrint.as_view()), name='ra_print'),
path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()), path('event/<int:pk>/checklist/', permission_required_with_403('RIGS.add_eventchecklist')(views.EventChecklistCreate.as_view()),
@@ -91,10 +89,26 @@ urlpatterns = [
name='ec_detail'), name='ec_detail'),
path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()), path('event/checklist/<int:pk>/edit/', permission_required_with_403('RIGS.change_eventchecklist')(views.EventChecklistEdit.as_view()),
name='ec_edit'), name='ec_edit'),
path('event/checklist/list', permission_required_with_403('RIGS.view_eventchecklist')(views.EventChecklistList.as_view()), path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.MarkReviewed.as_view()),
name='ec_list'), name='ec_review', kwargs={'model': 'EventChecklist'}),
path('event/checklist/<int:pk>/review/', permission_required_with_403('RIGS.review_eventchecklist')(views.EventChecklistReview.as_view()),
name='ec_review'), path('event/<int:pk>/power/', permission_required_with_403('RIGS.add_powertestrecord')(views.PowerTestCreate.as_view()),
name='event_pt'),
path('event/power/<int:pk>/', login_required(views.PowerTestDetail.as_view()),
name='pt_detail'),
path('event/power/<int:pk>/edit/', permission_required_with_403('RIGS.change_powertestrecord')(views.PowerTestEdit.as_view()),
name='pt_edit'),
path('event/power/<int:pk>/review/', permission_required_with_403('RIGS.review_power')(views.MarkReviewed.as_view()),
name='pt_review', kwargs={'model': 'PowerTestRecord'}),
path('event/<int:pk>/checkin/', login_required(views.EventCheckIn.as_view()),
name='event_checkin'),
path('event/checkout/', login_required(views.EventCheckOut.as_view()),
name='event_checkout'),
path('event/<int:pk>/checkin/edit/', login_required(views.EventCheckInEdit.as_view()),
name='edit_checkin'),
path('event/<int:pk>/checkin/add/', login_required(views.EventCheckInOverride.as_view()),
name='event_checkin_override'),
# Finance # Finance
path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()), path('invoice/', permission_required_with_403('RIGS.view_invoice')(views.InvoiceIndex.as_view()),

View File

@@ -1,16 +1,46 @@
from django.apps import apps
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse_lazy from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.views import generic from django.views import generic
from reversion import revisions as reversion from reversion import revisions as reversion
from RIGS import models, forms from RIGS import models, forms
from RIGS.views.rigboard import get_related from RIGS.views.rigboard import get_related
from PyRIGS.views import PrintView from PyRIGS.views import PrintView, ModalURLMixin
from django.shortcuts import redirect
class EventRiskAssessmentCreate(generic.CreateView): class HSCreateView(generic.CreateView):
def get_form(self, **kwargs):
form = super().get_form(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event
return form
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create {self.model.__name__} for Event {event.display_id}'
return context
class MarkReviewed(generic.View):
def get(self, *args, **kwargs):
obj = apps.get_model('RIGS', kwargs.get('model')).objects.get(pk=kwargs.get('pk'))
with reversion.create_revision():
reversion.set_user(self.request.user)
obj.reviewed_by = self.request.user
obj.reviewed_at = timezone.now()
obj.save()
return HttpResponseRedirect(reverse('hs_list'))
class EventRiskAssessmentCreate(HSCreateView):
model = models.RiskAssessment model = models.RiskAssessment
template_name = 'hs/risk_assessment_form.html' template_name = 'hs/risk_assessment_form.html'
form_class = forms.EventRiskAssessmentForm form_class = forms.EventRiskAssessmentForm
@@ -23,28 +53,12 @@ class EventRiskAssessmentCreate(generic.CreateView):
ra = models.RiskAssessment.objects.filter(event=event).first() ra = models.RiskAssessment.objects.filter(event=event).first()
if ra is not None: if ra is not None:
return HttpResponseRedirect(reverse_lazy('ra_edit', kwargs={'pk': ra.pk})) return HttpResponseRedirect(reverse('ra_edit', kwargs={'pk': ra.pk}))
return super(EventRiskAssessmentCreate, self).get(self) return super(EventRiskAssessmentCreate, self).get(self)
def get_form(self, **kwargs):
form = super(EventRiskAssessmentCreate, self).get_form(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event
return form
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentCreate, self).get_context_data(**kwargs)
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create Risk Assessment for Event {event.display_id}'
get_related(context['form'], context)
return context
def get_success_url(self): def get_success_url(self):
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk}) return reverse('ra_detail', kwargs={'pk': self.object.pk})
class EventRiskAssessmentEdit(generic.UpdateView): class EventRiskAssessmentEdit(generic.UpdateView):
@@ -57,7 +71,7 @@ class EventRiskAssessmentEdit(generic.UpdateView):
ra.reviewed_by = None ra.reviewed_by = None
ra.reviewed_at = None ra.reviewed_at = None
ra.save() ra.save()
return reverse_lazy('ra_detail', kwargs={'pk': self.object.pk}) return reverse('ra_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentEdit, self).get_context_data(**kwargs) context = super(EventRiskAssessmentEdit, self).get_context_data(**kwargs)
@@ -80,36 +94,6 @@ class EventRiskAssessmentDetail(generic.DetailView):
return context return context
class EventRiskAssessmentList(generic.ListView):
paginate_by = 20
model = models.RiskAssessment
template_name = 'hs/hs_object_list.html'
def get_queryset(self):
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
def get_context_data(self, **kwargs):
context = super(EventRiskAssessmentList, self).get_context_data(**kwargs)
context['title'] = 'Risk Assessment'
context['view'] = 'ra_detail'
context['edit'] = 'ra_edit'
context['review'] = 'ra_review'
context['perm'] = 'perms.RIGS.review_riskassessment'
return context
class EventRiskAssessmentReview(generic.View):
def get(self, *args, **kwargs):
rpk = kwargs.get('pk')
ra = models.RiskAssessment.objects.get(pk=rpk)
with reversion.create_revision():
reversion.set_user(self.request.user)
ra.reviewed_by = self.request.user
ra.reviewed_at = timezone.now()
ra.save()
return HttpResponseRedirect(reverse_lazy('ra_list'))
class EventChecklistDetail(generic.DetailView): class EventChecklistDetail(generic.DetailView):
model = models.EventChecklist model = models.EventChecklist
template_name = 'hs/event_checklist_detail.html' template_name = 'hs/event_checklist_detail.html'
@@ -130,7 +114,7 @@ class EventChecklistEdit(generic.UpdateView):
ec.reviewed_by = None ec.reviewed_by = None
ec.reviewed_at = None ec.reviewed_at = None
ec.save() ec.save()
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk}) return reverse('ec_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(EventChecklistEdit, self).get_context_data(**kwargs) context = super(EventChecklistEdit, self).get_context_data(**kwargs)
@@ -143,7 +127,7 @@ class EventChecklistEdit(generic.UpdateView):
return context return context
class EventChecklistCreate(generic.CreateView): class EventChecklistCreate(HSCreateView):
model = models.EventChecklist model = models.EventChecklist
template_name = 'hs/event_checklist_form.html' template_name = 'hs/event_checklist_form.html'
form_class = forms.EventChecklistForm form_class = forms.EventChecklistForm
@@ -158,57 +142,67 @@ class EventChecklistCreate(generic.CreateView):
if ra is None: if ra is None:
messages.error(self.request, f'A Risk Assessment must exist prior to creating any Event Checklists for {event}! Please create one now.') messages.error(self.request, f'A Risk Assessment must exist prior to creating any Event Checklists for {event}! Please create one now.')
return HttpResponseRedirect(reverse_lazy('event_ra', kwargs={'pk': epk})) return HttpResponseRedirect(reverse('event_ra', kwargs={'pk': epk}))
return super(EventChecklistCreate, self).get(self) return super(EventChecklistCreate, self).get(self)
def get_form(self, **kwargs): def get_success_url(self):
form = super(EventChecklistCreate, self).get_form(**kwargs) return reverse('ec_detail', kwargs={'pk': self.object.pk})
epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk)
form.instance.event = event class PowerTestDetail(generic.DetailView):
return form model = models.PowerTestRecord
template_name = 'hs/power_detail.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(EventChecklistCreate, self).get_context_data(**kwargs) context = super().get_context_data(**kwargs)
epk = self.kwargs.get('pk') context['page_title'] = f"Power Test Record for Event <a href='{self.object.event.get_absolute_url()}'>{self.object.event.display_id} {self.object.event.name}</a>"
event = models.Event.objects.get(pk=epk)
context['event'] = event
context['page_title'] = f'Create Event Checklist for Event {event.display_id}'
return context return context
class PowerTestEdit(generic.UpdateView):
model = models.PowerTestRecord
template_name = 'hs/power_form.html'
form_class = forms.PowerTestRecordForm
def get_success_url(self): def get_success_url(self):
return reverse_lazy('ec_detail', kwargs={'pk': self.object.pk}) ec = self.get_object()
ec.reviewed_by = None
ec.reviewed_at = None
class EventChecklistList(generic.ListView): ec.save()
paginate_by = 20 return reverse('ec_detail', kwargs={'pk': self.object.pk})
model = models.EventChecklist
template_name = 'hs/hs_object_list.html'
def get_queryset(self):
return self.model.objects.exclude(event__status=models.Event.CANCELLED).order_by('reviewed_at').select_related('event')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(EventChecklistList, self).get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['title'] = 'Event Checklist' pk = self.kwargs.get('pk')
context['view'] = 'ec_detail' ec = models.PowerTestRecord.objects.get(pk=pk)
context['edit'] = 'ec_edit' context['event'] = ec.event
context['review'] = 'ec_review' context['edit'] = True
context['perm'] = 'perms.RIGS.review_eventchecklist' context['page_title'] = f'Edit Power Test Record for Event {ec.event.display_id}'
# get_related(context['form'], context)
return context return context
class EventChecklistReview(generic.View): class PowerTestCreate(HSCreateView):
model = models.PowerTestRecord
template_name = 'hs/power_form.html'
form_class = forms.PowerTestRecordForm
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
rpk = kwargs.get('pk') epk = kwargs.get('pk')
ec = models.EventChecklist.objects.get(pk=rpk) event = models.Event.objects.get(pk=epk)
with reversion.create_revision():
reversion.set_user(self.request.user) # Check if RA exists
ec.reviewed_by = self.request.user ra = models.RiskAssessment.objects.filter(event=event).first()
ec.reviewed_at = timezone.now()
ec.save() if ra is None:
return HttpResponseRedirect(reverse_lazy('ec_list')) messages.error(self.request, f'A Risk Assessment must exist prior to creating any Power Test Records for {event}! Please create one now.')
return HttpResponseRedirect(reverse('event_ra', kwargs={'pk': epk}))
return super().get(self)
def get_success_url(self):
return reverse('pt_detail', kwargs={'pk': self.object.pk})
class HSList(generic.ListView): class HSList(generic.ListView):
@@ -233,3 +227,64 @@ class RAPrint(PrintView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['filename'] = f"EventSpecificRiskAssessment_for_{context['object'].event.display_id}.pdf" context['filename'] = f"EventSpecificRiskAssessment_for_{context['object'].event.display_id}.pdf"
return context return context
class EventCheckIn(generic.CreateView, ModalURLMixin):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EventCheckInForm
def get_success_url(self):
return self.get_close_url('event_detail', 'event_detail') # Well, that's one way of doing that...!
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = models.Event.objects.get(pk=self.kwargs.get('pk'))
context['page_title'] = f'Check In to Event {context["event"].display_id}'
# get_related(context['form'], context)
return context
class EventCheckInOverride(generic.CreateView):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EditCheckInForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = models.Event.objects.get(pk=self.kwargs.get('pk'))
context['page_title'] = f'Manually add Check In to Event {context["event"].display_id}'
context['manual'] = True
return context
class EventCheckInEdit(generic.UpdateView, ModalURLMixin):
model = models.EventCheckIn
template_name = 'hs/eventcheckin_form.html'
form_class = forms.EditCheckInForm
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if not obj.person == self.request.user and not obj.event.mic == self.request.user:
return redirect(self.request.META.get('HTTP_REFERER', '/'))
return super().dispatch(request)
def get_success_url(self):
return self.get_close_url('event_detail', 'event_detail') # Well, that's one way of doing that...!
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['event'] = self.object.event
context['page_title'] = f'Edit Check In for Event {context["event"].display_id}'
context['edit'] = True
# get_related(context['form'], context)
return context
class EventCheckOut(generic.RedirectView):
def get_redirect_url(self, *args, **kwargs):
checkin = self.request.user.current_event()
if checkin:
checkin.end_time = timezone.now()
checkin.save()
return self.request.META.get('HTTP_REFERER', '/')

View File

@@ -73,7 +73,7 @@ function initPicker(obj) {
return array; return array;
} }
}; };
console.log(obj.data); //console.log(obj.data);
if (!obj.data('noclear')) { if (!obj.data('noclear')) {
obj.prepend($("<option></option>") obj.prepend($("<option></option>")
.attr("value",'') .attr("value",'')

View File

@@ -30,6 +30,9 @@
<body> <body>
<a class="skip-link" href='#main'>Skip to content</a> <a class="skip-link" href='#main'>Skip to content</a>
{% block navbar %} {% block navbar %}
{% if request.user.current_event %}
<div class="bg-primary d-flex justify-content-between align-items-center"><span class="ml-2">You are currently checked in to <a href="{{request.user.current_event.event.get_absolute_url}}" class="text-white">{{request.user.current_event.event}}</a></span><a href="{% url 'event_checkout'%}" class="btn btn-warning">Check Out</a></div>
{% endif %}
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" role="navigation"> <nav class="navbar navbar-expand-lg navbar-dark bg-dark" role="navigation">
<div class="container"> <div class="container">
<a class="navbar-brand" style="position: absolute; left:0.5em; top: 2px;" href="{% if request.user.is_authenticated %}https://rigs.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}"> <a class="navbar-brand" style="position: absolute; left:0.5em; top: 2px;" href="{% if request.user.is_authenticated %}https://rigs.nottinghamtec.co.uk{%else%}https://nottinghamtec.co.uk{%endif%}">

View File

@@ -8,6 +8,13 @@
<div class="row"> <div class="row">
<h1 class="col-sm-12 pb-3">R<small class="text-muted">ig</small> I<small class="text-muted">nformation</small> G<small class="text-muted">athering</small> S<small class="text-muted">ystem</small></h1> <h1 class="col-sm-12 pb-3">R<small class="text-muted">ig</small> I<small class="text-muted">nformation</small> G<small class="text-muted">athering</small> S<small class="text-muted">ystem</small></h1>
<h2 class="col-sm-12 pb-3">Welcome back {{ user.get_full_name }}, there {%if rig_count == 1 %}is one rig coming up{%else%}are {{ rig_count|apnumber }} rigs coming up.{%endif%}</h2> <h2 class="col-sm-12 pb-3">Welcome back {{ user.get_full_name }}, there {%if rig_count == 1 %}is one rig coming up{%else%}are {{ rig_count|apnumber }} rigs coming up.{%endif%}</h2>
{% if now %}
<div class="col-sm-12 alert alert-primary rounded-0 mx-auto">
{% for event in now %}
Event {{ event }} is happening now! <a href="{% url 'event_checkin' event.pk %}" class="btn btn-success btn-sm modal-href align-baseline {% if request.user.current_event %}disabled{%endif%}"><span class="fas fa-user-clock"></span> <span class="d-none d-sm-inline">Check In</span></a><br/>
{% endfor %}
</div>
{% endif %}
<div class="col-sm-4 mb-3"> <div class="col-sm-4 mb-3">
<div class="card"> <div class="card">
<img class="card-img-top d-none d-sm-block" src="{% static 'imgs/rigs.jpg' %}" alt="Some lights and haze, very purple" style="height: 150px; object-fit: cover;"> <img class="card-img-top d-none d-sm-block" src="{% static 'imgs/rigs.jpg' %}" alt="Some lights and haze, very purple" style="height: 150px; object-fit: cover;">
@@ -24,7 +31,6 @@
</div> </div>
<div class="col-sm-4 mb-3"> <div class="col-sm-4 mb-3">
<div class="card"> <div class="card">
{% now "m-d" as todays_date %}
<img class="card-img-top d-none d-sm-block" src="{% if todays_date == '04-01' %}{% static 'imgs/tappytaptap.gif' %}{%else%}{% static 'imgs/assets.jpg' %}{%endif%}" alt="M32 sound desk close up of the faders" style="height: 150px; object-fit: cover;"> <img class="card-img-top d-none d-sm-block" src="{% if todays_date == '04-01' %}{% static 'imgs/tappytaptap.gif' %}{%else%}{% static 'imgs/assets.jpg' %}{%endif%}" alt="M32 sound desk close up of the faders" style="height: 150px; object-fit: cover;">
<h4 class="card-header">Asset Database</h4> <h4 class="card-header">Asset Database</h4>
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">

View File

@@ -50,10 +50,7 @@ class Command(BaseCommand):
"add_supplier", "view_cabletype", "change_cabletype", "add_supplier", "view_cabletype", "change_cabletype",
"add_cabletype", "view_eventchecklist", "change_eventchecklist", "add_cabletype", "view_eventchecklist", "change_eventchecklist",
"add_eventchecklist", "view_riskassessment", "change_riskassessment", "add_eventchecklist", "view_riskassessment", "change_riskassessment",
"add_riskassessment", "add_eventchecklistcrew", "change_eventchecklistcrew", "add_riskassessment"]
"delete_eventchecklistcrew", "view_eventchecklistcrew", "add_eventchecklistvehicle",
"change_eventchecklistvehicle",
"delete_eventchecklistvehicle", "view_eventchecklistvehicle", ]
finance_perms = keyholder_perms + ["add_invoice", "change_invoice", "view_invoice", finance_perms = keyholder_perms + ["add_invoice", "change_invoice", "view_invoice",
"add_payment", "change_payment", "delete_payment"] "add_payment", "change_payment", "delete_payment"]
hs_perms = keyholder_perms + ["review_riskassessment", "review_eventchecklist"] hs_perms = keyholder_perms + ["review_riskassessment", "review_eventchecklist"]