Cables are now a thing, unfortunately by making a monolithic Asset model

Co-authored-by: Arona Jones <aj@aronajones.com>
This commit is contained in:
Matthew Smith
2019-10-14 00:05:18 +01:00
parent b7e14b7dc3
commit aac900318e
9 changed files with 162 additions and 119 deletions

View File

@@ -32,11 +32,6 @@ class ConnectorAdmin(admin.ModelAdmin):
list_display = ['id', '__str__', 'current_rating', 'voltage_rating', 'num_pins'] list_display = ['id', '__str__', 'current_rating', 'voltage_rating', 'num_pins']
@admin.register(assets.Cable)
class CableAdmin(admin.ModelAdmin):
pass
admin.AdminSite.site_header = 'PyAssets - TEC\'s Asset System' admin.AdminSite.site_header = 'PyAssets - TEC\'s Asset System'
admin.AdminSite.site_title = 'PyAssets Admin' admin.AdminSite.site_title = 'PyAssets Admin'
admin.AdminSite.index_title = 'System Administration' admin.AdminSite.index_title = 'System Administration'

View File

@@ -1,74 +1,14 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
import re
from assets import models from assets import models
class AssetForm(forms.ModelForm): class AssetForm(forms.ModelForm):
class Meta: class Meta:
model = models.Asset model = models.Asset
fields = '__all__' fields = '__all__'
widgets = {
'is_cable' : forms.CheckboxInput()
}
def clean_date_sold(self):
if self.cleaned_data["date_sold"] and self.cleaned_data["date_acquired"] > self.cleaned_data["date_sold"]:
raise ValidationError("Cannot sell an item before it is acquired")
return self.cleaned_data["date_sold"]
def clean_asset_id(self):
# If the asset ID has been changed
if self.instance.asset_id and self.instance.asset_id == self.cleaned_data["asset_id"]: #If the item was not changed
return self.cleaned_data["asset_id"]
else:
if re.search("^[a-zA-Z0-9]+$", self.cleaned_data["asset_id"]) == None:
raise ValidationError("An Asset ID can only consist of letters and numbers")
return self.cleaned_data["asset_id"]
def clean_purchase_price(self):
purchase_price = self.cleaned_data["purchase_price"]
if purchase_price and purchase_price < 0:
raise ValidationError("A price cannot be negative")
return purchase_price
def clean_salvage_value(self):
salvage_value = self.cleaned_data["salvage_value"]
if salvage_value and salvage_value < 0:
raise ValidationError("A price cannot be negative")
return salvage_value
class CableForm(AssetForm):
class Meta(AssetForm.Meta):
model = models.Cable
def clean_length(self):
length = self.cleaned_data["length"]
if length <= 0:
raise ValidationError("The length of a cable must be more than 0")
return length
def clean_csa(self):
csa = self.cleaned_data["csa"]
if csa <= 0:
raise ValidationError("The CSA of a cable must be more than 0")
return csa
def clean_circuits(self):
circuits = self.cleaned_data["circuits"]
if circuits <= 0:
raise ValidationError("There must be at least one circuit")
return circuits
def clean_cores(self):
cores = self.cleaned_data["cores"]
if cores <= 0:
raise ValidationError("There must be at least one core")
return cores
class SupplierForm(forms.Form): class SupplierForm(forms.Form):
class Meta: class Meta:

View File

@@ -0,0 +1,67 @@
# Generated by Django 2.0.13 on 2019-10-13 20:23
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0009_auto_20191008_2148'),
]
operations = [
migrations.RemoveField(
model_name='cable',
name='asset_ptr',
),
migrations.RemoveField(
model_name='cable',
name='plug',
),
migrations.RemoveField(
model_name='cable',
name='socket',
),
migrations.AlterModelOptions(
name='asset',
options={},
),
migrations.RemoveField(
model_name='asset',
name='polymorphic_ctype',
),
migrations.AddField(
model_name='asset',
name='circuits',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='asset',
name='cores',
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='asset',
name='csa',
field=models.DecimalField(blank=True, decimal_places=2, help_text='mm^2', max_digits=10, null=True),
),
migrations.AddField(
model_name='asset',
name='length',
field=models.DecimalField(blank=True, decimal_places=1, help_text='m', max_digits=10, null=True),
),
migrations.AddField(
model_name='asset',
name='plug',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'),
),
migrations.AddField(
model_name='asset',
name='socket',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'),
),
migrations.DeleteModel(
name='Cable',
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 2.0.13 on 2019-10-13 21:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0010_auto_20191013_2123'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='plug',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plug', to='assets.Connector'),
),
migrations.AlterField(
model_name='asset',
name='socket',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='socket', to='assets.Connector'),
),
]

View File

@@ -1,9 +1,10 @@
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from polymorphic.models import PolymorphicModel from polymorphic.models import PolymorphicModel
import datetime import datetime
import re
class AssetCategory(models.Model): class AssetCategory(models.Model):
class Meta: class Meta:
@@ -37,7 +38,17 @@ class Supplier(models.Model):
return self.name return self.name
class Asset(PolymorphicModel): class Connector(models.Model):
description = models.CharField(max_length=80)
current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps')
voltage_rating = models.IntegerField(help_text='Volts')
num_pins = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.description
class Asset(models.Model):
parent = models.ForeignKey(to='self', related_name='asset_parent', blank=True, null=True, on_delete=models.SET_NULL) parent = models.ForeignKey(to='self', related_name='asset_parent', blank=True, null=True, on_delete=models.SET_NULL)
asset_id = models.CharField(max_length=10, unique=True) asset_id = models.CharField(max_length=10, unique=True)
@@ -55,34 +66,56 @@ class Asset(PolymorphicModel):
# Cable assets # Cable assets
is_cable = models.BooleanField(default=False) is_cable = models.BooleanField(default=False)
plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='plug', blank=True, null=True)
def get_absolute_url(self): socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='socket', blank=True, null=True)
return reverse('asset_detail', kwargs={'pk': self.pk})
def __str__(self):
return str(self.asset_id) + ' - ' + self.description
class Connector(models.Model):
description = models.CharField(max_length=80)
current_rating = models.DecimalField(decimal_places=2, max_digits=10, help_text='Amps')
voltage_rating = models.IntegerField(help_text='Volts')
num_pins = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.description
class Cable(Asset):
plug = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='plug', null=True)
socket = models.ForeignKey(Connector, on_delete=models.SET_NULL, related_name='socket', null=True)
length = models.DecimalField(decimal_places=1, max_digits=10, blank=True, null=True, help_text='m') 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^2') csa = models.DecimalField(decimal_places=2, max_digits=10, blank=True, null=True, help_text='mm^2')
circuits = models.IntegerField(blank=True, null=True) circuits = models.IntegerField(blank=True, null=True)
cores = models.IntegerField(blank=True, null=True) cores = models.IntegerField(blank=True, null=True)
def cable_resistance(self): def get_absolute_url(self):
rho = 0.0000000168 return reverse('asset_detail', kwargs={'pk': self.pk})
return (rho * self.length) / (self.csa * 1000000)
def __str__(self): def __str__(self):
return '{} - {}m - {}'.format(self.plug, self.length, self.socket) out = str(self.asset_id) + ' - ' + self.description
if self.is_cable:
out += '{} - {}m - {}'.format(self.plug, self.length, self.socket)
return out
def clean(self):
if self.date_sold and self.date_acquired > self.date_sold:
raise ValidationError({"date_sold": "Cannot sell an item before it is acquired"})
self.asset_id = self.asset_id.upper()
if re.search("^[a-zA-Z0-9]+$", self.asset_id) is None:
raise ValidationError({"asset_id": "An Asset ID can only consist of letters and numbers"})
if self.purchase_price and self.purchase_price < 0:
raise ValidationError({"purchase_price": "A price cannot be negative"})
if self.salvage_value and self.salvage_value < 0:
raise ValidationError({"purchase_price": "A price cannot be negative"})
if self.is_cable:
if self.length is None:
raise ValidationError({"length": "The length of a cable must be a number"})
elif self.csa is None:
raise ValidationError({"csa": "The csa of a cable must be a number"})
elif self.circuits is None:
raise ValidationError({"circuits": "The number of circuits in a cable must be a number"})
elif self.cores is None:
raise ValidationError({"cores": "The number of cores in a cable must be a number"})
elif self.socket is None:
raise ValidationError({"plug": "A cable must have a plug"})
elif self.plug is None:
raise ValidationError({"socket": "A cable must have a socket"})
if self.length <= 0:
raise ValidationError({"length": "The length of a cable must be more than 0"})
elif self.csa <= 0:
raise ValidationError({"csa": "The CSA of a cable must be more than 0"})
elif self.circuits <= 0:
raise ValidationError({"circuits": "There must be at least one circuit in a cable"})
elif self.cores <= 0:
raise ValidationError({"cores": "There must be at least one core in a cable"})

View File

