From 3d9adf321c2c95e7e33581841d9e347eebfd1d14 Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Tue, 7 May 2024 12:55:46 +0200 Subject: [PATCH] ENH: checking of foreign keys and missing columns. --- .../table_json_conversion/convert.py | 143 +++++++++++++++--- .../data/multiple_choice_data_missing.xlsx | Bin 0 -> 5996 bytes .../multiple_refs_data_wrong_foreign.xlsx | Bin 0 -> 12914 bytes .../data/simple_data_missing.xlsx | Bin 0 -> 8915 bytes .../data/simple_data_wrong_foreign.xlsx | Bin 0 -> 8979 bytes .../table_json_conversion/test_read_data.py | 73 ++++++++- 6 files changed, 193 insertions(+), 23 deletions(-) create mode 100644 unittests/table_json_conversion/data/multiple_choice_data_missing.xlsx create mode 100644 unittests/table_json_conversion/data/multiple_refs_data_wrong_foreign.xlsx create mode 100644 unittests/table_json_conversion/data/simple_data_missing.xlsx create mode 100644 unittests/table_json_conversion/data/simple_data_wrong_foreign.xlsx diff --git a/src/caosadvancedtools/table_json_conversion/convert.py b/src/caosadvancedtools/table_json_conversion/convert.py index 94856167..5cc6fe24 100644 --- a/src/caosadvancedtools/table_json_conversion/convert.py +++ b/src/caosadvancedtools/table_json_conversion/convert.py @@ -22,12 +22,16 @@ from __future__ import annotations +import datetime +import itertools +import sys from functools import reduce from operator import getitem from types import SimpleNamespace from typing import Any, BinaryIO, Callable, TextIO, Union +from warnings import warn -from jsonschema import validate, ValidationError +import jsonschema from openpyxl import load_workbook from openpyxl.worksheet.worksheet import Worksheet @@ -42,6 +46,12 @@ def _strict_bool(value: Any) -> bool: raise TypeError(f"Not a good boolean: {repr(value)}") +class ForeignError(KeyError): + def __init__(self, *args, definitions: list, message: str = ""): + super().__init__(message, *args) + self.definitions = definitions + + class XLSXConverter: """Class for conversion from XLSX to JSON. @@ -56,7 +66,8 @@ documentation. "boolean": _strict_bool, } - def __init__(self, xlsx: Union[str, BinaryIO], schema: Union[dict, str, TextIO]): + def __init__(self, xlsx: Union[str, BinaryIO], schema: Union[dict, str, TextIO], + strict: bool = False): """ Parameters ---------- @@ -65,16 +76,29 @@ xlsx: Union[str, BinaryIO] schema: Union[dict, str, TextIO] Schema for validation of XLSX content. + +strict: bool, optional + If True, fail faster. """ 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) self._handled_sheets: set[str] = set() self._result: dict = {} + self._errors: dict = {} - def to_dict(self) -> dict: + def to_dict(self, validate: bool = False, collect_errors: bool = True) -> dict: """Convert the xlsx contents to a dict. +Parameters +---------- +validate: bool, optional + If True, validate the result against the schema. + +collect_errors: bool, optional + If True, do not fail at the first error, but try to collect as many errors as possible. + Returns ------- out: dict @@ -82,12 +106,62 @@ out: dict """ 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]) + self._handle_sheet(self._workbook[sheetname], fail_later=collect_errors) + if validate: + jsonschema.validate(self._result, self._schema) + if self._errors: + raise RuntimeError("There were error while handling the XLSX file.") return self._result - def _handle_sheet(self, sheet: Worksheet) -> None: + def get_errors(self) -> dict: + """Return a dict with collected errors.""" + return self._errors + + def _check_columns(self, fail_fast: bool = False): + """Check if the columns correspond to the schema.""" + def missing(path): + message = f"Missing column: {xlsx_utils.p2s(path)}" + if fail_fast: + raise ValueError(message) + else: + warn(message) + for sheetname in self._workbook.sheetnames: + sheet = self._workbook[sheetname] + parents: dict = {} + col_paths = [] + for col in xlsx_utils.get_data_columns(sheet).values(): + parents[xlsx_utils.p2s(col.path[:-1])] = col.path[:-1] + col_paths.append(col.path) + for path in parents.values(): + subschema = xlsx_utils.get_subschema(path, self._schema) + if subschema.get("type") == "array": + subschema = subschema["items"] + if "enum" in subschema: # Was handled in parent level already + continue + for child, content in subschema["properties"].items(): + child_path = path + [child] + if content == {'type': 'string', 'format': 'data-url'}: + continue # skip files + if content.get("type") == "array" and ( + content.get("items").get("type") == "object"): + if child_path not in itertools.chain(*self._defining_path_index.values()): + missing(child_path) + elif content.get("type") == "array" and "enum" in content.get("items", []) and ( + content.get("uniqueItems") is True): + # multiple choice + for choice in content["items"]["enum"]: + if child_path + [choice] not in col_paths: + missing(child_path + [choice]) + elif content.get("type") == "object": + pass + else: + if child_path not in col_paths: + missing(child_path) + + def _handle_sheet(self, sheet: Worksheet, fail_later: bool = False) -> None: """Add the contents of the sheet to the result (stored in ``self._result``). Each row in the sheet corresponds to one entry in an array in the result. Which array exactly is @@ -95,6 +169,11 @@ defined by the sheet's "proper name" and the content of the foreign columns. Look at ``xlsx_utils.get_path_position`` for the specification of the "proper name". + +Parameters +---------- +fail_later: bool, optional + If True, do not fail with unresolvable foreign definitions, but collect all errors. """ row_type_column = xlsx_utils.get_row_type_column_index(sheet) foreign_columns = xlsx_utils.get_foreign_key_columns(sheet) @@ -106,7 +185,7 @@ Look at ``xlsx_utils.get_path_position`` for the specification of the "proper na if parent: parent_sheetname = xlsx_utils.get_worksheet_for_path(parent, self._defining_path_index) if parent_sheetname not in self._handled_sheets: - self._handle_sheet(self._workbook[parent_sheetname]) + self._handle_sheet(self._workbook[parent_sheetname], fail_later=fail_later) # # We save single entries in lists, indexed by their foreign key contents. Each entry # # consists of: @@ -114,7 +193,7 @@ Look at ``xlsx_utils.get_path_position`` for the specification of the "proper na # # - data: The actual data of this entry, a dict. # entries: dict[str, list[SimpleNamespace]] = {} - for row in sheet.iter_rows(values_only=True): + for row_idx, row in enumerate(sheet.iter_rows(values_only=True)): # Skip non-data rows. if row[row_type_column] is not None: continue @@ -147,13 +226,18 @@ Look at ``xlsx_utils.get_path_position`` for the specification of the "proper na _set_in_nested(mydict=data, path=path, value=value, prefix=parent, skip=1) continue - # Find current position in tree - parent_dict = self._get_parent_dict(parent_path=parent, foreign=foreign) - - # Append data to current position's list - if proper_name not in parent_dict: - parent_dict[proper_name] = [] - parent_dict[proper_name].append(data) + try: + # Find current position in tree + parent_dict = self._get_parent_dict(parent_path=parent, foreign=foreign) + + # Append data to current position's list + if proper_name not in parent_dict: + parent_dict[proper_name] = [] + parent_dict[proper_name].append(data) + except ForeignError as kerr: + if not fail_later: + raise + self._errors[(sheet.title, row_idx)] = kerr.definitions self._handled_sheets.add(sheet.title) def _is_multiple_choice(self, path: list[str]) -> bool: @@ -187,7 +271,12 @@ the values given in the ``foreign`` specification. current_object = cand break else: - raise KeyError("Cannot find an element which matches the foreign definitions") + message = "Cannot find an element which matches these foreign definitions:\n\n" + for name, value in group.definitions: + message += f"{name}: {value}\n" + print(message, file=sys.stderr) + error = ForeignError(definitions=group.definitions, message=message) + raise error assert isinstance(current_object, dict) return current_object @@ -208,8 +297,16 @@ This includes: values = [self.PARSER[array_type](v) for v in value.split(";")] return values try: - validate(value, subschema) - except ValidationError as verr: + # special case: datetime or date + if ("anyOf" in subschema): + if isinstance(value, datetime.datetime) and ( + {'type': 'string', 'format': 'date-time'} in subschema["anyOf"]): + return value + if isinstance(value, datetime.date) and ( + {'type': 'string', 'format': 'date'} in subschema["anyOf"]): + return value + jsonschema.validate(value, subschema) + except jsonschema.ValidationError as verr: print(verr) print(path) raise @@ -359,7 +456,8 @@ mydict: dict return mydict -def to_dict(xlsx: Union[str, BinaryIO], schema: Union[dict, str, TextIO]) -> dict: +def to_dict(xlsx: Union[str, BinaryIO], schema: Union[dict, str, TextIO], + validate: bool = None, strict: bool = False) -> dict: """Convert the xlsx contents to a dict, it must follow a schema. Parameters @@ -370,10 +468,17 @@ xlsx: Union[str, BinaryIO] schema: Union[dict, str, TextIO] Schema for validation of XLSX content. +validate: bool, optional + If True, validate the result against the schema. + +strict: bool, optional + If True, fail faster. + + Returns ------- out: dict A dict representing the JSON with the extracted data. """ - converter = XLSXConverter(xlsx, schema) + converter = XLSXConverter(xlsx, schema, strict=strict) return converter.to_dict() diff --git a/unittests/table_json_conversion/data/multiple_choice_data_missing.xlsx b/unittests/table_json_conversion/data/multiple_choice_data_missing.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..10d76529ab11d2e79ca0651313dd50f0cc326341 GIT binary patch literal 5996 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 literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/data/multiple_refs_data_wrong_foreign.xlsx b/unittests/table_json_conversion/data/multiple_refs_data_wrong_foreign.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8290260743fa92b27057e90e7dcead1124cd592b GIT binary patch literal 12914 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 FcmU6F=A{4t literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/data/simple_data_missing.xlsx b/unittests/table_json_conversion/data/simple_data_missing.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6e6d3a39d965de81236f2ad14bd2116ac4d7669b GIT binary patch literal 8915 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 literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/data/simple_data_wrong_foreign.xlsx b/unittests/table_json_conversion/data/simple_data_wrong_foreign.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f2e88217bc0dd123ecb79e86173e97cc320139f3 GIT binary patch literal 8979 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 literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/test_read_data.py b/unittests/table_json_conversion/test_read_data.py index eef9a9d8..3f4350b6 100644 --- a/unittests/table_json_conversion/test_read_data.py +++ b/unittests/table_json_conversion/test_read_data.py @@ -20,6 +20,7 @@ """Testing the conversion from XLSX to JSON""" +import datetime import json import os import re @@ -39,17 +40,23 @@ def rfp(*pathcomponents): def convert_and_compare(xlsx_file: str, schema_file: str, known_good_file: str, - strict: bool = False) -> dict: + known_good_data: dict = None, strict: bool = False, + validate: bool = True) -> dict: """Convert an XLSX file and compare to a known result. +Exactly one of ``known_good_file`` and ``known_good_data`` should be non-empty. + Returns ------- json: dict The result of the conversion. """ - result = convert.to_dict(xlsx=xlsx_file, schema=schema_file) - with open(known_good_file, encoding="utf-8") as myfile: - expected = json.load(myfile) + result = convert.to_dict(xlsx=xlsx_file, schema=schema_file, validate=validate) + if known_good_file: + with open(known_good_file, encoding="utf-8") as myfile: + expected = json.load(myfile) + else: + expected = known_good_data assert_equal_jsons(result, expected, allow_none=not strict, allow_empty=not strict) return result @@ -70,6 +77,13 @@ def test_conversions(): known_good_file=rfp("data/multiple_choice_data.json"), strict=True) + with open(rfp("data/simple_data.json"), encoding="utf-8") as myfile: + expected_datetime = json.load(myfile) + expected_datetime["Training"][0]["date"] = datetime.datetime(2023, 1, 1, 0, 0) + convert_and_compare(xlsx_file=rfp("data/simple_data_datetime.xlsx"), + schema_file=rfp("data/simple_schema.json"), + known_good_file="", known_good_data=expected_datetime) + # Data loss when saving as xlsx with pytest.raises(AssertionError) as err: convert_and_compare(xlsx_file=rfp("data/simple_data_ascii_chars.xlsx"), @@ -78,6 +92,57 @@ def test_conversions(): assert str(err.value).startswith("Values at path ['Training', 0, ") +def test_missing_columns(): + with pytest.raises(ValueError) as caught: + convert.to_dict(xlsx=rfp("data/simple_data_missing.xlsx"), + schema=rfp("data/simple_schema.json"), strict=True) + assert str(caught.value) == "Missing column: Training.coach.given_name" + with pytest.warns(UserWarning) as caught: + convert.to_dict(xlsx=rfp("data/simple_data_missing.xlsx"), + schema=rfp("data/simple_schema.json")) + assert str(caught.pop().message) == "Missing column: Training.coach.given_name" + with pytest.warns(UserWarning) as caught: + convert.to_dict(xlsx=rfp("data/multiple_choice_data_missing.xlsx"), + schema=rfp("data/multiple_choice_schema.json")) + messages = {str(w.message) for w in caught} + for expected in [ + "Missing column: Training.skills.Communication", + "Missing column: Training.exam_types.Oral", + ]: + assert expected in messages + + +def test_faulty_foreign(): + # Simple wrong foreign key + converter = convert.XLSXConverter(xlsx=rfp("data/simple_data_wrong_foreign.xlsx"), + schema=rfp("data/simple_schema.json")) + with pytest.raises(RuntimeError): + converter.to_dict() + errors = converter.get_errors() + assert errors == {('Training.coach', 6): [['date', datetime.datetime(2023, 1, 2, 0, 0)], + ['url', 'www.indiscale.com']]} + + # More extensive example + converter = convert.XLSXConverter(xlsx=rfp("data/multiple_refs_data_wrong_foreign.xlsx"), + schema=rfp("data/multiple_refs_schema.json")) + with pytest.raises(RuntimeError): + converter.to_dict() + errors = converter.get_errors() + assert errors == { + ('Training.Organisation.Person', 8): [ + ['name', 'World Training Organization 2']], + ('Training.Organisation.Person', 9): [ + ['date', '2024-03-21T14:12:00.000Z'], + ['url', 'www.getlinkahead.com']], + ('Training.participant', 6): [ + ['date', '2024-03-21T14:12:00.000Z'], + ['url', None]], + ('Training.participant', 7): [ + ['date', '2024-03-21T14:12:00.000Z'], + ['url', None]], + } + + def test_set_in_nested(): """Test the ``_set_in_nested`` function.""" set_in_nested = convert._set_in_nested # pylint: disable=protected-access -- GitLab