diff --git a/src/caosadvancedtools/table_json_conversion/table_generator.py b/src/caosadvancedtools/table_json_conversion/table_generator.py index c69c4984a1f9c4b725c8276ff2603df082c9299c..1e8f939266eee25a5326f8498fb909e72dfa3ee4 100644 --- a/src/caosadvancedtools/table_json_conversion/table_generator.py +++ b/src/caosadvancedtools/table_json_conversion/table_generator.py @@ -124,20 +124,21 @@ class TableTemplateGenerator(ABC): if cpath[0] not in keys: raise ValueError(f"A foreign key definition is missing for path: \n{path}\n{keys}") keys = keys[cpath[0]] - if isinstance(keys, tuple): - selected_keys, keys = keys - elif isinstance(keys, list): + if isinstance(keys, list): selected_keys, keys = keys, None else: selected_keys, keys = None, keys cpath = cpath[1:] + if isinstance(keys, dict) and "__this__" in keys: + selected_keys = keys["__this__"] + if selected_keys is None: raise ValueError(f"A foreign key definition is missing for path:" f"\n{path}\n{foreign_keys}") return selected_keys def _treat_schema_element(self, schema: dict, sheets: dict = None, path: list = None, - foreign_keys: dict = None + foreign_keys: dict = None, level_in_sheet_name: int = 1 ) -> dict[str, tuple[str, list]]: """ recursively transforms elements from the schema into column definitions """ if not ("type" in schema or "enum" in schema or "oneOf" in schema or "anyOf" in schema): @@ -150,10 +151,11 @@ class TableTemplateGenerator(ABC): if 'type' in schema and schema['type'] == 'array': if 'type' in schema['items'] and schema['items']['type'] == 'object' and len(path) > 1: # list of references; special treatment # we add a new sheet - sheets[".".join(path)] = self._treat_schema_element(schema['items'], sheets, path, foreign_keys) + sheets[path[-1]] = self._treat_schema_element(schema['items'], sheets, path, + foreign_keys, + len(path)) for c in self._get_foreign_keys(foreign_keys, path[:-1]): - sheets[".".join(path)].update({".".join(path[:-1]+[c]): ( - ColumnType.FOREIGN, f"see sheet '{path[0]}'", path[:-1]+[c])}) + sheets[path[-1]].update({c: (ColumnType.FOREIGN, f"see sheet '{path[0]}'", path[:-1]+[c])}) # columns are added to the new sheet, thus we do not return columns return {} else: @@ -171,7 +173,8 @@ class TableTemplateGenerator(ABC): cols = {} for pname in schema["properties"].keys(): cols.update(self._treat_schema_element( - schema["properties"][pname], sheets, path+[pname], foreign_keys)) + schema["properties"][pname], sheets, path+[pname], foreign_keys, + level_in_sheet_name)) return cols else: description = schema['description'] if 'description' in schema else None @@ -179,17 +182,17 @@ class TableTemplateGenerator(ABC): # those are the leaves if 'type' not in schema: if 'enum' in schema: - return {".".join(path[1:]): (ctype, description, path)} + return {".".join(path[level_in_sheet_name:]): (ctype, description, path)} if 'anyOf' in schema: for d in schema['anyOf']: # currently the only case where this occurs is date formats assert d['type'] == 'string' assert d['format'] == 'date' or d['format'] == 'date-time' - return {".".join(path[1:]): (ctype, description, path)} + return {".".join(path[level_in_sheet_name:]): (ctype, description, path)} elif schema["type"] in ['string', 'number', 'integer', 'boolean']: if 'format' in schema and schema['format'] == 'data-url': return {} # file; ignore for now - return {".".join(path[1:]): (ctype, description, path)} + return {".".join(path[level_in_sheet_name:]): (ctype, description, path)} else: raise ValueError("Inappropriate JSON schema: The following part should define an" f" object with properties or a primitive type:\n{schema}\n") @@ -285,7 +288,10 @@ class XLSXTemplateGenerator(TableTemplateGenerator): del wb['Sheet'] # order sheets - for index, sn in enumerate(sorted(wb.sheetnames)): + # for index, sn in enumerate(sorted(wb.sheetnames)): + # wb.move_sheet(sn, index-wb.index(wb[sn])) + # reverse sheets + for index, sn in enumerate(wb.sheetnames[::-1]): wb.move_sheet(sn, index-wb.index(wb[sn])) return wb diff --git a/unittests/table_json_conversion/example_template.xlsx b/unittests/table_json_conversion/example_template.xlsx index e6b24f25ad52b40991708c4fc710d25eaabd741e..feb8e5e2d7150d015bef49c0dd9a4bbb1648f809 100644 Binary files a/unittests/table_json_conversion/example_template.xlsx and b/unittests/table_json_conversion/example_template.xlsx differ diff --git a/unittests/table_json_conversion/test_table_template_generator.py b/unittests/table_json_conversion/test_table_template_generator.py index a64ec08324803aaf7485366c57981c06f9bc71a2..b83682f989929bfdb88b1ba520d8a3172dcd1bb5 100644 --- a/unittests/table_json_conversion/test_table_template_generator.py +++ b/unittests/table_json_conversion/test_table_template_generator.py @@ -112,7 +112,7 @@ def test_generate_sheets_from_schema(): with pytest.raises(ValueError, match="A foreign key definition is missing.*"): generator._generate_sheets_from_schema(schema) sdef = generator._generate_sheets_from_schema(schema, - foreign_keys={'Training': (['date', 'url'], {})}) + foreign_keys={'Training': {"__this__": ['date', 'url']}}) assert "Training" in sdef tdef = sdef['Training'] assert tdef['date'] == (ColumnType.SCALAR, 'date', ["Training", 'date']) @@ -127,14 +127,13 @@ def test_generate_sheets_from_schema(): assert tdef['participants'] == (ColumnType.SCALAR, None, ["Training", 'participants']) assert tdef['subjects'] == (ColumnType.LIST, None, ["Training", 'subjects']) assert tdef['remote'] == (ColumnType.SCALAR, None, ["Training", 'remote']) - cdef = sdef['Training.coach'] - assert cdef['coach.family_name'] == (ColumnType.SCALAR, None, ["Training", 'coach', 'family_name']) - assert cdef['coach.given_name'] == (ColumnType.SCALAR, None, ["Training", 'coach', 'given_name']) - assert cdef['coach.Organisation'] == (ColumnType.SCALAR, None, ["Training", 'coach', - 'Organisation']) - print(cdef) - assert cdef['Training.date'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'date']) - assert cdef['Training.url'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'url']) + cdef = sdef['coach'] + assert cdef['family_name'] == (ColumnType.SCALAR, None, ["Training", 'coach', 'family_name']) + assert cdef['given_name'] == (ColumnType.SCALAR, None, ["Training", 'coach', 'given_name']) + assert cdef['Organisation'] == (ColumnType.SCALAR, None, ["Training", 'coach', + 'Organisation']) + assert cdef['date'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'date']) + assert cdef['url'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'url']) def test_get_foreign_keys(): @@ -142,14 +141,14 @@ def test_get_foreign_keys(): fkd = {"Training": ['a']} assert ['a'] == generator._get_foreign_keys(fkd, ['Training']) - fkd = {"Training": (['a'], {})} + fkd = {"Training": {"__this__": ['a']}} assert ['a'] == generator._get_foreign_keys(fkd, ['Training']) fkd = {"Training": {'hallo'}} with pytest.raises(ValueError, match=r"A foreign key definition is missing for path:\n\['Training'\]\n{'Training': \{'hallo'\}\}"): generator._get_foreign_keys(fkd, ['Training']) - fkd = {"Training": (['a'], {'b': ['c']})} + fkd = {"Training": {"__this__": ['a'], 'b': ['c']}} assert ['c'] == generator._get_foreign_keys(fkd, ['Training', 'b']) with pytest.raises(ValueError, match=r"A foreign key definition is missing for.*"): @@ -167,7 +166,7 @@ def test_template_generator(): schema = json.load(sfi) path = os.path.join(tempfile.mkdtemp(), 'test.xlsx') assert not os.path.exists(path) - generator.generate(schema=schema, foreign_keys={'Training': (['date', 'url'], {})}, filepath=path) + generator.generate(schema=schema, foreign_keys={'Training': {"__this__": ['date', 'url']}}, filepath=path) assert os.path.exists(path) generated = load_workbook(path) # workbook can be read example = load_workbook(rfp("example_template.xlsx")) @@ -190,8 +189,14 @@ def test_template_generator(): rp = os.path.join(di, fi) with open(rp) as sfi: schema = json.load(sfi) + fk_path = os.path.join(di, "foreign_keys"+fi[len('schema'):]) + if not os.path.exists(fk_path): + print(f"No foreign keys file for:\n{rp}") + continue + with open(fk_path) as sfi: + fk = json.load(sfi) generator.generate(schema=schema, - foreign_keys={}, + foreign_keys=fk, filepath=path) os.system(f'libreoffice {path}')