Compare commits
3 Commits
35255cf4e9
...
ea623212bb
Author | SHA1 | Date |
---|---|---|
Mario Hüttel | ea623212bb | |
Mario Hüttel | 2bc0f3124c | |
Mario Hüttel | a34557499a |
|
@ -9,6 +9,7 @@ admin.site.register(parts_models.Manufacturer)
|
|||
admin.site.register(parts_models.Storage)
|
||||
admin.site.register(parts_models.Stock)
|
||||
admin.site.register(parts_models.ComponentParameter)
|
||||
admin.site.register(parts_models.PackageParameter)
|
||||
admin.site.register(parts_models.ComponentParameterType)
|
||||
admin.site.register(parts_models.ComponentType)
|
||||
admin.site.register(parts_models.Distributor)
|
||||
|
|
|
@ -244,6 +244,7 @@ class AdvancedComponentSearchForm(forms.Form):
|
|||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.disable_csrf = True
|
||||
self.helper.layout = Layout(
|
||||
Row(
|
||||
Column('name'),
|
||||
|
@ -262,17 +263,27 @@ class AdvancedComponentSearchForm(forms.Form):
|
|||
),
|
||||
)
|
||||
|
||||
|
||||
PARAMETER_COMPARISON_TYPES = (
|
||||
('eq', '=='),
|
||||
('lte', '<='),
|
||||
('gte', '>='),
|
||||
)
|
||||
|
||||
class ComponentParameterSearchForm(forms.Form):
|
||||
parameter = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='parameter_name')
|
||||
value = forms.CharField(max_length=100, required=False)
|
||||
compare_method = forms.ChoiceField(choices=PARAMETER_COMPARISON_TYPES, required=True, initial=1)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.disable_csrf = True
|
||||
self.helper.layout = Layout(
|
||||
Row(
|
||||
Column('parameter'),
|
||||
Column('compare_method'),
|
||||
Column('value')
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 3.2.5 on 2022-01-10 18:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('parts', '0010_auto_20220103_1606'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='componenttype',
|
||||
name='key_parameter1',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='type_param1', to='parts.componentparametertype'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='componenttype',
|
||||
name='key_parameter2',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='type_param2', to='parts.componentparametertype'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='componenttype',
|
||||
name='key_parameter3',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='type_param3', to='parts.componentparametertype'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PackageParameter',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||
('value', models.FloatField(default=0)),
|
||||
('text_value', models.TextField(blank=True)),
|
||||
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parts.package')),
|
||||
('parameter_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parts.componentparametertype')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['id'],
|
||||
'unique_together': {('package', 'parameter_type')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -49,7 +49,10 @@ class ComponentType(models.Model):
|
|||
class_name = models.CharField(max_length=50, unique=True)
|
||||
passive = models.BooleanField()
|
||||
possible_parameter = models.ManyToManyField(ComponentParameterType, blank=True)
|
||||
|
||||
key_parameter1 = models.ForeignKey(ComponentParameterType, on_delete=models.CASCADE, blank=True, null=True, related_name="type_param1")
|
||||
key_parameter2 = models.ForeignKey(ComponentParameterType, on_delete=models.CASCADE, blank=True, null=True, related_name="type_param2")
|
||||
key_parameter3 = models.ForeignKey(ComponentParameterType, on_delete=models.CASCADE, blank=True, null=True, related_name="type_param3")
|
||||
|
||||
def __str__(self):
|
||||
return '[' + self.class_name + ']'
|
||||
|
||||
|
@ -144,6 +147,37 @@ class Package(models.Model):
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class PackageParameter(models.Model):
|
||||
class Meta:
|
||||
unique_together = ('package', 'parameter_type')
|
||||
ordering = ['id']
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
|
||||
package = models.ForeignKey(Package, on_delete=models.CASCADE) # A target package is required!
|
||||
parameter_type = models.ForeignKey(ComponentParameterType, on_delete=models.CASCADE)
|
||||
value = models.FloatField(default=0)
|
||||
text_value = models.TextField(null=False, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
if self.parameter_type.parameter_type == 'F':
|
||||
value = self.text_value
|
||||
else:
|
||||
value = str(self.value)
|
||||
|
||||
return str(self.package)+ ': '+ str(self.parameter_type) + ': ' + value
|
||||
|
||||
def resolved_value_as_string(self):
|
||||
my_type = self.parameter_type.parameter_type
|
||||
|
||||
if my_type == 'E' or my_type == 'I':
|
||||
# Engineering float number
|
||||
(num, prefix) = NumConv.number_to_engineering(self.value, it_unit=(True if my_type=='I' else False))
|
||||
return f'{num:.3f} {prefix}{self.parameter_type.unit}'
|
||||
elif my_type == 'N':
|
||||
# Standard float number
|
||||
return f'{self.value:.3f} {self.parameter_type.unit}'
|
||||
elif my_type == 'F':
|
||||
return self.text_value
|
||||
|
||||
|
||||
class Manufacturer(models.Model):
|
||||
class Meta:
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.views import View
|
|||
import django.forms as forms
|
||||
from django.views.generic import TemplateView, DetailView
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||
from .models import Storage, Stock, Component, Distributor, Manufacturer, Package, ComponentParameter, ComponentParameterType, DistributorNum
|
||||
from .models import Storage, Stock, Component, Distributor, Manufacturer, Package, ComponentParameter, ComponentParameterType, DistributorNum, PackageParameter
|
||||
from .qr_parser import QrCodeValidator
|
||||
from django.core.paginator import Paginator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -23,7 +23,7 @@ from django.db.models.functions import Lower
|
|||
from django.forms import formset_factory
|
||||
import uuid
|
||||
|
||||
ParameterSearchFormSet = formset_factory(ComponentParameterSearchForm, extra=2)
|
||||
ParameterSearchFormSet = formset_factory(ComponentParameterSearchForm, extra=0)
|
||||
|
||||
class QrSearchForm(forms.Form):
|
||||
my_qr_validator = QrCodeValidator()
|
||||
|
@ -149,9 +149,9 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
default_page_size = 25
|
||||
|
||||
def get_component_query_set(self, search_string):
|
||||
queryset = Component.objects.all()
|
||||
queryset = Component.objects.select_related('package', 'manufacturer').all()
|
||||
|
||||
if not search_string:
|
||||
if search_string is None or search_string == '':
|
||||
return queryset
|
||||
|
||||
search_fragments = search_string.strip().split()
|
||||
|
@ -161,7 +161,7 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
return queryset
|
||||
|
||||
def get_component_queryset_from_advanced_search(self, cleaned_data):
|
||||
queryset = Component.objects.all()
|
||||
queryset = Component.objects.select_related('manufacturer', 'package').all()
|
||||
|
||||
if cleaned_data['name']:
|
||||
queryset = queryset.filter(Q(name__icontains=cleaned_data['name']))
|
||||
|
@ -181,15 +181,19 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
queryset = queryset.filter(manufacturer=cleaned_data['manufacturer'])
|
||||
return queryset
|
||||
|
||||
def filter_queryset_with_parameters(self, queryset, parameter, value):
|
||||
def filter_queryset_with_parameters(self, queryset, parameter, value, compare_method):
|
||||
if parameter and (value is None or value == ''):
|
||||
return queryset.filter(Q(componentparameter__parameter_type=parameter))
|
||||
elif parameter and value is not None:
|
||||
if parameter.parameter_type == 'F':
|
||||
return queryset.filter(Q(componentparameter__text_value__icontains=value))
|
||||
return queryset.filter(Q(componentparameter__text_value__icontains=value) & Q(componentparameter__parameter_type=parameter))
|
||||
else:
|
||||
return queryset.filter(Q(componentparameter__value=value))
|
||||
|
||||
if compare_method == 'lte': # <=
|
||||
return queryset.filter(Q(componentparameter__value__lte=value) & Q(componentparameter__parameter_type=parameter))
|
||||
elif compare_method == 'gte': # >=
|
||||
return queryset.filter(Q(componentparameter__value__gte=value) & Q(componentparameter__parameter_type=parameter))
|
||||
else:
|
||||
return queryset.filter(Q(componentparameter__value=value) & Q(componentparameter__parameter_type=parameter))
|
||||
return queryset
|
||||
|
||||
def get_context_data_int(self, advanced_search, parameter_formset : ParameterSearchFormSet, **kwargs):
|
||||
|
@ -205,15 +209,13 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
if advanced_search.is_valid():
|
||||
paginator_queryset = self.get_component_queryset_from_advanced_search(advanced_search.cleaned_data)
|
||||
else:
|
||||
paginator_queryset = Component.objects.all()
|
||||
paginator_queryset = self.get_component_query_set(None)
|
||||
|
||||
if parameter_formset.is_valid():
|
||||
# Process parameters
|
||||
for f in parameter_formset:
|
||||
# If the form is valid and has changed compared to its initial empty state
|
||||
if f.is_valid() and f.has_changed():
|
||||
print(f.cleaned_data)
|
||||
paginator_queryset = self.filter_queryset_with_parameters(paginator_queryset, f.cleaned_data['parameter'], f.cleaned_data['value'])
|
||||
# Process parameters
|
||||
for f in parameter_formset:
|
||||
# If the form is valid and has changed compared to its initial empty state
|
||||
if f.is_valid() and f.has_changed():
|
||||
paginator_queryset = self.filter_queryset_with_parameters(paginator_queryset, f.cleaned_data['parameter'], f.cleaned_data['value'], f.cleaned_data['compare_method'])
|
||||
|
||||
|
||||
else:
|
||||
|
@ -228,15 +230,24 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
|
||||
if not parameter_formset:
|
||||
context['advanced_search_param_formset'] = ParameterSearchFormSet()
|
||||
|
||||
|
||||
if not advanced_search:
|
||||
context['advanced_search_form'] = AdvancedComponentSearchForm(auto_id='adv_search_%s')
|
||||
|
||||
return context
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return self.get_context_data_int(advanced_search = None, parameter_formset=None, **kwargs)
|
||||
adv_search_form = None
|
||||
adv_param_search_formset = None
|
||||
if 'submit-advanced-search' in self.request.GET:
|
||||
adv_search_form = AdvancedComponentSearchForm(auto_id='adv_search_%s', data=self.request.GET)
|
||||
adv_param_search_formset = ParameterSearchFormSet(data=self.request.GET)
|
||||
if adv_search_form.is_valid():
|
||||
pass
|
||||
if adv_param_search_formset.is_valid():
|
||||
pass
|
||||
|
||||
|
||||
return self.get_context_data_int(advanced_search = adv_search_form, parameter_formset=adv_param_search_formset, **kwargs)
|
||||
|
||||
|
||||
def handle_new_component_post(self, request, open=False, **kwargs):
|
||||
|
@ -252,29 +263,12 @@ class ComponentView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
|
|||
if open and new_component:
|
||||
return redirect(reverse('parts-components-detail', kwargs={'uuid':new_component.id}))
|
||||
return self.render_to_response(context)
|
||||
|
||||
def handle_advanced_search_post(self, request, **kwargs):
|
||||
|
||||
form = AdvancedComponentSearchForm(auto_id='adv_search_%s', data=request.POST)
|
||||
param_formset = ParameterSearchFormSet(data=request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
print('Valid')
|
||||
|
||||
if param_formset.is_valid():
|
||||
print('Formset is valid!')
|
||||
|
||||
|
||||
context = self.get_context_data_int(form, param_formset, **kwargs)
|
||||
return self.render_to_response(context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if 'submit-edit-component' in request.POST:
|
||||
return self.handle_new_component_post(request, open=False, **kwargs)
|
||||
elif 'submit-edit-component-open' in request.POST:
|
||||
return self.handle_new_component_post(request, open=True, **kwargs)
|
||||
elif 'submit-advanced-search' in request.POST:
|
||||
return self.handle_advanced_search_post(request, **kwargs)
|
||||
else:
|
||||
return super().post(request, *args, **kwargs)
|
||||
|
||||
|
@ -642,7 +636,9 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
|
|||
context['new_param_form'] = ComponentParameterCreateForm()
|
||||
context['distri_nums'] = DistributorNum.objects.filter(component=self.object).order_by('distributor__name')
|
||||
context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by('parameter_type__parameter_name')
|
||||
|
||||
if self.object.package:
|
||||
context['package_parameters'] = PackageParameter.objects.filter(package=self.object.package).order_by('parameter_type__parameter_name')
|
||||
|
||||
return context
|
||||
|
||||
def handle_submit_edit_component_post(self, request, **kwargs):
|
||||
|
|
|
@ -119,6 +119,17 @@
|
|||
<th scope="col"></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for param in package_parameters %}
|
||||
<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>
|
||||
{% endfor %}
|
||||
{% for param in parameters %}
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -148,6 +159,13 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
{% 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 class="col">
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
</form>
|
||||
<div class="collapse mb-3{% if advanced_search_shown %} show{% endif %}" id="advanced-search-collapse" aria-expanded="{% if advanced_search_shown %}true{% else %}false{% endif %}">
|
||||
<form method="POST">
|
||||
<form method="GET">
|
||||
<div class="row">
|
||||
<div class="col-sm">
|
||||
{% crispy advanced_search_form %}
|
||||
|
|
Loading…
Reference in New Issue