Skip to content
Snippets Groups Projects
Commit 6ff71e56 authored by Henrik tom Wörden's avatar Henrik tom Wörden
Browse files

ENH: xlsx template generator is complete

parent e25679e1
No related branches found
No related tags found
4 merge requests!100WIP: Filling XLSX: Seems to be working.,!94ENH: add framework for converting json schema into table templates,!93Filling XLSX: Everything except multiple choice.,!92ENH: xlsx template generator
Pipeline #48090 failed
......@@ -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
......
No preview for this file type
......@@ -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}')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment