From 2bdd72866513ec86f97c9a343aa650041207711d Mon Sep 17 00:00:00 2001
From: Daniel <d.hornung@indiscale.com>
Date: Mon, 11 Mar 2024 12:25:38 +0100
Subject: [PATCH] WIP: Deferring foreign keys and inserting deeper refs into
 context.

---
 .../table_json_conversion/fill_xlsx.py        |  30 +++++++++++++++---
 .../data/indirect_data.xlsx                   | Bin 0 -> 5945 bytes
 .../table_json_conversion/test_fill_xlsx.py   |  10 ++++--
 3 files changed, 33 insertions(+), 7 deletions(-)
 create mode 100644 unittests/table_json_conversion/data/indirect_data.xlsx

diff --git a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py
index 62aa922a..63c14af7 100644
--- a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py
+++ b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py
@@ -262,6 +262,8 @@ out: union[dict, None]
             context = TemplateFiller.Context()
         context.fill_from_data(data)
 
+        explode_later: List[dict] = []
+
         insertables: Dict[str, Any] = {}
         for name, content in data.items():
             path = current_path + [name]
@@ -275,16 +277,23 @@ 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, context=next_context)
+                        explode_later.append({"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, context=next_context)
+                    explode_later.append({"data": content, "current_path": path,
+                                          "context": next_context})
                     continue
                 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)
+
+                # print(insert)
+                # from IPython import embed
+                # embed()
+
                 insertables.update(insert)
                 continue
             else:  # scalars
@@ -301,8 +310,9 @@ out: union[dict, None]
             insertables[path_str] = value
         if only_collect_insertables:
             return insertables
-        if not current_path:
-            return None
+        # if not current_path:
+        #     print("returning early")
+        #     return None
 
         # actual data insertion
         insert_row = None
@@ -318,11 +328,21 @@ out: union[dict, None]
 
             sheet.cell(row=insert_row+1, column=col_index+1, value=value)
 
+        # continue in exploded sheets
+        for exploded_content in explode_later:
+            print(f"Exploding: {exploded_content}")
+            self._handle_data(**exploded_content)
+
         # Insert foreign keys
         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 = context[path]
+                try:
+                    value = context[path]
+                except KeyError as err:
+                    print(f"\nCurrent context: {context._props}")
+                    from IPython import embed
+                    embed()
                 sheet.cell(row=insert_row+1, column=index+1, value=value)
 
         return None
diff --git a/unittests/table_json_conversion/data/indirect_data.xlsx b/unittests/table_json_conversion/data/indirect_data.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..45ed38b8222d105f1958cef6a41c12d42a49c883
GIT binary patch
literal 5945
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

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 4af73e4d..1dec8494 100644
--- a/unittests/table_json_conversion/test_fill_xlsx.py
+++ b/unittests/table_json_conversion/test_fill_xlsx.py
@@ -78,10 +78,16 @@ def test_fill_xlsx():
                      template_file=rfp("data/multiple_refs_template.xlsx"),
                      known_good=rfp("data/multiple_refs_data.xlsx"),
                      schema=rfp("data/multiple_refs_schema.json"))
+
+
+def test_fill_indirect_reference():
+    # Run this test:
+    # pytest --pdb --pdbcls=IPython.terminal.debugger:Pdb test_fill_xlsx.py::test_fill_indirect_reference -s
     fill_and_compare(json_file=rfp("data/indirect_data.json"),
                      template_file=rfp("data/indirect_template.xlsx"),
-                     known_good=rfp("data/multiple_refs_data.xlsx"),
-                     schema=rfp("data/indirect_schema.json"))
+                     known_good=rfp("data/indirect_data.xlsx"),
+                     schema=rfp("data/indirect_schema.json"),
+                     custom_output=rfp("data/indirect_data.xlsx"))
 
 
 def test_errors():
-- 
GitLab