2
0
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:
eeintech 2021-05-05 12:40:48 -04:00
parent b4342d6203
commit 9773fee50b
4 changed files with 121 additions and 62 deletions

View File

@ -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

View File

@ -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):

View File

@ -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>

View File

@ -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']}))