diff --git a/PyRIGS/views.py b/PyRIGS/views.py
index fe7a44e0..372e1af0 100644
--- a/PyRIGS/views.py
+++ b/PyRIGS/views.py
@@ -48,6 +48,7 @@ class Index(generic.TemplateView): # Displays the current rig count along with
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['rig_count'] = models.Event.objects.rig_count()
+ context['now'] = models.Event.objects.events_in_bounds(timezone.now(), timezone.now())
return context
diff --git a/RIGS/admin.py b/RIGS/admin.py
index a46a0eee..9fff16ca 100644
--- a/RIGS/admin.py
+++ b/RIGS/admin.py
@@ -20,6 +20,7 @@ admin.site.register(models.VatRate, VersionAdmin)
admin.site.register(models.Event, VersionAdmin)
admin.site.register(models.EventItem, VersionAdmin)
admin.site.register(models.Invoice, VersionAdmin)
+admin.site.register(models.EventCheckIn)
@transaction.atomic() # Copied from django-extensions. GenericForeignKey support removed as unnecessary.
@@ -206,3 +207,8 @@ class RiskAssessmentAdmin(VersionAdmin):
@admin.register(models.EventChecklist)
class EventChecklistAdmin(VersionAdmin):
list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
+
+
+@admin.register(models.PowerTestRecord)
+class EventChecklistAdmin(VersionAdmin):
+ list_display = ('id', 'event', 'reviewed_at', 'reviewed_by')
diff --git a/RIGS/forms.py b/RIGS/forms.py
index 0664875d..c5e5de66 100644
--- a/RIGS/forms.py
+++ b/RIGS/forms.py
@@ -44,7 +44,7 @@ class EventForm(forms.ModelForm):
return simplejson.dumps(items)
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['start_date'].widget.format = '%Y-%m-%d'
@@ -200,86 +200,41 @@ class EventChecklistForm(forms.ModelForm):
related_models = {
'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:
model = models.EventChecklist
fields = '__all__'
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__'
diff --git a/RIGS/management/commands/generateSampleRIGSData.py b/RIGS/management/commands/generateSampleRIGSData.py
index df0258c6..0f89cc07 100644
--- a/RIGS/management/commands/generateSampleRIGSData.py
+++ b/RIGS/management/commands/generateSampleRIGSData.py
@@ -278,7 +278,7 @@ class Command(BaseCommand):
suspended_structures=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
- 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_packing=bool(random.getrandbits(1)),
exits=bool(random.getrandbits(1)),
@@ -287,6 +287,4 @@ class Command(BaseCommand):
ear_plugs=bool(random.getrandbits(1)),
hs_location="Locked away safely",
extinguishers_location="Somewhere, I forgot",
- earthing=bool(random.getrandbits(1)),
- pat=bool(random.getrandbits(1)),
date=timezone.now(), venue=random.choice(self.venues))
diff --git a/RIGS/migrations/0046_create_powertests.py b/RIGS/migrations/0046_create_powertests.py
new file mode 100644
index 00000000..95f1c8e4
--- /dev/null
+++ b/RIGS/migrations/0046_create_powertests.py
@@ -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?
(using socket tester)', null=True)),
+ ('earthing', models.BooleanField(blank=True, help_text='Equipment appropriately earthed?
(truss, stage, generators etc)', 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?
(if cable is more than 3m long) ', 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
(if required)', null=True, verbose_name='Phase Rotation')),
+ ('fd_earth_fault', models.DecimalField(blank=True, decimal_places=2, help_text='Earth Fault Loop Impedance (ZS)', 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 (ZS)', 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 (ZS)', 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 (ZS)', max_digits=5, null=True, verbose_name='Earth Fault Loop Impedance')),
+ ('all_rcds_tested', models.BooleanField(blank=True, help_text='All circuit RCDs tested?
(using test button)', null=True)),
+ ('public_sockets_tested', models.BooleanField(blank=True, help_text='Public/Performer accessible circuits tested?
(using socket tester)', 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),
+ ]
diff --git a/RIGS/migrations/0047_auto_20230517_0944.py b/RIGS/migrations/0047_auto_20230517_0944.py
new file mode 100644
index 00000000..ac02413d
--- /dev/null
+++ b/RIGS/migrations/0047_auto_20230517_0944.py
@@ -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),
+ ]
diff --git a/RIGS/migrations/0048_auto_20230518_1256.py b/RIGS/migrations/0048_auto_20230518_1256.py
new file mode 100644
index 00000000..ccd35a82
--- /dev/null
+++ b/RIGS/migrations/0048_auto_20230518_1256.py
@@ -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',
+ ),
+ ]
diff --git a/RIGS/models.py b/RIGS/models.py
index 30fdecd8..550b4df5 100644
--- a/RIGS/models.py
+++ b/RIGS/models.py
@@ -81,6 +81,10 @@ class Profile(AbstractUser):
def __str__(self):
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):
def search(self, query=None):
@@ -405,7 +409,15 @@ class Event(models.Model, RevisionMixin):
@property
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
def has_start_time(self):
@@ -478,6 +490,15 @@ class Event(models.Model, RevisionMixin):
else:
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()
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')
+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
-class RiskAssessment(models.Model, RevisionMixin):
+class RiskAssessment(ReviewableModel, RevisionMixin):
SMALL = (0, 'Small')
MEDIUM = (1, 'Medium')
LARGE = (2, 'Large')
@@ -738,10 +772,6 @@ class RiskAssessment(models.Model, RevisionMixin):
# 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)
expected_values = {
@@ -778,10 +808,6 @@ class RiskAssessment(models.Model, RevisionMixin):
('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
def event_size(self):
# 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):
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
def activity_feed_string(self):
return str(self.event)
@@ -803,20 +835,12 @@ class RiskAssessment(models.Model, RevisionMixin):
def name(self):
return str(self)
- def get_absolute_url(self):
- return reverse('ra_detail', kwargs={'pk': self.pk})
- def __str__(self):
- return f"{self.pk} | {self.event}"
-
-
-@reversion.register(follow=['vehicles', 'crew'])
-class EventChecklist(models.Model, RevisionMixin):
+@reversion.register
+class EventChecklist(ReviewableModel, RevisionMixin):
event = models.ForeignKey('Event', related_name='checklists', on_delete=models.CASCADE)
# 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)
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")
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
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?
(using socket tester)")
@@ -865,58 +915,40 @@ class EventChecklist(models.Model, RevisionMixin):
all_rcds_tested = models.BooleanField(blank=True, null=True, help_text="All circuit RCDs tested?
(using test button)")
public_sockets_tested = models.BooleanField(blank=True, null=True, help_text="Public/Performer accessible circuits tested?
(using socket tester)")
- 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:
ordering = ['event']
permissions = [
- ('review_eventchecklist', 'Can review Event Checklists')
+ ('review_power', 'Can review Power Test Records')
]
- @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]
+ 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})
+
+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):
- return f"{self.pk} - {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
+ return f"{self.person} on {self.event}"
def clean(self):
- if self.start > self.end:
- raise ValidationError('Unless you\'ve invented time travel, crew can\'t finish before they have started.')
+ sass = " Please invent time travel and retry."
+ 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):
- return f"{self.crewmember} ({self.role})"
+ def get_absolute_url(self):
+ return reverse('event_detail', kwargs={'pk': self.event_id})
+
+ def active(self):
+ return end_time is not None
diff --git a/RIGS/templates/base_rigs.html b/RIGS/templates/base_rigs.html
index 0ae1a62f..16d06ba9 100644
--- a/RIGS/templates/base_rigs.html
+++ b/RIGS/templates/base_rigs.html
@@ -34,16 +34,7 @@
{% if perms.RIGS.view_riskassessment %}
-
| Name | +Vehicle | +Start Time | +Role | +End Time | +{% if request.user.pk is event.mic.pk %} Add{% endif %} | +
|---|---|---|---|---|---|
| {{crew.person}} | +{{crew.vehicle|default:"None"}} | +{{crew.time}} | +{{crew.role}} | +{% if crew.end_time %}{{crew.end_time}}{% else %}{% endif %} | +{% 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%} | +
| Apparently this event happened by magic... | +|||||
List vehicles and their drivers
-| Crewmember | -Start Time | -Role | -End Time | -
|---|---|---|---|
| {{crew.crewmember}} | -{{crew.start}} | -{{crew.role}} | -{{crew.end}} | -
| Apparently this event happened by magic... | -|||
Tests at first distro
-| Test | -Value | -||
|---|---|---|---|
| Voltage (cube meter) |
- {{ object|help_text:'fd_voltage_l1' }} | -{{ object|help_text:'fd_voltage_l2' }} | -{{ object|help_text:'fd_voltage_l3' }} | -
| {{ object.fd_voltage_l1 }} | -{{ object.fd_voltage_l2 }} | -{{ object.fd_voltage_l3 }} | -|
| {{ object|help_text:'fd_phase_rotation'|safe }} | -{{ object.fd_phase_rotation|yesnoi }} | -||
| {{ object|help_text:'fd_earth_fault'|safe}} | -{{ object.fd_earth_fault }} | -||
| {{ object|help_text:'fd_pssc'}} | -{{ object.fd_pssc }} | -||
Tests at 'Worst Case' points (at least 1 point required)
-| Test | -Point 1 | -Point 2 | -Point 3 | -
|---|---|---|---|
| {{ object|help_text:'w1_description'|safe}} | -{{ object.w1_description }} | -{{ object.w2_description|default:'' }} | -{{ object.w3_description|default:'' }} | -
| {{ object|help_text:'w1_polarity'|safe}} | -{{ object.w1_polarity|yesnoi }} | -{{ object.w2_polarity|default:''|yesnoi }} | -{{ object.w3_polarity|default:''|yesnoi }} | -
| {{ object|help_text:'w1_voltage'|safe}} | -{{ object.w1_voltage }} | -{{ object.w2_voltage|default:'' }} | -{{ object.w3_voltage|default:'' }} | -
| {{ object|help_text:'w1_earth_fault'|safe}} | -{{ object.w1_earth_fault }} | -{{ object.w2_earth_fault|default:'' }} | -{{ object.w3_earth_fault|default:'' }} | -
| Person | -Start Time | -Role | -End Time | -- |
|---|---|---|---|---|
| - - | -- | - | - | - |
Tests at first distro
-| Test | -Value | -||
|---|---|---|---|
| Voltage (cube meter) |
- {{ form.fd_voltage_l1.help_text }} | -{{ form.fd_voltage_l2.help_text }} | -{{ form.fd_voltage_l3.help_text }} | -
| {% render_field form.fd_voltage_l1 class+="form-control" style="min-width: 5rem;" %} | -{% render_field form.fd_voltage_l2 class+="form-control" style="min-width: 5rem;" %} | -{% render_field form.fd_voltage_l3 class+="form-control" style="min-width: 5rem;" %} | -|
| {{form.fd_phase_rotation.help_text|safe}} | -{% include 'partials/checklist_checkbox.html' with formitem=form.fd_phase_rotation %} | -||
| {{form.fd_earth_fault.help_text|safe}} | -{% render_field form.fd_earth_fault class+="form-control" %} | -||
| {{form.fd_pssc.help_text|safe}} | -{% render_field form.fd_pssc class+="form-control" %} | -||
Tests at 'Worst Case' points (at least 1 point required)
-| Test | -Point 1 | -Point 2 | -Point 3 | -
|---|---|---|---|
| {{form.w1_description.help_text|safe}} | -{% render_field form.w1_description class+="form-control" style="min-width: 5rem;" %} | -{% render_field form.w2_description class+="form-control" style="min-width: 5rem;" %} | -{% render_field form.w3_description class+="form-control" style="min-width: 5rem;" %} | -
| {{form.w1_polarity.help_text|safe}} | -{% render_field form.w1_polarity %} | -{% render_field form.w2_polarity %} | -{% render_field form.w3_polarity %} | -
| {{form.w1_voltage.help_text|safe}} | -{% render_field form.w1_voltage class+="form-control" %} | -{% render_field form.w2_voltage class+="form-control" %} | -{% render_field form.w3_voltage class+="form-control" %} | -
| {{form.w1_earth_fault.help_text|safe}} | -{% render_field form.w1_earth_fault class+="form-control" %} | -{% render_field form.w2_earth_fault class+="form-control" %} | -{% render_field form.w3_earth_fault class+="form-control" %} | -
| Event | - {# mmm hax #} - {% if object_list.0 != None %} - {% for field in object_list.0.fieldz %} -{{ object_list.0|verbose_name:field|title }} | - {% endfor %} - {% endif %} -- | |||
|---|---|---|---|---|---|
| {{ object.event }} {{ object.event.get_status_display }} |
- {% for field in object_list.0.fieldz %}
- {{ object|get_field:field }} | - {% endfor %} - {# Buttons #} -- {% include 'partials/hs_status.html' %} - | -|||
| Nothing found | -|||||
Tests at first distro
+| Test | +Value | +||
|---|---|---|---|
| Voltage (cube meter) |
+ {{ object|help_text:'fd_voltage_l1' }} | +{{ object|help_text:'fd_voltage_l2' }} | +{{ object|help_text:'fd_voltage_l3' }} | +
| {{ object.fd_voltage_l1 }} | +{{ object.fd_voltage_l2 }} | +{{ object.fd_voltage_l3 }} | +|
| {{ object|help_text:'fd_phase_rotation'|safe }} | +{{ object.fd_phase_rotation|yesnoi }} | +||
| {{ object|help_text:'fd_earth_fault'|safe}} | +{{ object.fd_earth_fault }} | +||
| {{ object|help_text:'fd_pssc'}} | +{{ object.fd_pssc }} | +||
Tests at 'Worst Case' points (at least 1 point required)
+| Test | +Point 1 | +Point 2 | +Point 3 | +
|---|---|---|---|
| {{ object|help_text:'w1_description'|safe}} | +{{ object.w1_description }} | +{{ object.w2_description|default:'' }} | +{{ object.w3_description|default:'' }} | +
| {{ object|help_text:'w1_polarity'|safe}} | +{{ object.w1_polarity|yesnoi }} | +{{ object.w2_polarity|default:''|yesnoi }} | +{{ object.w3_polarity|default:''|yesnoi }} | +
| {{ object|help_text:'w1_voltage'|safe}} | +{{ object.w1_voltage }} | +{{ object.w2_voltage|default:'' }} | +{{ object.w3_voltage|default:'' }} | +
| {{ object|help_text:'w1_earth_fault'|safe}} | +{{ object.w1_earth_fault }} | +{{ object.w2_earth_fault|default:'' }} | +{{ object.w3_earth_fault|default:'' }} | +
Voltage Drop on Circuit: 5% (approx. 12v)
+Voltage Drop on Circuit: ≤5% (approx. 12v)