mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
Validation of matching fields is working
This commit is contained in:
parent
b4342d6203
commit
9773fee50b
@ -43,27 +43,42 @@ class FileManager:
|
|||||||
# Update headers
|
# Update headers
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
def process(self, file):
|
@classmethod
|
||||||
""" Process file """
|
def validate(cls, file):
|
||||||
|
""" Validate file extension and data """
|
||||||
|
|
||||||
self.data = None
|
cleaned_data = None
|
||||||
|
|
||||||
ext = os.path.splitext(file.name)[-1].lower()
|
ext = os.path.splitext(file.name)[-1].lower()
|
||||||
|
|
||||||
if ext in ['.csv', '.tsv', ]:
|
if ext in ['.csv', '.tsv', ]:
|
||||||
# These file formats need string decoding
|
# These file formats need string decoding
|
||||||
raw_data = file.read().decode('utf-8')
|
raw_data = file.read().decode('utf-8')
|
||||||
|
# Reset stream position to beginning of file
|
||||||
|
file.seek(0)
|
||||||
elif ext in ['.xls', '.xlsx']:
|
elif ext in ['.xls', '.xlsx']:
|
||||||
raw_data = file.read()
|
raw_data = file.read()
|
||||||
|
# Reset stream position to beginning of file
|
||||||
|
file.seek(0)
|
||||||
else:
|
else:
|
||||||
raise ValidationError(_(f'Unsupported file format: {ext}'))
|
raise ValidationError(_(f'Unsupported file format: {ext}'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.data = tablib.Dataset().load(raw_data)
|
cleaned_data = tablib.Dataset().load(raw_data)
|
||||||
except tablib.UnsupportedFormat:
|
except tablib.UnsupportedFormat:
|
||||||
raise ValidationError(_(f'Error reading {self.name} file (invalid format)'))
|
raise ValidationError(_('Error reading file (invalid format)'))
|
||||||
except tablib.core.InvalidDimensions:
|
except tablib.core.InvalidDimensions:
|
||||||
raise ValidationError(_(f'Error reading {self.name} file (incorrect dimension)'))
|
raise ValidationError(_('Error reading file (incorrect dimension)'))
|
||||||
|
except KeyError:
|
||||||
|
# TODO: Find fix for XLSX format as it keeps on returning a KeyError
|
||||||
|
raise ValidationError(_('Error reading file (data could be corrupted)'))
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
def process(self, file):
|
||||||
|
""" Process file """
|
||||||
|
|
||||||
|
self.data = self.__class__.validate(file)
|
||||||
|
|
||||||
def update_headers(self):
|
def update_headers(self):
|
||||||
""" Update headers """
|
""" Update headers """
|
||||||
@ -74,7 +89,7 @@ class FileManager:
|
|||||||
""" Setup headers depending on the file name """
|
""" Setup headers depending on the file name """
|
||||||
|
|
||||||
if not self.name:
|
if not self.name:
|
||||||
return False
|
return
|
||||||
|
|
||||||
if self.name == 'order':
|
if self.name == 'order':
|
||||||
self.REQUIRED_HEADERS = [
|
self.REQUIRED_HEADERS = [
|
||||||
@ -94,8 +109,6 @@ class FileManager:
|
|||||||
# Update headers
|
# Update headers
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def guess_header(self, header, threshold=80):
|
def guess_header(self, header, threshold=80):
|
||||||
""" Try to match a header (from the file) to a list of known headers
|
""" Try to match a header (from the file) to a list of known headers
|
||||||
|
|
||||||
|
@ -298,11 +298,11 @@ class UploadFile(forms.Form):
|
|||||||
def clean_file(self):
|
def clean_file(self):
|
||||||
file = self.cleaned_data['file']
|
file = self.cleaned_data['file']
|
||||||
|
|
||||||
# Create a FileManager object - will perform initial data validation
|
# Validate file using FileManager class - will perform initial data validation
|
||||||
# (and raise a ValidationError if there is something wrong with the file)
|
# (and raise a ValidationError if there is something wrong with the file)
|
||||||
file_manager = FileManager(file=file, name='order')
|
FileManager.validate(file)
|
||||||
|
|
||||||
return file_manager
|
return file
|
||||||
|
|
||||||
|
|
||||||
class MatchField(forms.Form):
|
class MatchField(forms.Form):
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
{% endblock form_buttons_top %}
|
{% endblock form_buttons_top %}
|
||||||
|
|
||||||
{% block form_content %}
|
{% block form_content %}
|
||||||
{{ wizard.form }}
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -29,6 +29,7 @@ from part.models import Part
|
|||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
from common.views import MultiStepFormView
|
from common.views import MultiStepFormView
|
||||||
|
from common.files import FileManager
|
||||||
|
|
||||||
from . import forms as order_forms
|
from . import forms as order_forms
|
||||||
|
|
||||||
@ -590,6 +591,7 @@ class PurchaseOrderUpload(MultiStepFormView):
|
|||||||
headers = None
|
headers = None
|
||||||
rows = None
|
rows = None
|
||||||
columns = None
|
columns = None
|
||||||
|
missing_columns = None
|
||||||
|
|
||||||
def get_context_data(self, form, **kwargs):
|
def get_context_data(self, form, **kwargs):
|
||||||
context = super().get_context_data(form=form, **kwargs)
|
context = super().get_context_data(form=form, **kwargs)
|
||||||
@ -603,12 +605,13 @@ class PurchaseOrderUpload(MultiStepFormView):
|
|||||||
# print(f'{self.headers}')
|
# print(f'{self.headers}')
|
||||||
if self.columns:
|
if self.columns:
|
||||||
context.update({'columns': self.columns})
|
context.update({'columns': self.columns})
|
||||||
# print(f'{self.columns}')
|
print(f'{self.columns}')
|
||||||
if self.rows:
|
if self.rows:
|
||||||
context.update({'rows': self.rows})
|
context.update({'rows': self.rows})
|
||||||
# print(f'{self.rows}')
|
# print(f'{self.rows}')
|
||||||
|
if self.missing_columns:
|
||||||
|
context.update({'missing_columns': self.missing_columns})
|
||||||
|
|
||||||
print(f'{context=}')
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def getTableDataFromForm(self, form_data):
|
def getTableDataFromForm(self, form_data):
|
||||||
@ -623,10 +626,6 @@ class PurchaseOrderUpload(MultiStepFormView):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.file_manager:
|
|
||||||
print('Lost file manager...')
|
|
||||||
return
|
|
||||||
|
|
||||||
# Map the columns
|
# Map the columns
|
||||||
self.column_names = {}
|
self.column_names = {}
|
||||||
self.column_selections = {}
|
self.column_selections = {}
|
||||||
@ -746,68 +745,116 @@ class PurchaseOrderUpload(MultiStepFormView):
|
|||||||
for col in self.file_manager.PART_MATCH_HEADERS:
|
for col in self.file_manager.PART_MATCH_HEADERS:
|
||||||
self.missing_columns.append(col)
|
self.missing_columns.append(col)
|
||||||
|
|
||||||
def handleFileUpload(self, form):
|
def getFileManager(self, form=None):
|
||||||
""" Process file upload and setup fields form """
|
""" Create FileManager instance from upload file """
|
||||||
|
|
||||||
|
if self.file_manager:
|
||||||
|
return
|
||||||
|
|
||||||
# Check if user completed file upload
|
|
||||||
if self.steps.current == 'upload':
|
if self.steps.current == 'upload':
|
||||||
# Copy FileManager instance from form
|
# Get file from form data
|
||||||
self.file_manager = form.cleaned_data['file']
|
order_file = form.cleaned_data['file']
|
||||||
print(f'{self.file_manager=}')
|
self.file_manager = FileManager(file=order_file, name='order')
|
||||||
# Setup FileManager for order upload
|
else:
|
||||||
setup_valid = self.file_manager.setup()
|
# Retrieve stored files from upload step
|
||||||
if setup_valid:
|
upload_files = self.storage.get_step_files('upload')
|
||||||
# Set headers
|
# Get file
|
||||||
self.headers = self.file_manager.HEADERS
|
order_file = upload_files.get('upload-file', None)
|
||||||
# Set columns and rows
|
if order_file:
|
||||||
self.columns = self.file_manager.columns()
|
self.file_manager = FileManager(file=order_file, name='order')
|
||||||
self.rows = self.file_manager.rows()
|
|
||||||
|
|
||||||
# Save FileManager
|
def setupFieldSelection(self, form):
|
||||||
# self.storage.set_step_data('file', self.file_manager)
|
""" Setup fields form """
|
||||||
|
|
||||||
|
# Get FileManager
|
||||||
|
self.getFileManager(form)
|
||||||
|
# Setup headers
|
||||||
|
self.file_manager.setup()
|
||||||
|
# Set headers
|
||||||
|
self.headers = self.file_manager.HEADERS
|
||||||
|
# Set columns and rows
|
||||||
|
self.columns = self.file_manager.columns()
|
||||||
|
self.rows = self.file_manager.rows()
|
||||||
|
|
||||||
def handleFieldSelection(self, form_data):
|
def handleFieldSelection(self, form):
|
||||||
""" Process field matching """
|
""" Process field matching """
|
||||||
|
|
||||||
|
# Update headers
|
||||||
|
if self.file_manager:
|
||||||
|
self.file_manager.setup()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
# Extract form data
|
# Extract form data
|
||||||
self.getTableDataFromForm(form_data)
|
self.getTableDataFromForm(form.data)
|
||||||
|
|
||||||
valid = len(self.missing_columns) == 0 and not self.duplicates
|
valid = len(self.missing_columns) == 0 and not self.duplicates
|
||||||
|
|
||||||
if not valid:
|
return valid
|
||||||
raise ValidationError('Invalid data')
|
|
||||||
|
|
||||||
def handlePartSelection(self, form_data):
|
def handlePartSelection(self, form):
|
||||||
pass
|
pass
|
||||||
# print(f'{form_data=}')
|
|
||||||
|
|
||||||
# def process_step(self, form):
|
|
||||||
# print(f'{self.steps.current=} | {form.data}')
|
|
||||||
# return self.get_form_step_data(form)
|
|
||||||
|
|
||||||
def get_form_step_data(self, form):
|
def get_form_step_data(self, form):
|
||||||
print(f'{self.steps.current=}\n{form.data=}')
|
""" Process form data after it has been posted """
|
||||||
print(f'{self.file_manager=}')
|
|
||||||
|
# print(f'{self.steps.current=}\n{form.data=}')
|
||||||
|
|
||||||
|
# Retrieve FileManager instance from uploaded file
|
||||||
|
self.getFileManager(form)
|
||||||
|
# print(f'{self.file_manager=}')
|
||||||
|
|
||||||
# Process steps
|
# Process steps
|
||||||
if self.steps.current == 'upload':
|
if self.steps.current == 'upload':
|
||||||
self.handleFileUpload(form)
|
self.setupFieldSelection(form)
|
||||||
if self.steps.current == 'fields':
|
# elif self.steps.current == 'fields':
|
||||||
self.handleFieldSelection(form.data)
|
# self.handleFieldSelection(form)
|
||||||
elif self.steps.current == 'parts':
|
# elif self.steps.current == 'parts':
|
||||||
self.handlePartSelection(form.data)
|
# self.handlePartSelection(form)
|
||||||
|
|
||||||
return form.data
|
return form.data
|
||||||
|
|
||||||
# def get_all_cleaned_data(self):
|
|
||||||
# cleaned_data = super().get_all_cleaned_data()
|
|
||||||
# print(f'{self.steps.current=} | {cleaned_data}')
|
|
||||||
# return cleaned_data
|
|
||||||
|
|
||||||
# def post(self, request, *args, **kwargs):
|
def validate(self, step, form):
|
||||||
# """ Perform the various 'POST' requests required.
|
""" Validate forms """
|
||||||
# """
|
|
||||||
# print('Posting!')
|
valid = False
|
||||||
# return super().post(*args, **kwargs)
|
|
||||||
|
# Process steps
|
||||||
|
if step == 'upload':
|
||||||
|
# Validation is done during POST
|
||||||
|
valid = True
|
||||||
|
elif step == 'fields':
|
||||||
|
# Retrieve FileManager instance from uploaded file
|
||||||
|
self.getFileManager(form)
|
||||||
|
# Validate user form data
|
||||||
|
valid = self.handleFieldSelection(form)
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
form.add_error(None, 'Fields matching failed')
|
||||||
|
# Set headers
|
||||||
|
self.headers = self.file_manager.HEADERS
|
||||||
|
|
||||||
|
elif step == 'parts':
|
||||||
|
valid = self.handlePartSelection(form)
|
||||||
|
|
||||||
|
return valid
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
""" Perform validations before posting data """
|
||||||
|
|
||||||
|
wizard_goto_step = self.request.POST.get('wizard_goto_step', None)
|
||||||
|
|
||||||
|
form = self.get_form(data=self.request.POST, files=self.request.FILES)
|
||||||
|
|
||||||
|
print(f'\nCurrent step = {self.steps.current}')
|
||||||
|
form_valid = self.validate(self.steps.current, form)
|
||||||
|
|
||||||
|
if not form_valid and not wizard_goto_step:
|
||||||
|
# Re-render same step
|
||||||
|
return self.render(form)
|
||||||
|
|
||||||
|
print('\nPosting... ')
|
||||||
|
return super().post(*args, **kwargs)
|
||||||
|
|
||||||
def done(self, form_list, **kwargs):
|
def done(self, form_list, **kwargs):
|
||||||
return HttpResponseRedirect(reverse('po-detail', kwargs={'pk': self.kwargs['pk']}))
|
return HttpResponseRedirect(reverse('po-detail', kwargs={'pk': self.kwargs['pk']}))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user