From d27e09238f5386692379344965657735944afcb9 Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Fri, 8 Mar 2024 10:11:03 +0100 Subject: [PATCH] WIP: Filling XLSX: Seems to be working. We should still have a few more tests. --- .../table_json_conversion/fill_xlsx.py | 73 +++++++++++++++--- .../data/multiple_refs_data.json | 48 ++++++++++++ .../data/multiple_refs_data.xlsx | Bin 0 -> 12870 bytes .../table_json_conversion/test_fill_xlsx.py | 8 +- unittests/table_json_conversion/utils.py | 4 +- 5 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 unittests/table_json_conversion/data/multiple_refs_data.json create mode 100644 unittests/table_json_conversion/data/multiple_refs_data.xlsx diff --git a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py index ec381ec7..5a620d67 100644 --- a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py +++ b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py @@ -20,6 +20,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. +from __future__ import annotations + import json from collections import OrderedDict from types import SimpleNamespace @@ -126,7 +128,6 @@ class TemplateFiller: def __init__(self, workbook: Workbook): self._workbook = workbook self._create_index() - self._context: Optional[dict[str, Any]] = None @property def workbook(self): @@ -134,9 +135,49 @@ class TemplateFiller: def fill_data(self, data: dict): """Fill the data into the workbook.""" - self._context = data self._handle_data(data=data) - self._context = None + + class Context: + """Context for an entry: simple properties of all ancestors, organized in a dict. + + This is similar to a dictionary with all scalar element properties at the tree nodes up to + the root. Siblings in lists and dicts are ignored. Additionally the context knows where + its current position is. + + Lookup of elements can easily be achieved by giving the path (as ``list[str]`` or + stringified path). + + """ + + def __init__(self, current_path: List[str] = None, props: Dict[str, Any] = None): + self._current_path = current_path if current_path is not None else [] + self._props = props if props is not None else {} # this is flat + + def copy(self) -> TemplateFiller.Context: + """Deep copy.""" + result = TemplateFiller.Context(current_path=self._current_path.copy(), + props=self._props.copy()) + return result + + def next_level(self, next_level: str) -> TemplateFiller.Context: + result = self.copy() + result._current_path.append(next_level) + return result + + def __getitem__(self, path: Union[List[str], str], owner=None) -> Any: + if isinstance(path, list): + path = p2s(path) + return self._props[path] + + def __setitem__(self, propname: str, value): + fullpath = p2s(self._current_path + [propname]) + self._props[fullpath] = value + + def fill_from_data(self, data: Dict[str, Any]): + """Fill current level with all scalar elements of ``data``.""" + for name, value in data.items(): + if not isinstance(value, (dict, list)): + self[name] = value def _create_index(self): """Create a sheet index for the workbook. @@ -175,6 +216,7 @@ class TemplateFiller: col_type=col[coltype_idx].value) def _handle_data(self, data: dict, current_path: List[str] = None, + context: TemplateFiller.Context = None, only_collect_insertables: bool = False, ) -> Optional[Dict[str, Any]]: """Handle the data and write it into ``workbook``. @@ -186,7 +228,13 @@ data: dict current_path: list[str], optional If this is None or empty, we are at the top level. This means that all children shall be entered - into their respective sheets and not into a sheet at this level. + into their respective sheets and not into a sheet at this level. ``current_path`` and ``context`` + must either both be given, or none of them. + +context: TemplateFiller.Context, optional + Directopry of scalar element properties at the tree nodes up to the root. Siblings in lists + and dicts are ignored. ``context`` and ``current_path`` must either both be given, or none of + them. only_collect_insertables: bool, optional If True, do not insert anything on this level, but return a dict with entries to be inserted. @@ -194,16 +242,21 @@ only_collect_insertables: bool, optional Returns ------- - out: union[dict, None] If ``only_collect_insertables`` is True, return a dict (path string -> value) """ + assert (current_path is None) is (context is None), ( + "`current_path` and `context` must either both be given, or none of them.") if current_path is None: current_path = [] - assert self._context is not None + if context is None: + context = TemplateFiller.Context() + context.fill_from_data(data) + insertables: Dict[str, Any] = {} for name, content in data.items(): path = current_path + [name] + next_context = context.next_level(name) # preprocessing if isinstance(content, list): if not content: @@ -213,13 +266,13 @@ out: union[dict, None] if isinstance(content[0], dict): # An array of objects: must go into exploded sheet for entry in content: - self._handle_data(data=entry, current_path=path) + self._handle_data(data=entry, current_path=path, context=next_context) continue elif isinstance(content, dict): if not current_path: # Special handling for top level - self._handle_data(content, current_path=path) + self._handle_data(content, current_path=path, context=next_context) continue - insert = self._handle_data(content, current_path=path, + insert = self._handle_data(content, current_path=path, context=next_context.copy(), only_collect_insertables=True) assert isinstance(insert, dict) assert not any(key in insertables for key in insert) @@ -260,7 +313,7 @@ out: union[dict, None] if insert_row is not None and sheet is not None and _is_exploded_sheet(sheet): foreigns = _get_foreign_key_columns(sheet) for index, path in ((f.index, f.path) for f in foreigns.values()): - value = _get_deep_value(self._context, path) + value = context[path] sheet.cell(row=insert_row+1, column=index+1, value=value) return None diff --git a/unittests/table_json_conversion/data/multiple_refs_data.json b/unittests/table_json_conversion/data/multiple_refs_data.json new file mode 100644 index 00000000..5b8ce913 --- /dev/null +++ b/unittests/table_json_conversion/data/multiple_refs_data.json @@ -0,0 +1,48 @@ +{ + "Training": { + "trainer": [], + "participant": [ + { + "full_name": "Petra Participant", + "email": "petra@indiscale.com" + }, + { + "full_name": "Peter", + "email": "peter@getlinkahead.com" + } + ], + "Organisation": [ + { + "Person": [ + { + "full_name": "Henry Henderson", + "email": "henry@organization.org" + }, + { + "full_name": "Harry Hamburg", + "email": "harry@organization.org" + } + ], + "name": "World Training Organization", + "Country": "US" + }, + { + "Person": [ + { + "full_name": "Hermione Harvard", + "email": "hermione@organisation.org.uk" + }, + { + "full_name": "Hazel Harper", + "email": "hazel@organisation.org.uk" + } + ], + "name": "European Training Organisation", + "Country": "UK" + } + ], + "date": "2024-03-21T14:12:00.000Z", + "url": "www.indiscale.com", + "name": "Example training with multiple organizations." + } +} diff --git a/unittests/table_json_conversion/data/multiple_refs_data.xlsx b/unittests/table_json_conversion/data/multiple_refs_data.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..21622ede9515b0cfa9f965f8c2ee89f782c4bf0c GIT binary patch literal 12870 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 b4+8RUcve9M66)<J$gkVlYfn;SeEar4J;J{d literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/test_fill_xlsx.py b/unittests/table_json_conversion/test_fill_xlsx.py index 5b45939a..18ab4f06 100644 --- a/unittests/table_json_conversion/test_fill_xlsx.py +++ b/unittests/table_json_conversion/test_fill_xlsx.py @@ -56,9 +56,6 @@ custom_output: str, optional assert os.path.exists(outfile) generated = load_workbook(outfile) # workbook can be read known_good_wb = load_workbook(known_good) - # if custom_output is not None: - # from IPython import embed - # embed() compare_workbooks(generated, known_good_wb) @@ -71,5 +68,6 @@ def test_detect(): def test_fill_xlsx(): fill_and_compare(json_file="data/simple_data.json", template_file="data/simple_template.xlsx", known_good="data/simple_data.xlsx") - # fill_and_compare(json_file="data/example.json", template_file="data/example_template.xlsx", - # known_good="data/example_single_data.xlsx") + fill_and_compare(json_file="data/multiple_refs_data.json", + template_file="data/multiple_refs_template.xlsx", + known_good="data/multiple_refs_data.xlsx") diff --git a/unittests/table_json_conversion/utils.py b/unittests/table_json_conversion/utils.py index 0311e8bb..6c32117c 100644 --- a/unittests/table_json_conversion/utils.py +++ b/unittests/table_json_conversion/utils.py @@ -33,7 +33,9 @@ Parameters hidden: bool, optional Test if the "hidden" status of rows and columns is the same. """ - assert wb1.sheetnames == wb2.sheetnames, "Sheet names are different." + assert wb1.sheetnames == wb2.sheetnames, ( + f"Sheet names are different: \n{wb1.sheetnames}\n !=\n{wb2.sheetnames}" + ) for sheetname in wb2.sheetnames: sheet_1 = wb1[sheetname] sheet_2 = wb2[sheetname] -- GitLab