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)
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.
Args:
match: Term to match against
include_description: Also search the part description (default = False)
threshold: Match percentage that must be exceeded (default = 65)
reverse: Ordering for search results (default = True - highest match is first)
compare_length: Include string length checks
Returns:
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
"""
match = str(match).strip().lower()
if len(match) == 0:
return []
parts = Part.objects.all()
matches = []
for part in parts:
compare = part.name
if include_description:
compare += part.description
compare = str(part.name).strip().lower()
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:
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 BomItem
from .models import SupplierPart
from .models import match_part_names
from . import forms as part_forms
@ -135,7 +136,7 @@ class PartCreate(AjaxCreateView):
form_class = part_forms.EditPartForm
ajax_form_title = 'Create new part'
ajax_template_name = 'modal_form.html'
ajax_template_name = 'part/create_part.html'
def get_data(self):
return {
@ -174,6 +175,28 @@ class PartCreate(AjaxCreateView):
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):
""" Get initial data for the new Part object:

View File

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