@@ -35,7 +35,8 @@
<div class="col-md-6"> <div class="col-md-6">
{% include 'partials/purchasedetails_form.html' %} {% include 'partials/purchasedetails_form.html' %}
</div> </div>
<div class="col-md-6" hidden="true" id="cable-table"> <div class="col-md-6"
{% if not object.is_cable %} hidden="true" {% endif %} id="cable-table">
{% include 'partials/cable_form.html' %} {% include 'partials/cable_form.html' %}
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
@@ -54,6 +55,7 @@
{% endblock %} {% endblock %}
{% block js%} {% block js%}
{% if edit %}
<script> <script>
function checkIfCableHidden() { function checkIfCableHidden() {
if (document.getElementById("id_is_cable").checked) { if (document.getElementById("id_is_cable").checked) {
@@ -64,4 +66,5 @@
} }
checkIfCableHidden(); checkIfCableHidden();
</script> </script>
{%endblock%} {% endif %}
{% endblock %}

View File

@@ -1,7 +1,7 @@
{% if edit and object %} {% if edit and object %}
<!--edit--> <!--edit-->
<button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-floppy-disk"></i> Save</button> <button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-floppy-disk"></i> Save</button>
<a class="btn btn-default" href="{% url 'asset_detail' previous_asset_pk %}"><i class="glyphicon glyphicon-duplicate"></i> Duplicate</a> <a class="btn btn-default" href="{% url 'asset_duplicate' object.pk %}"><i class="glyphicon glyphicon-duplicate"></i> Duplicate</a>
<a class="btn btn-danger" data-toggle="modal" data-target="#confirm_delete_modal"><i class="glyphicon glyphicon-trash"></i> Delete</a> <a class="btn btn-danger" data-toggle="modal" data-target="#confirm_delete_modal"><i class="glyphicon glyphicon-trash"></i> Delete</a>
{% elif duplicate %} {% elif duplicate %}
<!--duplicate--> <!--duplicate-->

View File

@@ -9,7 +9,7 @@
<div class="form-group"> <div class="form-group">
<label for="{{ form.plug.id_for_label }}">Plug</label> <label for="{{ form.plug.id_for_label }}">Plug</label>
<select name="{{ form.plug.name }}" id="{{ form.plug.id_for_label }}" class="form-control"> <select name="{{ form.plug.name }}" id="{{ form.plug.id_for_label }}" class="form-control">
<option value="None"> <option value="">
{% for connector in connectors %} {% for connector in connectors %}
<option value="{{ connector.pk }}"> <option value="{{ connector.pk }}">
{{ connector.description }} {{ connector.description }}
@@ -20,7 +20,7 @@
<div class="form-group"> <div class="form-group">
<label for="{{ form.socket.id_for_label }}">Socket</label> <label for="{{ form.socket.id_for_label }}">Socket</label>
<select name="{{ form.socket.name }}" id="{{ form.socket.id_for_label }}" class="form-control"> <select name="{{ form.socket.name }}" id="{{ form.socket.id_for_label }}" class="form-control">
<option value="None"> <option value="">
{% for connector in connectors %} {% for connector in connectors %}
<option value="{{ connector.pk }}"> <option value="{{ connector.pk }}">
{{ connector.description }} {{ connector.description }}

View File

@@ -47,7 +47,7 @@ class AssetList(LoginRequiredMixin, generic.ListView):
context["statuses"] = models.AssetStatus.objects.all() context["statuses"] = models.AssetStatus.objects.all()
context["status_select"] = self.request.GET.get('status', "") context["status_select"] = self.request.GET.get('status', "")
return context; return context
class AssetSearch(AssetList): class AssetSearch(AssetList):
def render_to_response(self, context, **response_kwargs): def render_to_response(self, context, **response_kwargs):
@@ -62,30 +62,18 @@ class AssetDetail(LoginRequiredMixin, generic.DetailView):
model = models.Asset model = models.Asset
template_name = 'asset_update.html' template_name = 'asset_update.html'
# class AssetCreate(LoginRequiredMixin, generic.TemplateView):
# fields = '__all__'
# template_name = 'asset_update.html'
# # success_url = reverse_lazy('asset_list')
class AssetEdit(LoginRequiredMixin, generic.UpdateView): class AssetEdit(LoginRequiredMixin, generic.UpdateView):
template_name = 'asset_update.html' template_name = 'asset_update.html'
model = models.Asset model = models.Asset
form_class = forms.AssetForm
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['edit'] = True context['edit'] = True
context["connectors"] = models.Connector.objects.all()
return context return context
def get_form_class(self):
if self.object.is_cable:
return forms.CableForm
return forms.AssetForm
def form_invalid(self, form):
return super().form_invalid(form)
def get_success_url(self): def get_success_url(self):
return reverse("asset_detail", kwargs={"pk":self.object.id}) return reverse("asset_detail", kwargs={"pk":self.object.id})
@@ -99,16 +87,9 @@ class AssetCreate(LoginRequiredMixin, generic.CreateView):
context["create"] = True context["create"] = True
context["connectors"] = models.Connector.objects.all() context["connectors"] = models.Connector.objects.all()
return context return context
def get_form_class(self):
if self.request.method == "POST":
if 'is_cable' in self.request.POST:
return forms.CableForm
return forms.AssetForm
else:
return forms.CableForm #For GET, so that all form fields can be placed
def get_success_url(self): def get_success_url(self):
return reverse("asset_detail", kwargs={"pk":self.object.id}) return reverse("asset_detail", kwargs={"pk":self.object.id})