diff --git a/lelcsc/core/forms.py b/lelcsc/core/forms.py index 9036a55..d7a349e 100644 --- a/lelcsc/core/forms.py +++ b/lelcsc/core/forms.py @@ -1,6 +1,10 @@ +from operator import itemgetter + from django import forms from django.utils.translation import gettext_lazy as _ +from .models import Property + class AddStockForm(forms.Form): part_number = forms.CharField(label=_("Component")) @@ -33,3 +37,31 @@ class SearchForm(forms.Form): label=_("Results per page"), widget=forms.Select(attrs={"form": "searchForm"}), ) + + +class FilterForm(forms.Form): + def __init__(self, properties, components, data=None, *args, **kwargs): + super().__init__(data, *args, **kwargs) + + self.properties = properties + + for property in self.properties: + choices = set() + for value in property.value_set.filter(component__in=components): + choices.add((getattr(value, property.value_field), str(value))) + + # FIXME: If two or more properties share the same slug, the last one will replace all the others + self.fields[property.key] = forms.MultipleChoiceField( + choices=list(sorted(choices, key=itemgetter(0))), + label=property.name, + required=False, + widget=forms.SelectMultiple(attrs={"form": "searchForm"}), + ) + + def values(self): + for property in self.properties: + key = property.key + if key not in self.cleaned_data or not self.cleaned_data[key]: + continue + + yield property, self.cleaned_data[key] diff --git a/lelcsc/core/models/component.py b/lelcsc/core/models/component.py index e910096..3052e7f 100644 --- a/lelcsc/core/models/component.py +++ b/lelcsc/core/models/component.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ @@ -22,6 +23,20 @@ class Property(models.Model): help_text=_("Exponent of the smallest possible value") ) + @property + def key(self): + return slugify(self.name).replace("-", "_") + + @property + def value_field(self): + match self.type: + case Property.Type.QUANTITY: + return "integer" + case Property.Type.TEXT: + return "text" + case _: + return None + def __str__(self): return self.name + ( f" [{self.unit}]" if self.type == Property.Type.QUANTITY else "" @@ -40,6 +55,16 @@ class Value(models.Model): class Meta: unique_together = ("component", "property") + SI_PREFIXES = { + -12: "p", + -9: "n", + -6: "µ", + -3: "m", + 3: "k", + 6: "M", + 9: "G", + } + property = models.ForeignKey(Property, on_delete=models.CASCADE) component = models.ForeignKey(Component, on_delete=models.CASCADE) text = models.TextField() @@ -54,9 +79,27 @@ class Value(models.Model): if scale > 0: s += "0" * scale elif scale < 0: - s = str(self.integer).rjust(abs(scale) + 1, "0") + s = s.rjust(abs(scale) + 1, "0") s = s[:scale] + "." + s[scale:] + s = s.rstrip("0").removesuffix(".") - return f"{s} {self.property.unit}" + leading_zeroes = len(s.removeprefix("0.")) - len( + s.removeprefix("0.").lstrip("0") + ) + if prefix := Value.SI_PREFIXES.get(-leading_zeroes // 3 * 3, ""): + s = s.removeprefix("0.").lstrip("0") + s = ( + (s[: -leading_zeroes % 3] or "0") + + "." + + s[-leading_zeroes % 3 :] + ) + + trailing_zeroes = len(s) - len(s.rstrip("0")) + if not prefix and ( + prefix := Value.SI_PREFIXES.get(trailing_zeroes // 3 * 3, "") + ): + s = s.rstrip("0") + "0" * (trailing_zeroes % 3) + + return f"{s} {prefix}{self.property.unit}" case Property.Type.TEXT: return self.text diff --git a/lelcsc/core/static/core/search_results.js b/lelcsc/core/static/core/search_results.js index 3c17c93..0219fc1 100644 --- a/lelcsc/core/static/core/search_results.js +++ b/lelcsc/core/static/core/search_results.js @@ -1,3 +1,15 @@ document.getElementById('id_per_page').addEventListener('change', event => { event.target.form.requestSubmit(); }); + +const resetButton = document.getElementById('resetButton'); + +if (resetButton !== null) { + resetButton.addEventListener('click', event => { + document.querySelectorAll('[data-filter-form] select[multiple]').forEach(filter => { + filter.value = ''; + }); + + document.getElementById('searchForm').requestSubmit(); + }); +} diff --git a/lelcsc/core/templates/core/search_results.html b/lelcsc/core/templates/core/search_results.html index 92daaa9..11f5bd8 100644 --- a/lelcsc/core/templates/core/search_results.html +++ b/lelcsc/core/templates/core/search_results.html @@ -2,52 +2,72 @@ {% load django_bootstrap5 %} {% load i18n %} -{% load properties %} {% load static %} {% block page_title %}{% translate "Search results for" %} "{{ query }}"{% endblock %} {% block content %}
- {% translate "Part number" %}
- {% translate "Total stock" %}
- |
- {% translate "Total stock" %} | -{% for property in properties %} -{{ property.name }} | +{% if results %} +
---|
+ {% translate "Part number" %}
+ {% translate "Total stock" %}
+ |
+ {% translate "Total stock" %} | +{% for property in properties %} +{{ property.name }} | +{% endfor %} +|||
---|---|---|---|---|---|
- {{ component.part_number }}
-
|
- {{ component.total_stock }} | +||||
+ {{ component.part_number }}
+
|
+ {{ component.total_stock|default:0 }} | {% for _, value in properties %} {% if value %} -{{ value|format_value }} | +{{ value }} | {% else %} -{% translate "n/a" %} | +{% translate "n/a" %} | {% endif %} {% endfor %} -