Update PartCreate form

- Display list of close matches
- Invalidate form (for now)
This commit is contained in:
Oliver Walters 2019-05-11 11:55:17 +10:00
parent 4e4ee2742b
commit d9c0d2f5e3
4 changed files with 65 additions and 7 deletions

View File

@ -116,14 +116,14 @@ def rename_part_image(instance, filename):
return os.path.join(base, fn) return os.path.join(base, fn)
def match_part_names(match, include_description=False, threshold=65, reverse=True): def match_part_names(match, threshold=80, reverse=True, compare_length=False):
""" Return a list of parts whose name matches the search term using fuzzy search. """ Return a list of parts whose name matches the search term using fuzzy search.
Args: Args:
match: Term to match against match: Term to match against
include_description: Also search the part description (default = False)
threshold: Match percentage that must be exceeded (default = 65) threshold: Match percentage that must be exceeded (default = 65)
reverse: Ordering for search results (default = True - highest match is first) reverse: Ordering for search results (default = True - highest match is first)
compare_length: Include string length checks
Returns: Returns:
A sorted dict where each element contains the following key:value pairs: A sorted dict where each element contains the following key:value pairs:
@ -131,16 +131,29 @@ def match_part_names(match, include_description=False, threshold=65, reverse=Tru
- 'ratio' : The matched ratio - 'ratio' : The matched ratio
""" """
match = str(match).strip().lower()
if len(match) == 0:
return []
parts = Part.objects.all() parts = Part.objects.all()
matches = [] matches = []
for part in parts: for part in parts:
compare = part.name compare = str(part.name).strip().lower()
if include_description:
compare += part.description
ratio = fuzz.partial_ratio(match, compare) if len(compare) == 0:
continue
ratio = fuzz.partial_ratio(compare, match)
if compare_length:
# Also employ primitive length comparison
l_min = min(len(match), len(compare))
l_max = max(len(match), len(compare))
ratio *= (l_min / l_max)
if ratio >= threshold: if ratio >= threshold:
matches.append({ matches.append({

View File

@ -0,0 +1,18 @@
{% extends "modal_form.html" %}
{% block pre_form_content %}
{{ block.super }}
{% if matches %}
<b>Matching Parts</b>
<ul class='list-group'>
{% for match in matches %}
<li class='list-group-item list-group-item-condensed'>
{{ match.part.name }} - <i>{{ match.part.description }}</i> ({{ match.ratio }}%)
</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

View File

@ -16,6 +16,7 @@ from company.models import Company
from .models import PartCategory, Part, PartAttachment from .models import PartCategory, Part, PartAttachment
from .models import BomItem from .models import BomItem
from .models import SupplierPart from .models import SupplierPart
from .models import match_part_names
from . import forms as part_forms from . import forms as part_forms
@ -135,7 +136,7 @@ class PartCreate(AjaxCreateView):
form_class = part_forms.EditPartForm form_class = part_forms.EditPartForm
ajax_form_title = 'Create new part' ajax_form_title = 'Create new part'
ajax_template_name = 'modal_form.html' ajax_template_name = 'part/create_part.html'
def get_data(self): def get_data(self):
return { return {
@ -174,6 +175,28 @@ class PartCreate(AjaxCreateView):
return form return form
def post(self, request, *args, **kwargs):
form = self.get_form()
name = request.POST.get('name', None)
context = {}
if name:
matches = match_part_names(name)
if len(matches) > 0:
context['matches'] = matches
form.non_field_errors = 'Check matches'
data = {
'form_valid': False
}
return self.renderJsonResponse(request, form, data, context=context)
def get_initial(self): def get_initial(self):
""" Get initial data for the new Part object: """ Get initial data for the new Part object:

View File

@ -27,6 +27,10 @@
padding: 6px 12px; padding: 6px 12px;
} }
.list-group-item-condensed {
padding: 5px 10px;
}
/* Force select2 elements in modal forms to be full width */ /* Force select2 elements in modal forms to be full width */
.select-full-width { .select-full-width {
width: 100%; width: 100%;