From d0f4f3f5ba5c6fc314bafc9bdf66b7f0450a8f16 Mon Sep 17 00:00:00 2001
From: "i.nueske" <i.nueske@indiscale.com>
Date: Fri, 15 Nov 2024 20:39:21 +0100
Subject: [PATCH] MNT: Implement review feedback, extend test, some more
 comments

---
 .../table_json_conversion/convert.py          | 108 ++++++++++--------
 .../data/simple_data_broken.xlsx              | Bin 9175 -> 9133 bytes
 .../data/simple_data_broken_paths.xlsx        | Bin 0 -> 9175 bytes
 .../table_json_conversion/test_read_xlsx.py   |  32 +++++-
 4 files changed, 89 insertions(+), 51 deletions(-)
 create mode 100644 unittests/table_json_conversion/data/simple_data_broken_paths.xlsx

diff --git a/src/caosadvancedtools/table_json_conversion/convert.py b/src/caosadvancedtools/table_json_conversion/convert.py
index 37c39aae..3bc25556 100644
--- a/src/caosadvancedtools/table_json_conversion/convert.py
+++ b/src/caosadvancedtools/table_json_conversion/convert.py
@@ -25,6 +25,7 @@ from __future__ import annotations
 import datetime
 import itertools
 import sys
+import textwrap
 from functools import reduce
 from operator import getitem
 from types import SimpleNamespace
