Implement package parameters

This commit is contained in:
Mario Hüttel 2024-11-24 01:39:03 +01:00
parent 26b001d983
commit c19f4a8159
6 changed files with 150 additions and 5 deletions

View File

@ -252,15 +252,19 @@ class DistributorNumberDeleteForm(forms.Form):
class ComponentParameterDeleteForm(forms.Form): class ComponentParameterDeleteForm(forms.Form):
param_num = forms.UUIDField(required=True) param_num = forms.UUIDField(required=True)
model = parts_models.ComponentParameter
def clean_param_num(self): def clean_param_num(self):
my_uuid = self.cleaned_data['param_num'] my_uuid = self.cleaned_data['param_num']
try: try:
param = parts_models.ComponentParameter.objects.get(id=my_uuid) param = self.model.objects.get(id=my_uuid)
except: except:
raise ValidationError('Parameter Number Invalid') raise ValidationError('Parameter Number Invalid')
return param return param
class PackageParameterDeleteForm(ComponentParameterDeleteForm):
model = parts_models.PackageParameter
class AdvancedComponentSearchForm(forms.Form): class AdvancedComponentSearchForm(forms.Form):
name = forms.CharField(max_length=255, label='Component Name', required=False) name = forms.CharField(max_length=255, label='Component Name', required=False)
package = AutocompleteForeingKeyField(required=False, api_search_url='package-list', foreign_model=parts_models.Package) package = AutocompleteForeingKeyField(required=False, api_search_url='package-list', foreign_model=parts_models.Package)
@ -309,6 +313,7 @@ class ComponentParameterSearchForm(forms.Form):
class ComponentParameterCreateForm(forms.Form): class ComponentParameterCreateForm(forms.Form):
parameter_type = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='descriptive_name') parameter_type = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='descriptive_name')
value = forms.CharField(required=True, max_length=256) value = forms.CharField(required=True, max_length=256)
model = parts_models.ComponentParameter
def clean(self): def clean(self):
data = super().clean() data = super().clean()
@ -338,7 +343,20 @@ class ComponentParameterCreateForm(forms.Form):
else: else:
text_value = '' text_value = ''
value = self.cleaned_data['number_value'] value = self.cleaned_data['number_value']
parts_models.ComponentParameter.objects.create(parameter_type=param_type, component=component, value=value, text_value=text_value) self.model.objects.create(parameter_type=param_type, component=component, value=value, text_value=text_value)
class PackageParameterCreateForm(ComponentParameterCreateForm):
model = parts_models.PackageParameter
def save(self, package):
param_type = self.cleaned_data['parameter_type']
if param_type.parameter_type == 'F':
text_value = self.cleaned_data['value']
value = 0
else:
text_value = ''
value = self.cleaned_data['number_value']
self.model.objects.create(parameter_type=param_type, package=package, value=value, text_value=text_value)
class QrSearchForm(forms.Form): class QrSearchForm(forms.Form):
my_qr_validator = QrCodeValidator() my_qr_validator = QrCodeValidator()

View File

@ -0,0 +1,37 @@
from django.core.management.base import BaseCommand, CommandParser
from django.contrib.auth import get_user_model
from parts.models import Component, ComponentParameter, ComponentParameterType, PackageParameter, Package
class Command(BaseCommand):
help = "Remove component parameters, that are also set on the package with the same value"
def add_arguments(self, parser: CommandParser):
parser.add_argument('--dry-run',
help='Do not perform parameter deletion. Print only',
action='store_true')
def handle(self, *args, **options):
# Get all components with set packages. Ignore the ones without packages
all_comps = Component.objects.exclude(package__isnull=True)
for component in all_comps:
package_parameters = PackageParameter.objects.filter(package=component.package)
component_parameters = ComponentParameter.objects.filter(component=component)
package_param_ids = package_parameters.values_list('parameter_type_id', flat=True)
component_param_ids = component_parameters.values_list('parameter_type_id', flat=True)
self.stdout.write(f'Comp: {str(component)} Found {len(component_param_ids)} different parameters')
self.stdout.write(f'\tPackage: {str(component.package)} Found {len(package_param_ids)} different parameters')
commontypes = ctypes = ComponentParameterType.objects.filter(id__in=component_param_ids).filter(id__in=package_param_ids)
self.stdout.write(f'\tCommon parameter count: {len(commontypes)}')
# Check if values are the same when rendered as a string. This avoids float comparison problems
for common_type in commontypes:
s1 = package_parameters.filter(parameter_type=common_type).first().resolved_value_as_string()
comp_param = component_parameters.filter(parameter_type=common_type).first()
s2 = comp_param.resolved_value_as_string()
if s1 == s2:
self.stdout.write(f'\tParameter {common_type.parameter_name} is the same value for component and package: {s1}. Removing from component')
if not options['dry_run']:
comp_param.delete()

View File

@ -8,7 +8,7 @@ from django.db.models import Q
from django.forms import formset_factory from django.forms import formset_factory
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import ProtectedError from django.db.models import ProtectedError
from ..models import Stock, Component, ComponentParameter, DistributorNum from ..models import Stock, Component, ComponentParameter, DistributorNum, PackageParameter
from ..forms import * from ..forms import *
from .component_import import import_components_from_csv from .component_import import import_components_from_csv
from .generic_views import BaseTemplateMixin from .generic_views import BaseTemplateMixin
@ -174,6 +174,8 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
'distributor__name') 'distributor__name')
context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by( context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by(
'parameter_type__parameter_name') 'parameter_type__parameter_name')
context['package_parameters'] = PackageParameter.objects.filter(package=self.object.package).order_by(
'parameter_type__parameter_name')
return context return context

