start component parameter formset

This commit is contained in:
Mario Hüttel 2021-11-12 20:14:02 +01:00
parent c08a442ce5
commit 4fe9bb1431
6 changed files with 212 additions and 5 deletions

View File

@ -1,6 +1,7 @@
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from parts import models as parts_models from parts import models as parts_models
from shimatta_modules.EngineeringNumberConverter import EngineeringNumberConverter
class MyTestForm(forms.Form): class MyTestForm(forms.Form):
pass pass
@ -200,3 +201,63 @@ class EditComponentForm(forms.Form):
self.instance.package = self.cleaned_data['package_object'] self.instance.package = self.cleaned_data['package_object']
self.instance.component_type = self.cleaned_data['component_type_object'] self.instance.component_type = self.cleaned_data['component_type_object']
self.instance.save() self.instance.save()
class EditComponentParameterForm(forms.Form):
parameter_type = forms.CharField() # This must come first. Do not change the order of these elements!
value = forms.CharField()
def __init__(self, *args, **kwargs):
init_values = kwargs.get('initial', None)
if init_values is not None:
if isinstance(init_values['parameter_type'], parts_models.ComponentParameterType):
type_instance = init_values['parameter_type']
self.parameter_type_object = type_instance
kwargs['initial']['parameter_type'] = init_values['parameter_type'].parameter_name
if isinstance(init_values['value'], int) or isinstance(init_values['value'], float):
if type_instance.engineering_unit:
(num, prefix) = EngineeringNumberConverter.number_to_engineering(init_values['value'], False)
kwargs['initial']['value'] = f'{num} {prefix}'
elif type_instance.it_unit:
(num, prefix) = EngineeringNumberConverter.number_to_engineering(init_values['value'], True)
kwargs['initial']['value'] = f'{num} {prefix}'
super().__init__(*args, **kwargs)
def clean_parameter_type(self):
data = self.cleaned_data['parameter_type']
param_instance = None
try:
param_instance = parts_models.ComponentParameterType.objects.get(parameter_name=data)
except:
raise ValidationError(f'Component Parameter Type {data} is not defined')
self.cleaned_data['parameter_type_object'] = param_instance
return data
def clean_value(self):
parameter_type = self.cleaned_data.get('parameter_type_object', None)
value_data = self.cleaned_data['value']
if parameter_type is None:
raise ValidationError('Cannot convert value for unknown parameter type')
processed_value = None
if parameter_type.it_unit or parameter_type.engineering_unit:
try:
processed_value = EngineeringNumberConverter.engineering_to_number(value_data)
except:
raise ValidationError(f'Cannot not convert Value "{value_data}" to a number')
elif parameter_type.freetext_parameter:
processed_value = value_data
else:
try:
processed_value = float(value_data)
except:
raise ValidationError(f'"{value_data}" is not a valid number')
self.cleaned_data['processed_value'] = processed_value
return value_data

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2 on 2021-11-12 18:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parts', '0002_alter_componenttype_possible_parameter'),
]
operations = [
migrations.AlterField(
model_name='componentparametertype',
name='unit',
field=models.CharField(blank=True, max_length=10, null=True),
),
]

View File

@ -19,13 +19,17 @@ class ComponentParameterType(models.Model):
parameter_name = models.CharField(max_length=50, unique=True) parameter_name = models.CharField(max_length=50, unique=True)
parameter_description = models.TextField(null=True, blank=True) parameter_description = models.TextField(null=True, blank=True)
unit = models.CharField(max_length=10) unit = models.CharField(max_length=10, null=True, blank=True)
freetext_parameter = models.BooleanField() freetext_parameter = models.BooleanField()
engineering_unit = models.BooleanField() engineering_unit = models.BooleanField()
it_unit = models.BooleanField() it_unit = models.BooleanField()
def __str__(self): def __str__(self):
return self.parameter_name + ' in ' + self.unit unit = self.unit
if unit:
return self.parameter_name + ' in ' + unit
else:
return self.parameter_name
class ComponentType(models.Model): class ComponentType(models.Model):
class Meta: class Meta:

View File