@@ -95,65 +96,52 @@ def _format_exception_table(exceptions: list[tuple], worksheet_title: str,
     exceptions.sort(key=lambda tup: tup[1])
     for row_i, col_i, excep in exceptions:
         if column_names is not None:
-            # Update Names
+            # Add a line with information about the current column
             if current_column != col_i:
                 current_column = col_i
                 new_data.append({
                     "loc": f"\nErrors in column '{column_names[col_i]}':",
                     "type": "", "mess": [""]
                 })
-        # Setup
-        row = {}
-        new_data.append(row)
-        # Field
+        # Setup for current Exception
+        curr_err_data = {}
+        new_data.append(curr_err_data)
+        # Get field
         if isinstance(row_i, int):
-            row["loc"] = f"Cell {_column_id_to_chars(col_i)}{row_i + 1}"
+            curr_err_data["loc"] = f"Cell {_column_id_to_chars(col_i)}{row_i + 1}"
         else:
-            row["loc"] = f"Column {_column_id_to_chars(col_i)}"
-        lengths["loc"] = max(lengths["loc"], len(row["loc"]))
-        # Code
-        row["type"] = type(excep).__name__
-        lengths["type"] = max(lengths["type"], len(row["type"]))
-        # Message
+            curr_err_data["loc"] = f"Column {_column_id_to_chars(col_i)}"
+        lengths["loc"] = max(lengths["loc"], len(curr_err_data["loc"]))
+        # Add error code
+        curr_err_data["type"] = type(excep).__name__
+        lengths["type"] = max(lengths["type"], len(curr_err_data["type"]))
+        # Format message - split into lines
         lines = str(excep).split('\n')
         new_lines = []
         for line in lines:
-            if len(line) > max_line_length:
-                words = line.split(' ')
-                current = ""
-                for word, next_word in zip(words, words[1:] + [""]):
-                    if current != "":
-                        current += " "
-                    current += word
-                    if len(current + next_word) > max_line_length:
-                        lengths["mess"] = max(lengths["mess"], len(current))
-                        new_lines.append(current)
-                        current = ""
-                if current != "":
-                    lengths["mess"] = max(lengths["mess"], len(current))
-                    new_lines.append(current)
-            elif len(line) > 0:
-                lengths["mess"] = max(lengths["mess"], len(line))
-                new_lines.append(line)
+            new_lines += textwrap.wrap(line, max_line_length, break_long_words=False)
+        for line in new_lines:
+            lengths["mess"] = max(lengths["mess"], len(line))
         if new_lines == []:
             new_lines = [""]
-        row["mess"] = new_lines
+        curr_err_data["mess"] = new_lines
 
+    # Generate underline for each header
     dividers = {key: '–' * l for key, l in lengths.items()}
     dividers["mess"] = [dividers["mess"]]
-
-    # Fill for the messages is set to 0, if we want another column or align
-    # right we need to use lengths["mess"]
+    # Fill with spaces for alignment
     string_rep = f"There were errors during the validation of worksheet '{worksheet_title}':\n\n"
-    for row in [headers, dividers] + new_data:
-        string_rep += ' {loc: <{fill}}  '.format(loc=row["loc"],
+    for curr_err_data in [headers, dividers] + new_data:
+        string_rep += ' {loc: <{fill}}  '.format(loc=curr_err_data["loc"],
                                                  fill=lengths["loc"])
-        string_rep += ' {typ: <{fill}}  '.format(typ=row["type"],
+        string_rep += ' {typ: <{fill}}  '.format(typ=curr_err_data["type"],
                                                  fill=lengths["type"])
-        string_rep += ' {mes: <{fill}}\n'.format(mes=row["mess"][0], fill=0)
-        for line in row["mess"][1:]:
-            # Front padding
-            string_rep += ' ' * (lengths["loc"] + lengths["type"] + 7)
+        # Fill for the messages is set to 0, if we want another column or align
+        # right we need to use lengths["mess"]
+        string_rep += ' {mes: <{fill}}\n'.format(mes=curr_err_data["mess"][0], fill=0)
+        for line in curr_err_data["mess"][1:]:
+            # Front padding for lines without location and error type
+            string_rep += ' ' * (lengths["loc"] + lengths["type"] + 6)
             string_rep += ' {mes: <{fill}}\n'.format(mes=line, fill=0)
     return string_rep
 
@@ -194,7 +182,11 @@ class XLSXConverter:
         self._workbook = load_workbook(xlsx)
         self._schema = read_or_dict(schema)
         self._defining_path_index = xlsx_utils.get_defining_paths(self._workbook)
-        self._check_columns(fail_fast=strict)
+        try:
+            self._check_columns(fail_fast=strict)
+        except KeyError as e:
+            raise jsonschema.ValidationError(f"Malformed metadata: Cannot parse paths. "
+                                             f"Unknown path: {e}") from e
         self._handled_sheets: set[str] = set()
         self._result: dict = {}
         self._errors: dict = {}
@@ -220,9 +212,29 @@ class XLSXConverter:
         self._handled_sheets = set()
         self._result = {}
         self._errors = {}
-        for sheetname in self._workbook.sheetnames:
-            if sheetname not in self._handled_sheets:
-                self._handle_sheet(self._workbook[sheetname], fail_later=collect_errors)
+        if not collect_errors:
+            for sheetname in self._workbook.sheetnames:
+                if sheetname not in self._handled_sheets:
+                    self._handle_sheet(self._workbook[sheetname], fail_later=collect_errors)
+        else:
+            # Collect errors from converting
+            exceptions = []
+            for sheetname in self._workbook.sheetnames:
+                if sheetname not in self._handled_sheets:
+                    try:
+                        self._handle_sheet(self._workbook[sheetname], fail_later=collect_errors)
+                    except jsonschema.ValidationError as e:
+                        exceptions.append(e)
+                        # do not collect errors from sheet again
+                        self._handled_sheets.add(sheetname)
+            if len(exceptions) == 1:
+                raise exceptions[0]
+            elif len(exceptions) > 1:
+                mess = "There were errors during the validation of several worksheets:\n\n"
+                mess += '\n\n'.join([str(e).replace("There were errors during the validation of worksheet",
+                                                    "In worksheet")
+                                     for e in exceptions])
+                raise jsonschema.ValidationError(mess)
         if validate:
             jsonschema.validate(self._result, self._schema)
         if self._errors:
@@ -323,6 +335,7 @@ class XLSXConverter:
         # entries: dict[str, list[SimpleNamespace]] = {}
 
         exceptions = []
+        warns = []
         col_names = {}
         for row_idx, row in enumerate(sheet.iter_rows(values_only=True)):
             # Skip non-data rows
@@ -359,7 +372,12 @@ class XLSXConverter:
                             _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)}.")
+                        mess = (f"\nNo metadata configured for column "
+                                f"'{_column_id_to_chars(col_idx)}' in worksheet "
+                                f"'{sheet.title}'.\n")
+                        if mess not in warns:
+                            print(mess, file=sys.stderr)
+                            warns.append(mess)  # Prevent multiple instances of same warning
                 except (ValueError, KeyError, jsonschema.ValidationError) as e:
                     # Append error for entire column only once
                     if isinstance(e, KeyError) and 'column' in str(e):
diff --git a/unittests/table_json_conversion/data/simple_data_broken.xlsx b/unittests/table_json_conversion/data/simple_data_broken.xlsx
index 0221570c942fc28f2b59a282f751781ff4a504fa..a65d464a53459de73e41fd20d807899c44728cda 100644
GIT binary patch
delta 6686
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

delta 6811
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
QpwXp-Gcuty(f{`RAI8aq-2eap

diff --git a/unittests/table_json_conversion/data/simple_data_broken_paths.xlsx b/unittests/table_json_conversion/data/simple_data_broken_paths.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..0221570c942fc28f2b59a282f751781ff4a504fa
GIT binary patch
literal 9175
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
XiBuJ!pGO237}Cr3>~{@hFIWEqzzhRX

literal 0
HcmV?d00001

diff --git a/unittests/table_json_conversion/test_read_xlsx.py b/unittests/table_json_conversion/test_read_xlsx.py
index 8fbf8a2a..f51e114f 100644
--- a/unittests/table_json_conversion/test_read_xlsx.py
+++ b/unittests/table_json_conversion/test_read_xlsx.py
@@ -113,27 +113,47 @@ def test_missing_columns():
         assert expected in messages
 
 
-def test_wrong_datatype():
+def test_error_table():
     with pytest.raises(jsonschema.ValidationError) as caught:
         convert.to_dict(xlsx=rfp("data/simple_data_broken.xlsx"),
                         schema=rfp("data/simple_schema.json"))
     # Correct Errors
+    assert "Malformed metadata: Cannot parse paths in worksheet 'Person'." in str(caught.value)
     assert "'Not a num' is not of type 'number'" in str(caught.value)
+    assert "'Yes a number?' is not of type 'number'" in str(caught.value)
     assert "1.5 is not of type 'integer'" in str(caught.value)
+    assert "1.2345 is not of type 'integer'" in str(caught.value)
+    assert "'There is no entry in the schema" in str(caught.value)
+    assert "'Not an enum' is not one of [" in str(caught.value)
     # Correct Locations
     for line in str(caught.value).split('\n'):
         if "'Not a num' is not of type 'number'" in line:
             assert "J7" in line
+        if "'Yes a number?' is not of type 'number'" in line:
+            assert "J8" in line
         if "1.5 is not of type 'integer'" in line:
             assert "K7" in line
         if "1.2345 is not of type 'integer'" in line:
             assert "K8" in line
-    # No additional type errors
-    if "is not of type 'boolean'" in str(caught.value):   # ToDo: Remove when boolean is fixed
-        assert str(caught.value).count("is not of type") == 3
+        if "'There is no entry in the schema" in line:
+            assert "Column M" in line
+        if "'Not an enum' is not one of [" in line:
+            assert "G8" in line
+    # No additional errors
+    assert str(caught.value).count("Malformed metadata: Cannot parse paths in worksheet") == 1
+    assert str(caught.value).count("There is no entry in the schema") == 1
+    assert str(caught.value).count("is not one of") == 1
+    # FIXME ToDo: Remove when boolean is fixed / when everything works as
+    #             expected, set correct number.
+    if "is not of type 'boolean'" in str(caught.value):
+        assert str(caught.value).count("is not of type") == 6
     else:
-        assert str(caught.value).count("is not of type") == 2  # FIXME when everything works as
-        #                                                      # expected, set correct number.
+        assert str(caught.value).count("is not of type") == 4
+    # Check correct error message for completely unknown path
+    with pytest.raises(jsonschema.ValidationError) as caught:
+        convert.to_dict(xlsx=rfp("data/simple_data_broken_paths.xlsx"),
+                        schema=rfp("data/simple_schema.json"))
+    assert "Malformed metadata: Cannot parse paths" in str(caught.value)
 
 
 def test_additional_column():
-- 
GitLab