View File

@ -5,7 +5,7 @@ from django.core.paginator import Paginator
from django.db.models import ProtectedError from django.db.models import ProtectedError
from django.db.models import Q from django.db.models import Q
from ..forms import * from ..forms import *
from ..models import Package from ..models import Package, PackageParameter
from .generic_views import BaseTemplateMixin from .generic_views import BaseTemplateMixin
class PackageView(LoginRequiredMixin, BaseTemplateMixin, TemplateView): class PackageView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
@ -82,6 +82,9 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['package'] = self.object context['package'] = self.object
context['edit_form'] = PackageForm(instance=self.object) context['edit_form'] = PackageForm(instance=self.object)
context['new_param_form'] = PackageParameterCreateForm()
context['parameters'] = PackageParameter.objects.filter(package=self.object).order_by(
'parameter_type__parameter_name')
return context return context
@ -116,6 +119,27 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
if not edit_form.is_valid(): if not edit_form.is_valid():
context['edit_form'] = edit_form context['edit_form'] = edit_form
return self.render_to_response(context) return self.render_to_response(context)
def handle_submit_delete_param_post(self, request, **kwargs):
form = PackageParameterDeleteForm(data=request.POST)
if form.is_valid():
form.cleaned_data['param_num'].delete()
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def handle_submit_new_param_post(self, request, **kwargs):
form = PackageParameterCreateForm(data=request.POST)
if form.is_valid():
try:
form.save(self.object)
except IntegrityError:
form.add_error('__all__', 'This parameter is already set')
context = self.get_context_data(**kwargs)
if not form.is_valid():
context['new_param_form'] = form
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()
@ -124,5 +148,9 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
return self.handle_delete_package(request) return self.handle_delete_package(request)
elif 'submit-pkg-edit' in request.POST: elif 'submit-pkg-edit' in request.POST:
return self.edit_package(request) return self.edit_package(request)
elif 'submit-delete-param' in request.POST:
return self.handle_submit_delete_param_post(request, **kwargs)
elif 'submit-create-new-param' in request.POST:
return self.handle_submit_new_param_post(request, **kwargs)
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)

View File

@ -119,6 +119,19 @@
<th scope="col"></th> <th scope="col"></th>
</thead> </thead>
<tbody> <tbody>
{% for param in package_parameters %}
<tr>
<td>
<h6 {% if param.parameter_type.parameter_description %} class="accordion-header" data-bs-toggle="collapse" data-bs-target="#collapse-pkg-parameter-desc-{{forloop.counter}}"{% endif %}>
{{param.parameter_type.parameter_name}}
</h6>
</td>
<td>
{{param.resolved_value_as_string}}
</td>
<td><span class="text-info">from Package</span></td>
</tr>
{% endfor %}
{% for param in parameters %} {% for param in parameters %}
<tr> <tr>
<td> <td>
@ -148,6 +161,13 @@
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% for param in package_parameters %}
{% if param.parameter_type.parameter_description %}
<div class="collapse accordion-collapse" id="collapse-pkg-parameter-desc-{{forloop.counter}}" data-bs-parent="#accordion-param-desc">
{{param.parameter_type.parameter_description}}
</div>
{% endif %}
{% endfor %}
</div> </div>
</div> </div>
<div class="col"> <div class="col">

View File

@ -26,6 +26,46 @@
<input type="submit" class="btn btn-primary" value="Save" name="submit-pkg-edit"> <input type="submit" class="btn btn-primary" value="Save" name="submit-pkg-edit">
</form> </form>
</div> </div>
<div class="col-md-3">
<h3>Parameters <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#new-component-parameter-modal"><i class="bi bi-plus-circle"></i></button></h3>
<table class="table align-middle mb-3">
<thead>
<th scope="col">Parameter</th>
<th scope="col">Value</th>
<th scope="col"></th>
</thead>
<tbody>
{% for param in parameters %}
<tr>
<td>
<h6 {% if param.parameter_type.parameter_description %} class="accordion-header" data-bs-toggle="collapse" data-bs-target="#collapse-parameter-desc-{{forloop.counter}}"{% endif %}>
{{param.parameter_type.parameter_name}}
</h6>
</td>
<td>
{{param.resolved_value_as_string}}
</td>
<td>
<form method="post">
{% csrf_token %}
<input type="hidden" value="{{param.id}}" name="param_num">
<button class="btn btn-danger" name="submit-delete-param">X</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="accordion" id="accordion-param-desc">
{% for param in parameters %}
{% if param.parameter_type.parameter_description %}
<div class="collapse accordion-collapse" id="collapse-parameter-desc-{{forloop.counter}}" data-bs-parent="#accordion-param-desc">
{{param.parameter_type.parameter_description}}
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div> </div>
</div> </div>
@ -80,7 +120,7 @@
</div> </div>
</div> </div>
</div> </div>
{% include 'parts/modals/new-component-parameter-modal.html' with component_name=object.name form=new_param_form %}
{% endblock content %} {% endblock content %}