Coverage for src/backend/InvenTree/importer/operations.py: 16%
45 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 17:47 +0000
1"""Data import operational functions."""
3from typing import Optional
5from django.core.exceptions import ValidationError
6from django.utils.translation import gettext_lazy as _
8import tablib
9import tablib.core
11import InvenTree.helpers
14def load_data_file(data_file, file_format=None):
15 """Load data file into a tablib dataset.
17 Arguments:
18 data_file: django file object containing data to import (should be already opened!)
19 file_format: Format specifier for the data file
20 """
21 # Introspect the file format based on the provided file
22 if not file_format:
23 file_format = data_file.name.split('.')[-1]
25 if file_format and file_format.startswith('.'):
26 file_format = file_format[1:]
28 file_format = file_format.strip().lower()
30 if file_format not in InvenTree.helpers.GetExportFormats():
31 raise ValidationError(_('Unsupported data file format'))
33 file_object = data_file.file
35 if hasattr(file_object, 'open'):
36 file_object.open('r')
38 file_object.seek(0)
40 try:
41 data = file_object.read()
42 except OSError:
43 raise ValidationError(_('Failed to open data file'))
45 # Excel formats expect binary data
46 if file_format not in ['xls', 'xlsx']:
47 data = data.decode()
49 try:
50 data = tablib.Dataset().load(data, headers=True, format=file_format)
51 except tablib.core.UnsupportedFormat:
52 raise ValidationError(_('Unsupported data file format'))
53 except tablib.core.InvalidDimensions:
54 raise ValidationError(_('Invalid data file dimensions'))
56 return data
59def extract_column_names(data_file) -> list:
60 """Extract column names from a data file.
62 Uses the tablib library to extract column names from a data file.
64 Args:
65 data_file: File object containing data to import
67 Returns:
68 List of column names extracted from the file
70 Raises:
71 ValidationError: If the data file is not in a valid format
72 """
73 data = load_data_file(data_file)
75 return normalize_headers(data.headers)
78def normalize_headers(headers) -> list:
79 """Normalize a list of raw column headers extracted from a data file.
81 Strips whitespace from each header, and generates a default header
82 for any column that does not have one. Must be used consistently
83 wherever column headers are extracted, so that column names used for
84 field mapping match the column names used when extracting row data.
86 Args:
87 headers: List of raw header values (as returned by tablib)
89 Returns:
90 List of normalized column names
91 """
92 result = []
94 for idx, header in enumerate(headers):
95 if header:
96 result.append(str(header).strip())
97 else:
98 # If the header is empty, generate a default header
99 result.append(f'Column {idx + 1}')
101 return result
104def get_field_label(field) -> Optional[str]:
105 """Return the label for a field in a serializer class.
107 Check for labels in the following order of descending priority:
109 - The serializer class has a 'label' specified for the field
110 - The underlying model has a 'verbose_name' specified
111 - The field name is used as the label
113 Arguments:
114 field: Field instance from a serializer class
116 Returns:
117 str: Field label
118 """
119 if field and (label := getattr(field, 'label', None)):
120 return label
122 # TODO: Check if the field is a model field
124 return None