diff --git a/src/caosadvancedtools/table_json_conversion/convert.py b/src/caosadvancedtools/table_json_conversion/convert.py index 1f87bc7f5fa67b4597b320770469e9cca71fbb44..f650fdd9d0a5fc9bba81788bc957f84fc0dbd987 100644 --- a/src/caosadvancedtools/table_json_conversion/convert.py +++ b/src/caosadvancedtools/table_json_conversion/convert.py @@ -45,10 +45,16 @@ def _strict_bool(value: Any) -> bool: return value raise TypeError(f"Not a good boolean: {repr(value)}") - -def format_exception_table(exceptions: list(tuple), worksheet_title: str, - column_names: Optional[dict, list] = None, - max_line_length: Optional[int] = 120) -> str: +def _column_id_to_chars(num): + """Converts a column id (zero based) to the corresponding string + representation, e.g. 0 -> 'A', 97 -> 'CT'""" + if num < 0: + return "" + return _column_id_to_chars(int(num / 26) - 1) + chr(int(num % 26) + 65) + +def _format_exception_table(exceptions: list(tuple), worksheet_title: str, + column_names: Optional[dict, list] = None, + max_line_length: Optional[int] = 120) -> str: """ Given a list of tuples containing a row and column number as well as an exception in that order, and the title of the current worksheet, returns @@ -77,10 +83,6 @@ def format_exception_table(exceptions: list(tuple), worksheet_title: str, string_rep: str Table containing the given exceptions """ - def to_char(num): - if num < 0: - return "" - return to_char(int(num / 26) - 1) + chr(int(num % 26) + 65) max_line_length -= 40 # Estimate of Field + Type space use headers = {"loc": "Location", "type": "Error Type", "mess": ["Message"]} @@ -103,9 +105,9 @@ def format_exception_table(exceptions: list(tuple), worksheet_title: str, new_data.append(row) # Field if isinstance(row_i, int): - row["loc"] = f"Cell {to_char(col_i)}{row_i + 1}" + row["loc"] = f"Cell {_column_id_to_chars(col_i)}{row_i + 1}" else: - row["loc"] = f"Column {to_char(col_i)}" + row["loc"] = f"Column {_column_id_to_chars(col_i)}" lengths["loc"] = max(lengths["loc"], len(row["loc"])) # Code row["type"] = type(excep).__name__ @@ -296,12 +298,17 @@ class XLSXConverter: If True, do not fail with unresolvable foreign definitions, but collect all errors. """ row_type_column = xlsx_utils.get_row_type_column_index(sheet) + col_type_row = xlsx_utils.get_column_type_row_index(sheet) foreign_columns = xlsx_utils.get_foreign_key_columns(sheet) foreign_column_paths = {col.index: col.path for col in foreign_columns.values()} data_columns = xlsx_utils.get_data_columns(sheet) data_column_paths = {col.index: col.path for col in data_columns.values()} # Parent path, insert in correct order. - parent, proper_name = xlsx_utils.get_path_position(sheet) + try: + parent, proper_name = xlsx_utils.get_path_position(sheet) + except UnboundLocalError as e: + raise jsonschema.ValidationError(f"Malformed metadata: Cannot parse " + f"paths in worksheet '{sheet.title}'.") from e if parent: parent_sheetname = xlsx_utils.get_worksheet_for_path(parent, self._defining_path_index) if parent_sheetname not in self._handled_sheets: @@ -349,6 +356,8 @@ class XLSXConverter: value = self._validate_and_convert(value, path) _set_in_nested(mydict=data, path=path, value=value, prefix=parent, skip=1) continue + elif sheet.cell(col_type_row+1, col_idx+1).value is None: + warn(f"No metadata configured for column {_column_id_to_chars(col_idx)}.") except (ValueError, KeyError, jsonschema.ValidationError) as e: # Append error for entire column only once if isinstance(e, KeyError) and 'column' in str(e): @@ -372,8 +381,8 @@ class XLSXConverter: self._errors[(sheet.title, row_idx)] = kerr.definitions if exceptions != []: - exception_table = format_exception_table(exceptions, sheet.title, - col_names) + exception_table = _format_exception_table(exceptions, sheet.title, + col_names) raise jsonschema.ValidationError(exception_table) self._handled_sheets.add(sheet.title) diff --git a/src/caosadvancedtools/table_json_conversion/xlsx_utils.py b/src/caosadvancedtools/table_json_conversion/xlsx_utils.py index 3cb2de680e29154c888305b0786ba442f20b4354..7efe15c8b3cbf81bd6489b5993503a346415c40f 100644 --- a/src/caosadvancedtools/table_json_conversion/xlsx_utils.py +++ b/src/caosadvancedtools/table_json_conversion/xlsx_utils.py @@ -258,6 +258,16 @@ def get_row_type_column_index(sheet: Worksheet): raise ValueError("The column which defines row types (COL_TYPE, PATH, ...) is missing") +def get_column_type_row_index(sheet: Worksheet): + """Return the row index (0-indexed) of the row which defines the column types. + """ + for row in sheet.rows: + for cell in row: + if cell.value == RowType.COL_TYPE.name: + return cell.row - 1 + raise ValueError("The column which defines row types (COL_TYPE, SCALAR, ...) is missing") + + def get_subschema(path: list[str], schema: dict) -> dict: """Return the sub schema at ``path``.""" if path: