From 29a0bdf3426b926822a2ba0fc1b2f149e7ece3ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com>
Date: Mon, 4 Mar 2024 15:47:02 +0100
Subject: [PATCH] FIX: foreign keys

---
 .../table_json_conversion/table_generator.py  | 40 ++++++++++++++-----
 .../test_table_template_generator.py          | 21 +++++-----
 2 files changed, 40 insertions(+), 21 deletions(-)

diff --git a/src/caosadvancedtools/table_json_conversion/table_generator.py b/src/caosadvancedtools/table_json_conversion/table_generator.py
index f0e8f6c6..8c1952eb 100644
--- a/src/caosadvancedtools/table_json_conversion/table_generator.py
+++ b/src/caosadvancedtools/table_json_conversion/table_generator.py
@@ -103,7 +103,7 @@ class TableTemplateGenerator(ABC):
         if foreign_keys is None:
             foreign_keys = {}
         # here, we treat the top level
-        # sheets[sheetname][colname]= (COL_TYPE, [path])
+        # sheets[sheetname][colname]= (COL_TYPE, description, [path])
         sheets: dict[str, dict[str, tuple[str, list]]] = {}
         if "properties" not in schema:
             raise ValueError("Inappropriate JSON schema: The following part should contain "
@@ -139,24 +139,42 @@ class TableTemplateGenerator(ABC):
         return selected_keys
 
     def _treat_schema_element(self, schema: dict, sheets: dict = None, path: list = 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 """
+                              foreign_keys: dict = None, level_in_sheet_name: int = 1,
+                              array_paths: list = None
+                              ) -> dict[str, tuple[str, str, list]]:
+        """ recursively transforms elements from the schema into column definitions
+
+        sheets is modified in place.
+        Returns
+        -------
+
+            dict
+            describing the columns; see doc string of _generate_sheets_from_schema
+        """
         if not ("type" in schema or "enum" in schema or "oneOf" in schema or "anyOf" in schema):
             raise ValueError("Inappropriate JSON schema: The following part should contain the "
                              f"'type' key:\n{schema}\n")
 
+        if array_paths is None:
+            array_paths = [path]
+
         ctype = ColumnType.SCALAR
 
         # if it is an array, value defs are in 'items'
         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
+            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[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[path[-1]].update({c: (ColumnType.FOREIGN, f"see sheet '{path[0]}'", path[:-1]+[c])})
+                sheetname = ".".join(path)
+                sheets[sheetname] = self._treat_schema_element(
+                    schema['items'], sheets, path, foreign_keys, len(path),
+                    array_paths=array_paths+[path])
+                print("Sel", path)
+                for p in array_paths:
+                    keys = self._get_foreign_keys(foreign_keys, p)
+                    for k in keys:
+                        print(k, p)
+                        sheets[sheetname].update({k: (ColumnType.FOREIGN, f"see sheet '{path[0]}'", p+[k])})
                 # columns are added to the new sheet, thus we do not return columns
                 return {}
             else:
@@ -175,7 +193,7 @@ class TableTemplateGenerator(ABC):
             for pname in schema["properties"].keys():
                 cols.update(self._treat_schema_element(
                     schema["properties"][pname], sheets, path+[pname], foreign_keys,
-                    level_in_sheet_name))
+                    level_in_sheet_name, array_paths=array_paths))
             return cols
         else:
             description = schema['description'] if 'description' in schema else None
diff --git a/unittests/table_json_conversion/test_table_template_generator.py b/unittests/table_json_conversion/test_table_template_generator.py
index 34e0ac75..47fb5d21 100644
--- a/unittests/table_json_conversion/test_table_template_generator.py
+++ b/unittests/table_json_conversion/test_table_template_generator.py
@@ -69,7 +69,7 @@ def test_generate_sheets_from_schema():
     with pytest.raises(ValueError,
                        match="Inappropriate JSON schema: The following part "
                        "should define an object.*"):
-        generator._generate_sheets_from_schema(schema)
+        generator._generate_sheets_from_schema(schema, {'Training': ['a']})
 
     # bad schema
     schema = {
@@ -83,7 +83,7 @@ def test_generate_sheets_from_schema():
     with pytest.raises(ValueError,
                        match="Inappropriate JSON schema: The following part "
                        "should define an object.*"):
-        generator._generate_sheets_from_schema(schema)
+        generator._generate_sheets_from_schema(schema, {'Training': ['a']})
 
     # minimal case: one RT with one P
     schema = {
@@ -100,7 +100,7 @@ def test_generate_sheets_from_schema():
             }
         }
     }
-    sdef = generator._generate_sheets_from_schema(schema)
+    sdef = generator._generate_sheets_from_schema(schema, {'Training': ['a']})
     assert "Training" in sdef
     tdef = sdef['Training']
     assert 'name' in tdef
@@ -111,8 +111,9 @@ def test_generate_sheets_from_schema():
         schema = json.load(sfi)
     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': {"__this__": ['date', 'url']}})
+    sdef = generator._generate_sheets_from_schema(
+        schema,
+        foreign_keys={'Training': {"__this__": ['date', 'url']}})
     assert "Training" in sdef
     tdef = sdef['Training']
     assert tdef['date'] == (ColumnType.SCALAR, 'date', ["Training", 'date'])
@@ -127,7 +128,7 @@ 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['coach']
+    cdef = sdef['Training.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',
@@ -166,7 +167,9 @@ 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': {"__this__": ['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"))
@@ -195,9 +198,7 @@ def test_template_generator():
             continue
         with open(fk_path) as sfi:
             fk = json.load(sfi)
-        generator.generate(schema=schema,
-                           foreign_keys=fk,
-                           filepath=path)
+        generator.generate(schema=schema, foreign_keys=fk, filepath=path)
         os.system(f'libreoffice {path}')
 
     # TODO test collisions of sheet or colnames
-- 
GitLab