diff --git a/assets/admin.py b/assets/admin.py index 3e6c9d58..ca39ca3b 100644 --- a/assets/admin.py +++ b/assets/admin.py @@ -23,10 +23,15 @@ class SupplierAdmin(admin.ModelAdmin): @admin.register(assets.Asset) class AssetAdmin(admin.ModelAdmin): list_display = ['id', 'asset_id', 'description', 'category', 'status'] - list_filter = ['is_cable', 'category'] + list_filter = ['is_cable', 'category', 'status'] search_fields = ['id', 'asset_id', 'description'] +@admin.register(assets.CableType) +class CableTypeAdmin(admin.ModelAdmin): + list_display = ['id', '__str__', 'plug', 'socket', 'cores', 'circuits'] + + @admin.register(assets.Connector) class ConnectorAdmin(admin.ModelAdmin): list_display = ['id', '__str__', 'current_rating', 'voltage_rating', 'num_pins'] diff --git a/assets/forms.py b/assets/forms.py index 347c179f..7912c81f 100644 --- a/assets/forms.py +++ b/assets/forms.py @@ -1,6 +1,7 @@ from django import forms from assets import models +from django.db.models import Q class AssetForm(forms.ModelForm): @@ -41,3 +42,17 @@ class SupplierForm(forms.ModelForm): class SupplierSearchForm(forms.Form): query = forms.CharField(required=False) + + +class CableTypeForm(forms.ModelForm): + class Meta: + model = models.CableType + fields = '__all__' + + def clean(self): + form_data = self.cleaned_data + queryset = models.CableType.objects.filter(Q(plug=form_data['plug']) & Q(socket=form_data['socket']) & Q(circuits=form_data['circuits']) & Q(cores=form_data['cores'])) + # Being identical to itself shouldn't count... + if queryset.exists() and self.instance.pk != queryset[0].pk: + raise forms.ValidationError("A cable type that exactly matches this one already exists, please use that instead.", code="notunique") + return form_data diff --git a/assets/management/commands/generateSampleAssetsData.py b/assets/management/commands/generateSampleAssetsData.py index 3ed45eed..a7ddd404 100644 --- a/assets/management/commands/generateSampleAssetsData.py +++ b/assets/management/commands/generateSampleAssetsData.py @@ -92,6 +92,9 @@ class Command(BaseCommand): suppliers = models.Supplier.objects.all() connectors = models.Connector.objects.all() + for i in range(len(connectors)): + models.CableType.objects.create(plug=random.choice(connectors), socket=random.choice(connectors), circuits=random.choice(circuits), cores=random.choice(cores)) + for i in range(100): asset = models.Asset( asset_id='{}'.format(models.Asset.get_available_asset_id()), @@ -101,12 +104,9 @@ class Command(BaseCommand): date_acquired=timezone.now().date(), is_cable=True, - plug=random.choice(connectors), - socket=random.choice(connectors), + cable_type=random.choice(models.CableType.objects.all()), csa=random.choice(csas), length=random.choice(lengths), - circuits=random.choice(circuits), - cores=random.choice(circuits) ) if i % 5 == 0: diff --git a/assets/migrations/0011_auto_20200218_1617.py b/assets/migrations/0011_auto_20200218_1617.py new file mode 100644 index 00000000..2debea8c --- /dev/null +++ b/assets/migrations/0011_auto_20200218_1617.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.13 on 2020-02-18 16:17 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0010_auto_20200219_1444'), + ] + + operations = [ + migrations.CreateModel( + name='CableType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('circuits', models.IntegerField(blank=True, null=True)), + ('cores', models.IntegerField(blank=True, null=True)), + ('plug', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector')), + ('socket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector')), + ], + ), + migrations.AddField( + model_name='asset', + name='cable_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.CableType'), + ), + ] diff --git a/assets/migrations/0012_auto_20200218_1627.py b/assets/migrations/0012_auto_20200218_1627.py new file mode 100644 index 00000000..bf61b845 --- /dev/null +++ b/assets/migrations/0012_auto_20200218_1627.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.13 on 2020-02-18 16:27 + +from django.db import migrations +from django.db.models import Q + + +def move_cable_type_data(apps, schema_editor): + Asset = apps.get_model('assets', 'Asset') + CableType = apps.get_model('assets', 'CableType') + for asset in Asset.objects.filter(is_cable=True): + # Only create one type per...well...type + if(not CableType.objects.filter(Q(plug=asset.plug) & Q(socket=asset.socket))): + cabletype = CableType.objects.create(plug=asset.plug, socket=asset.socket, circuits=asset.circuits, cores=asset.cores) + asset.save() + cabletype.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0011_auto_20200218_1617'), + ] + + operations = [ + migrations.RunPython(move_cable_type_data) + ] diff --git a/assets/migrations/0013_auto_20200218_1639.py b/assets/migrations/0013_auto_20200218_1639.py new file mode 100644 index 00000000..c7cebec1 --- /dev/null +++ b/assets/migrations/0013_auto_20200218_1639.py @@ -0,0 +1,29 @@ +# Generated by Django 2.0.13 on 2020-02-18 16:39 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0012_auto_20200218_1627'), + ] + + operations = [ + migrations.RemoveField( + model_name='asset', + name='circuits', + ), + migrations.RemoveField( + model_name='asset', + name='cores', + ), + migrations.RemoveField( + model_name='asset', + name='plug', + ), + migrations.RemoveField( + model_name='asset', + name='socket', + ), + ] diff --git a/assets/migrations/0014_auto_20200218_1840.py b/assets/migrations/0014_auto_20200218_1840.py new file mode 100644 index 00000000..a5c36c40 --- /dev/null +++ b/assets/migrations/0014_auto_20200218_1840.py @@ -0,0 +1,17 @@ +# Generated by Django 2.0.13 on 2020-02-18 18:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0013_auto_20200218_1639'), + ] + + operations = [ + migrations.AlterModelOptions( + name='cabletype', + options={'ordering': ['plug', 'socket', '-circuits']}, + ), + ] diff --git a/assets/migrations/0011_auto_20200413_0106.py b/assets/migrations/0015_add_audit.py similarity index 95% rename from assets/migrations/0011_auto_20200413_0106.py rename to assets/migrations/0015_add_audit.py index 68828e19..7b786366 100644 --- a/assets/migrations/0011_auto_20200413_0106.py +++ b/assets/migrations/0015_add_audit.py @@ -9,7 +9,7 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('assets', '0010_auto_20200219_1444'), + ('assets', '0014_auto_20200218_1840'), ] operations = [ diff --git a/assets/models.py b/assets/models.py index 300e51e5..166fc549 100644 --- a/assets/models.py +++ b/assets/models.py @@ -63,6 +63,21 @@ class Connector(models.Model): return self.description +class CableType(models.Model): + class Meta: + ordering = ['plug', 'socket', '-circuits'] + + circuits = models.IntegerField(blank=True, null=True) + cores = models.IntegerField(blank=True, null=True) + plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, + related_name='plug', blank=True, null=True) + socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, + related_name='socket', blank=True, null=True) + + def __str__(self): + return "%s → %s" % (self.plug.description, self.socket.description) + + @reversion.register class Asset(models.Model, RevisionMixin): class Meta: @@ -93,16 +108,11 @@ class Asset(models.Model, RevisionMixin): # Cable assets is_cable = models.BooleanField(default=False) - plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, - related_name='plug', blank=True, null=True) - socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, - related_name='socket', blank=True, null=True) + cable_type = models.ForeignKey(to=CableType, blank=True, null=True, on_delete=models.SET_NULL) length = models.DecimalField(decimal_places=1, max_digits=10, blank=True, null=True, help_text='m') csa = models.DecimalField(decimal_places=2, max_digits=10, blank=True, null=True, help_text='mm²') - circuits = models.IntegerField(blank=True, null=True) - cores = models.IntegerField(blank=True, null=True) # Hidden asset_id components # For example, if asset_id was "C1001" then asset_id_prefix would be "C" and number "1001" @@ -132,7 +142,7 @@ class Asset(models.Model, RevisionMixin): def __str__(self): out = str(self.asset_id) + ' - ' + self.description if self.is_cable: - out += '{} - {}m - {}'.format(self.plug, self.length, self.socket) + out += '{} - {}m - {}'.format(self.cable_type.plug, self.length, self.cable_type.socket) return out def clean(self): @@ -157,14 +167,16 @@ class Asset(models.Model, RevisionMixin): errdict["length"] = ["The length of a cable must be more than 0"] if not self.csa or self.csa <= 0: errdict["csa"] = ["The CSA of a cable must be more than 0"] - if not self.circuits or self.circuits <= 0: - errdict["circuits"] = ["There must be at least one circuit in a cable"] - if not self.cores or self.cores <= 0: - errdict["cores"] = ["There must be at least one core in a cable"] - if self.socket is None: - errdict["socket"] = ["A cable must have a socket"] - if self.plug is None: - errdict["plug"] = ["A cable must have a plug"] + if not self.cable_type: + errdict["cable_type"] = ["A cable must have a type"] + # if not self.circuits or self.circuits <= 0: + # errdict["circuits"] = ["There must be at least one circuit in a cable"] + # if not self.cores or self.cores <= 0: + # errdict["cores"] = ["There must be at least one core in a cable"] + # if self.socket is None: + # errdict["socket"] = ["A cable must have a socket"] + # if self.plug is None: + # errdict["plug"] = ["A cable must have a plug"] if errdict != {}: # If there was an error when validation raise ValidationError(errdict) diff --git a/assets/templates/cable_type_form.html b/assets/templates/cable_type_form.html new file mode 100644 index 00000000..3f44ed71 --- /dev/null +++ b/assets/templates/cable_type_form.html @@ -0,0 +1,61 @@ +{% extends 'base_assets.html' %} +{% load widget_tweaks %} +{% block title %}Cable Type{% endblock %} + +{% block content %} +
| Cable Type | +Circuits | +Cores | +Quick Links | +
|---|---|---|---|
| {{ item }} | +{{ item.circuits }} | +{{ item.cores }} | ++ View + Edit + | +