@ -11,12 +11,12 @@ from django.views import View
import django.forms as forms import django.forms as forms
from django.views.generic import TemplateView, DetailView from django.views.generic import TemplateView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from .models import Storage, Stock, Component, Distributor, Manufacturer, Package from .models import Storage, Stock, Component, Distributor, Manufacturer, Package, ComponentParameter, ComponentParameterType
from .qr_parser import QrCodeValidator from .qr_parser import QrCodeValidator
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import IntegrityError from django.db import IntegrityError
from .forms import MyTestForm, AddSubStorageForm, DeleteStockForm, EditWatermarkForm, EditStockAmountForm, AddStockForm, EditComponentForm from .forms import MyTestForm, AddSubStorageForm, DeleteStockForm, EditWatermarkForm, EditStockAmountForm, AddStockForm, EditComponentForm, EditComponentParameterForm
from django.db.models import Q from django.db.models import Q
from django.db.models.functions import Lower from django.db.models.functions import Lower
import uuid import uuid
@ -265,7 +265,6 @@ class StockViewDetail(LoginRequiredMixin, BaseTemplateMixin, DetailView):
componente_stock_page = self.request.GET.get('stock_page', default=1) componente_stock_page = self.request.GET.get('stock_page', default=1)
stock_paginator = Paginator(self.search_stock_queryset(stock_search_input), self.default_pagination_size) stock_paginator = Paginator(self.search_stock_queryset(stock_search_input), self.default_pagination_size)
context['storages'] = storage_paginator.get_page(storage_page) context['storages'] = storage_paginator.get_page(storage_page)
@ -391,12 +390,28 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
pk_url_kwarg = 'uuid' pk_url_kwarg = 'uuid'
base_title = '' base_title = ''
navbar_selected = 'Components' navbar_selected = 'Components'
def preparae_initial_param_formset_data(self):
parameters = ComponentParameter.objects.filter(component=self.object)
initdata = []
for param in parameters:
param_type = param.parameter_type
if param_type.freetext_parameter:
value = param.text_value
else:
value = param.value
initdata.append({'parameter_type': param_type, 'value': value})
return initdata
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.base_title = 'Component / '+self.object.name self.base_title = 'Component / '+self.object.name
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['component'] = self.object context['component'] = self.object
context['edit_form'] = EditComponentForm(instance=self.object) context['edit_form'] = EditComponentForm(instance=self.object)
ParameterFormset = forms.formset_factory(EditComponentParameterForm, extra=0, max_num=100)
context['param_formset'] = ParameterFormset(
initial=self.preparae_initial_param_formset_data())
return context return context
def handle_submit_edit_post(self, request, **kwargs): def handle_submit_edit_post(self, request, **kwargs):
@ -420,10 +435,23 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context['edit_form'] = form context['edit_form'] = form
return self.render_to_response(context) return self.render_to_response(context)
def handle_submit_edit_params_post(self, request, **kwargs):
f = EditComponentParameterForm(data=request.POST)
if f.is_valid():
print('Valid')
else:
print('Invalid')
context = self.get_context_data()
return self.render_to_response(context)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
if 'submit-edit-comp' in request.POST: if 'submit-edit-comp' in request.POST:
return self.handle_submit_edit_post(request, **kwargs) return self.handle_submit_edit_post(request, **kwargs)
elif 'submit-edit-params' in request.POST:
return self.handle_submit_edit_params_post(request, **kwargs)
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)

View File

@ -0,0 +1,67 @@
class EngineeringNumberConverter():
prefixes = [
('y', 1e-24),
('z', 1e-21),
('a', 1e-18),
('f', 1e-15),
('p', 1e-12),
('n', 1e-9),
('u', 1e-6),
('m', 1e-3),
# We skip centi and dezi because no one really uses these besides for length measurements
('', 1),
# We also skip h for hekto
('k', 1e3),
('M', 1e6),
('G', 1e9),
('T', 1e12),
('P', 1e15),
('E', 1e18),
('Z', 1e21),
('Y', 1e24),
]
it_prefixes = [
('', 1),
('Ki', 1024),
('Mi', 1024*1024),
('Gi', 1024*1024*1024),
('Ti', 1024*1024*1024*1024)
]
@classmethod
def number_to_engineering(c, number, it_unit = False):
"""
Convert a number to engineering SI syntax with prefix.
This function will return a tuple of (new_number, prefix)
"""
if it_unit:
used_prefixes = c.it_prefixes
else:
used_prefixes = c.prefixes
if (len(used_prefixes) < 2):
return (number / used_prefixes[0][1], used_prefixes[0])
for i, (prefix, scale) in enumerate(used_prefixes[1:], 1):
if number < scale:
return (number / used_prefixes[i-1][1], used_prefixes[i-1][0])
return (number / used_prefixes[-1][1], used_prefixes[-1][0])
@classmethod
def engineering_to_number(c, input):
cleaned_input = input.strip().replace(' ', '')
selected_scaling = 1
for (prefix, scale) in c.prefixes+c.it_prefixes:
if prefix == '':
continue
if cleaned_input.endswith(prefix):
cleaned_input = cleaned_input.replace(prefix, '')
selected_scaling = scale
break
return float(cleaned_input) * selected_scaling

View File

@ -1,6 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load static %} {% load static %}
{% load qr_code %} {% load qr_code %}
{% load crispy_forms_tags %}
{% block content %} {% block content %}
<div class="container"> <div class="container">
@ -72,6 +73,34 @@
No description available No description available
</div> </div>
{% endif %} {% endif %}
<div class="row">
<div class="col">
<h2>Parameters</h2>
<form method="post">
{% csrf_token %}
<table class="table">
<thead>
<th scope="col">Parameter</th>
<th scope="col">Value</th>
<th scope="col">Unit</th>
</thead>
<tbody>
{% for f in param_formset %}
<tr>
<td><input type="text" class="form-control" name="{{f.parameter_type.name}}" value="{{f.parameter_type.value}}"></td>
<td><input type="text" class="form-control" name="{{f.value.name}}" value="{{f.value.value}}"></td>
<td>{{f.parameter_type_object.unit}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-primary" name="submit-edit-params" value="Save Parameters">
</form>
</div>
<div class="col">
<h2>Stocks</h2>
</div>
</div>
</div> </div>
</div> </div>