From bad0b2fa7a48b9d55c95c1e906e049a136bb462b Mon Sep 17 00:00:00 2001
From: Daniel Hornung <d.hornung@indiscale.com>
Date: Wed, 17 Apr 2024 10:59:45 +0200
Subject: [PATCH] ENH: XLSX table template generator works with multiple choice
 arrays

---
 .../table_json_conversion/table_generator.py  |  36 ++++++++++----
 src/doc/table-json-conversion/specs.md        |  37 ++++++++++++--
 .../data/multiple_choice_schema.json          |  47 ++++++++++++++++++
 .../data/multiple_choice_template.xlsx        | Bin 0 -> 5992 bytes
 .../test_table_template_generator.py          |   6 +++
 5 files changed, 111 insertions(+), 15 deletions(-)
 create mode 100644 unittests/table_json_conversion/data/multiple_choice_schema.json
 create mode 100644 unittests/table_json_conversion/data/multiple_choice_template.xlsx

diff --git a/src/caosadvancedtools/table_json_conversion/table_generator.py b/src/caosadvancedtools/table_json_conversion/table_generator.py
index 8ca026a8..857100ef 100644
--- a/src/caosadvancedtools/table_json_conversion/table_generator.py
+++ b/src/caosadvancedtools/table_json_conversion/table_generator.py
@@ -42,7 +42,8 @@ class ColumnType(Enum):
     SCALAR = 1
     LIST = 2
     FOREIGN = 3
-    IGNORE = 4
+    MULTIPLE_CHOICE = 4
+    IGNORE = 5
 
 
 class RowType(Enum):
@@ -189,15 +190,16 @@ class TableTemplateGenerator(ABC):
 
         # if it is an array, value defs are in 'items'
         if schema.get('type') == 'array':
-            if (schema['items'].get('type') == 'object'
-                    and len(path) > 1):  # list of references; special treatment
+            items = schema['items']
+            # list of references; special treatment
+            if (items.get('type') == 'object' and len(path) > 1):
                 # we add a new sheet with columns generated from the subtree of the schema
                 sheetname = p2s(path)
                 if sheetname in sheets:
                     raise ValueError("The schema would lead to two sheets with the same name, "
                                      f"which is forbidden: {sheetname}")
                 col_def = self._treat_schema_element(
-                    schema=schema['items'], sheets=sheets, path=path, foreign_keys=foreign_keys,
+                    schema=items, sheets=sheets, path=path, foreign_keys=foreign_keys,
                     level_in_sheet_name=len(path),
                     array_paths=array_paths+[path]  # since this level is an array extend the list
                 )
@@ -223,8 +225,23 @@ class TableTemplateGenerator(ABC):
                 # current sheet.
                 return {}
 
+            # List of enums: represent as checkbox columns
+            if (schema.get("uniqueItems") is True and "enum" in items and len(items) == 1):
+                choices = items["enum"]
+                assert len(path) >= 1
+                prop_name = path[-1]
+                result = {}
+                for choice in choices:
+                    name = f"{prop_name}.{choice}"
+                    result[name] = (
+                        ColumnType.MULTIPLE_CHOICE,
+                        schema.get('description'),
+                        path + [str(choice)],
+                    )
+                return result
+
             # it is a list of primitive types -> semicolon separated list
-            schema = schema['items']
+            schema = items
             ctype = ColumnType.LIST
 
         # This should only be the case for "new or existing reference".
@@ -247,9 +264,8 @@ class TableTemplateGenerator(ABC):
             return cols
 
         # The schema is a leaf.
-        description = schema['description'] if 'description' in schema else None
         # definition of a single column
-        default_return = {p2s(path[level_in_sheet_name:]): (ctype, description, path)}
+        default_return = {p2s(path[level_in_sheet_name:]): (ctype, schema.get('description'), path)}
         if 'type' not in schema and 'enum' in schema:
             return default_return
         if 'type' not in schema and 'anyOf' in schema:
@@ -350,12 +366,12 @@ class XLSXTemplateGenerator(TableTemplateGenerator):
             ordered_cols = self._get_ordered_cols(sheetdef)
 
             # create other columns
-            for index, (colname, ct, desc, path) in enumerate(ordered_cols):
-                ws.cell(1, 2 + index, ct.name)
+            for index, (colname, coltype, desc, path) in enumerate(ordered_cols):
+                ws.cell(1, 2 + index, coltype.name)
                 for path_index, el in enumerate(path):
                     ws.cell(2 + path_index, 2 + index, el)
                 ws.cell(header_index, 2 + index, colname)
-                if ct == ColumnType.FOREIGN:
+                if coltype == ColumnType.FOREIGN:
                     # Visual highlighting
                     ws.cell(header_index, 2 + index).fill = yellowfill
                 if desc:
diff --git a/src/doc/table-json-conversion/specs.md b/src/doc/table-json-conversion/specs.md
index 9ae717ca..68c54099 100644
--- a/src/doc/table-json-conversion/specs.md
+++ b/src/doc/table-json-conversion/specs.md
@@ -46,13 +46,15 @@ XLSX file. These properties have an attribute name and a value. The value can be
 a. A primitive (text, number, boolean, ...)
 b. A record
 c. A list of primitive types
-d. A list of records
+d. A list of unique enums (multiple choice)
+e. A list of records
 
 In cases *a.* and *c.*, a cell is created in the column corresponding to the property in the XLSX
 file.  In case *b.*, columns are created for the Properties of the record, where for each of the
-Properties the cases *a.* - *d.* are considered recursively.
+Properties the cases *a.* - *e.* are considered recursively.  Case *d.* leads to a number of
+columns, one for each of the possible choices.
 
-For case *d.* however, the two-dimensional structure of an XLSX sheet is not sufficient. Therefore,
+For case *e.* however, the two-dimensional structure of an XLSX sheet is not sufficient. Therefore,
 for such cases, *new* XLSX sheets/tables are created.
 
 In these sheets/tables, the referenced records are treated as described above (new columns for the
@@ -124,7 +126,31 @@ This entry would be represented in an XLSX sheet with the following content:
 The list elements are written into the cell separated by `;` (semicolon). If the elements contain
 the separator `;`, it is escaped with `\\`.
 
-### d. Properties containing lists with references ###
+### d. Multiple choice properties ###
+
+```JSON
+{
+    "Training": {
+        "date": "2024-04-17",
+        "skills": [
+              "Planning",
+              "Evaluation"
+        ]
+    }
+}
+```
+
+If the `skills` list is denoted as an `enum` array with `"uniqueItems": true` in the json schema,
+this entry would be represented like this in an XLSX:
+
+| date       | skills.Planning | skills.Communication | skills.Evaluation |
+|------------|-----------------|----------------------|-------------------|
+| 2024-04-17 | x               |                      | x                 |
+
+Note that this example assumes that the list of possible choices, as given in the json schema, was
+"Planning, Communication, Evaluation".
+
+### e. Properties containing lists with references ###
 
 ```JSON
 {
@@ -176,7 +202,8 @@ special treatment.  The following values are used:
 
 - ``IGNORE``: This row is ignored.  It can be used for explanatory texts or layout.
 - ``COL_TYPE``: Typically the first row that is not `IGNORE`.  It indicates the row that defines the
-  type of columns (`FOREIGN`, `SCALAR`, `LIST`, `IGNORE`).  This row may occur only once.
+  type of columns (`FOREIGN`, `SCALAR`, `LIST`, `MULTIPLE_CHOICE`, `IGNORE`).  This row must occur
+  exactly once per sheet.
 - ``PATH``: Indicates that the row is used to define the path within the JSON.  These rows are
   typically hidden for users.
 
diff --git a/unittests/table_json_conversion/data/multiple_choice_schema.json b/unittests/table_json_conversion/data/multiple_choice_schema.json
new file mode 100644
index 00000000..75de127e
--- /dev/null
+++ b/unittests/table_json_conversion/data/multiple_choice_schema.json
@@ -0,0 +1,47 @@
+{
+  "type": "object",
+  "properties": {
+    "Training": {
+      "type": "object",
+      "required": [],
+      "additionalProperties": false,
+      "title": "Training",
+      "properties": {
+        "name": {
+          "type": "string",
+          "description": "The name of the Record to be created"
+        },
+        "date": {
+          "description": "The date of the training.",
+          "anyOf": [
+            {
+              "type": "string",
+              "format": "date"
+            },
+            {
+              "type": "string",
+              "format": "date-time"
+            }
+          ]
+        },
+        "skills": {
+          "description": "Skills that are trained.",
+          "type": "array",
+          "items": {
+            "enum": [
+              "Planning",
+	            "Communication",
+	            "Evaluation"
+            ]
+          },
+          "uniqueItems": true
+        }
+      }
+    }
+  },
+  "required": [
+    "Training"
+  ],
+  "additionalProperties": false,
+  "$schema": "https://json-schema.org/draft/2020-12/schema"
+}
diff --git a/unittests/table_json_conversion/data/multiple_choice_template.xlsx b/unittests/table_json_conversion/data/multiple_choice_template.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..1b4abaadb5b9a2218340f848d93cd2fbf8948908
GIT binary patch
literal 5992
zcmaJ_1z42r(xywAr8@+qk&p)IS{gn=LST_*SvsYgB}7VT2|+p~rMnv>B&G8MB+uga
zUq_DTKhAr-8`tj4^FA~4+%xy6DIp>O;Ly;}-~f_wx^NHV5$v-u1Z3;P$#MT$9N((a
z&V?Cr;1$7g&bJ_emX+6_EZ;!OOZA1pp1e9drz7zAOqu{6GrFQ3>gfCW)--of*`95Y
zi&4oaTw5<3p^4GVC%>cr*3pBLe=ys(3r|2!!$FD#5~HekI`Hjz-v=+}SVfHQCqSi9
z_)W82b2w3nP>7+n+tXX~u#F(S-6(v=S321<uaO#qfOa-~_6pP#o?<*Jf6=|6b?s49
z&hgF{XvD_?g<yCrf_y&rFC8RclVWno1M!k*H}-po#%rOpHwpb)$;kxupEn?2(^Hhe
zM*JYISI%BQdLMb^{R~1%9%IEw{q*bp4FWYKRJ7n!-qd}V6NV7r;MD%7n{Z(NaJS`j
zbAZ^GIyl&HxZBxAYxLUBbKy3fvm!0pu^U;FCnEj@jfGplZRkyjmnE`^0Q5N8=6`Q-
zlh(6}I2#F+9%_5$V6k|-@j~f<G>TBalZ^sp0P*dPWP#5>^WIo%ln2pgmbwWHQ&;$Y
zd!2(sCOhnuDb$!nR`N=Wa=nY@LDquLl-no(8AUs5sCjCW136-286n)%SWJM)_Vf$p
zDO$FHJCpAf06|-4-%rn9qMAxT7|v?&qd`TWCk!I4SRRd5VkfDVOfNPDjGM-8tsA2@
zL(Sf3xDF_Cu&d>~74zwBmV!>;CB=In%b{(y)~WtLl%xk~kV0ypa_~%7!i%ZcM=In;
z*@TeKgVBjkEi9qa*3tl(+tQvI$_ePJ8n^U}AePf77FF5(@2+|cGo<|q*}+1o(OyfG
zVLrj!?}AUlJbEfaUDdeXA)S5#bY-)ys0p|nV``OF7tr1T<?LVGY(054l9sg)C$C?z
z*2Gy*qluw$sHYpJuwqLTfUwaVMqz;?jkQ???{C5^J}M`}(3bet|M(3Dnkmq7`eMhk
z<P3!Yk}ZfCP!=4_<6HH8s3`G6-j%UuJRQ%-lb-N!4>@|%v(+2kRK`jx-ms}}mbf9c
zZ(2h~-Ji-kj3>w@*~WWZz6n}aBR+jh=@S`(atOD6CfP6QMrR<bC<bzQ-92!?SAL0n
zpEg?sK)FkJI5;rkpV9{FpR{pu_OJyx-6zeSzG2Ke0f5hvvH$g=q-01)gqq82#axp1
zWQyY3LNNi8Fl{sX)hdP8H*?9j^1EBS^maV#(s&m}8D23l;$WK5B14(?oMF$BM+uWf
z&;0@|yCBN_gzm4C#0(Jf@nSb(xgvB-V#p3zZt1N%(lh6=B-`TfEGrSnX}6O~=FQ2q
zS~moX@ZP2e9ARMSt>w;7P_#CxXGYXH4L|ydVT!R5W?xi=AN{7BoiJY30dl&P@q#Q6
z8SGpav>xBL6h?=Q4qolqL+DU%{mwmvH4-9V-7IKK6Zc$1JFN|4>(~bB5`>_Wyj$&9
zf;o$FEL)?}KF1(5fehc2kC}n9yr=Ln3Vlo42+l4nhYZ7C00?T`Dmgf--eqCwhGLIT
zU8(HFx8K<Mo`5CV(S`^kpS}!o3{Bc0lplXq$qSSuv=DXeS!MJX@1pMTHm@-oIk7!1
zv#?Rs80TC4{64f@FcYW(ec8**aIizjOk;)n9r(Ju^wcDZJ^+Ju9Q?w5La$7}{H0`>
zq7emGL>Tm|anC6!-I@+?8%JjN<$iBw!-_ZlSPjiIMrTvQL|i?>Cz}e@6b!lxB9{>H
zc^3L{n*b&KtTz!3f&|P2d#Mwtt7ud#LgZaX&q(#LhAIpYC-51;dCK)oU+^rM7P;YF
z0KHqRp?|0Gp3#}6)`sVn&VrT2m&yU>_D?hy#=&U)Vma3mkU%GN$_Zz;&Tx%I_59gf
zAr}$VV6xOuMaq!Yaax{?9^IZ7BDIW92~xrbq%G*2&{Kk61Ks*McME6ON+JfYQ)hz2
zQ=){wHt!5xn^3njI+M4s4yEAy5Q)FkoFBhat+>$_52B)<Yk$>ykTvT)w##I5*1%?U
z>&)a{W*cdx-as|z>mRq_I$zr=HS5bt?B_qJ9<%?~vhm*<L6|`X4%~Rj+wor5^19ls
zQa7`MXv+rhLvjb2I2f{-U(n_tP|KPFZcf?cPM>7;l6AE6wji%acgCyC&>;FS-?w0Z
z=E@|Zr-zPVt(3<Yp~q(`(fac_sVr5|?$FCkD)O$AiRoQ+F#{@cL~_~L!Svi}(e&CX
z&xB!$!4N%t?mqi7hV;vs<7DnEkpXrrsU;kQ$m|8LR34OYU@gs|hygG8vtW6AC#ttw
z-BmT`H9kRK#j$ODJeSbQt0%<p?bxere7tR5SzB4224%6woTZS?bV1KjM68ulxSLBo
zC%Xpu1NYLosyrrid1FRt$_fYyVm`mNt?W2&AK6tW;Xz!g^_Z}&^0LJQpuX#NIMCeh
zz1*Xkayh@a3%U@$2La}tZLvNW0Ibpc2?R)f)ea|15Xjky^XD7)Jsvda8amDW#Di}~
zQW-TDh?oi(&KhVyH>)=aV|xS5=muS>Gl{I|^2a;piWSjuPt19M$GC@+N22HHtdFqU
zV{r7Wi@8^WHt6Sf>S}^dVr3bOp}PlemFG+~oydLk31bv7yK)12F{|Ps<bC@d0p(>h
z&xhn(>7*Su1Vog<A?~3>0-<k{D=^XP%#}ikF>~Y>5^|vj^*&5e47DyHJZJAIc%cem
z`MfVc0>nb-?N9L4#8l;5i)?X5E01L`?J_3|(`Caon>030Jd3?a(Mi{NiPB;%KvsJk
zs%hhCV${iZ70O0%#U<hI6O14Cw{1G7(7Lv;EK2PoB<RN`gvm{oiBc6_PY*;Wuy9=|
z(vv`<G@vhwC6v4)JI^JHBZWP&{H<q6ang=Aj=1Q!aP(3-!6FsIO_d(Tbbw{*fM<<I
zi!1001DVa*5$<nQki~iM2Nj^SEHj0eiz9<Uc}bbbw&NRZEhN%K2vKYV1z@O|dOpC?
zF;bHPgM#m%K|dTIZ%<u`4BSD&gO0KgRv=~Oe2OT|T8T?j64aDec%;DHYQ-{NRjg*(
zB5~_GT$9c`XfKrzAfLX&U()h%ey||H2}-r4*sD>}$7J(-s|mBWv4kRiTe8o9<_92A
zdxj|BJK2skN5DpP^W=*7q=ZRvdXn;2Z{ui-JXg%9dMyO4i|2H45{5=h-%*T4&I9$C
zlKUPfrnXls`D?DA=c&sm-h3qTgiZ?}H?kCvBBCs0RBC>4N6Uu%ZQS=RJD?vvQK6lX
zn3t7xoL*5L@U5}l`YCfCrl>^Rp(5n(zRsLfTua0A%}b5UyTQ8rQTAM8jZ41VmRx7y
zlJl!44Ua)?;_J(V%z^P0wK2z4t8TBwK`kG4rxW&VG}99j0(}yU@=I%Ca?XoyuO$$o
zL!<23@@i5Urwkf5=EOuB40Ho=M1T>^la##dvuouYlO)R)*rR{XPzu@T8HZm?$htR#
z^{RJh(br=IwM5nlL?ly63eXr8XWrFdTo&&)gap`0533zAn|cBq9Rb|JK{M)QS2Y-}
zF)TB(NcTT3sg*^OkYG^B3}Dl`0(PI6m~88&)0NWaY&hfe=+eGd<m2I*^f#jK4yV;1
zceD<Jt=N@vJiFG!i)cLX#n@umfrrZh$<<;swT;v5WT^FSMG$FkbwmJ1yqb2y*Pedh
zrZKcjM2qjkQV@xOX&!AL;~H_O<XtpqmpaK2r(*(k10XmZBBW3$3shQt`TQd*W3Kqf
zBFPkWR`_-V6o?Ht(ZI<}_KW%CCK_lhd;=ku09=>M*WBO-BxbPX6qx(yBV()JC!kG)
z*1VgB88Aj{$Jr{#{Eqq}@DeAlF%zMy-r=!&v&}o2JT2amw7JJ67+g%q7PV{W&6H^Y
z5G?u+U(MDT`}*g^W)hVhI{~WF&wdyNoUnc;awVc4`BZUE-;k-yCH>`$O#`r)u{jzO
z6gC&DZ?ZwY!HGn`o0W2%=BIR*cn~Jcj^@qKH^!n}8y8C?t)k$gH+3_tHLWAPLXt4t
z8O7C{TiE`Wx^wxW82c)1G@adx?gKR)@8p8J1K*N@3ovS8iowzs?4=RR_1BBbk1t;4
z5fcubO!og6naztMb>ZghXJ*gOT{JGL0+nQq?-FqAEV*^v!QX@Ac5Fn(5)vF7J^r6S
zlI$Ol{D+D>Y_tENC2>HTpS0wLQ1-l20o5!@pegEk>CFXvm-@;`2Y>uj^_?#Vxd8@f
zcjV2;^hvY)-`l*=+hHA2m?(wWNWrb0u4qmEH~V|vNOcCY7ndy`$COdy7c~;h_z^2`
z>Q|l!=y8~~#R?mK=v7qtINe(xxTnt9f2=V69)6Hev)BN!%AQrN!Kv-8%xoG9+c?K7
zgR2J%eGozw$1|!CW4g@B`4sik?#H_5x;P!>41Hf|V1@?28~UWN1=WG)BM0>PVm060
zYlf5K+7y}M(!~XDgnQ4Z9{%tggYEnAAE^i6AI~^hzJ!3xHJu?=_OE`nk_%c(O6?Mu
zzUQR^`wI*FGSYK>%$lg<(X*l#4CRBiN4^97b_K7SwuGKbpMH6km0)z~^CSL6{MvP=
zWx0%Mbmd2iQy40+8&1x2YjH$Iwrf6N_A8bUj{N8?6Tdn8ToQn!$(~z*{n957@RpA(
zGu<F(U7U3j3rXx!PR1riO+A_<N~hiTPMXy66CihfCblof><NF3$D+w1DN6oRrGno0
zv>wvpEOWav$?vtymEQbKockNA(!p%%?1!j_c4DbeBK47-^{V87{MD;#YyRHVufuYk
z+JLgd_dL^l7`Ck@fU*Ew)DQWxHW5k+fzeQ2x30tWx43n|*(9s%-+?0D5Dy-Mjs=+{
zvxBP(QE?~V(Ra&mb-kAw<y&pDBc!s;@C$P~CS*5rq@l&n_rk;@T}^LQg+N&8U>x%6
z!fW8xO^eEf&E`ee$0GHrc80HtNVi5NTi%ZY2ymDc7nR_P>W)AVVOwfwo9}xsM<vU@
zco-#dm(OiKiNz6?PV7?Nhj3Qr&4@pY2E-!&DY@}rAvAX|Q-e5wojA=LAfWrwAFR<2
zquRKQCxmea;j5?c6=PZf8jhLx_%by1U5j}k9p$$9xvyj1R9R)ct=rp;>J~Q2A3vx-
z<2U*AX#y#ZSr(qz2>s(Rx~JYyJ2^nNJHbRI*CfEzWR$l-@pE3|(?fF>tS*Hz|7RMB
z5Ygv0o4lPf$9xF`vPM}Vp9__H--j}Gryg-8BC4#oM=61oxw^q-B-@L4d+f*u>@Ic@
zQxafI<b=%koeXpr1uiCvUPluB@0pdhT*){Tam-6^YbuECYv(P6EpvT+1D1$lhV3TQ
z3O7CqWU?*bNPdRjkU(1WbDgG4o^D;R6_nG%XKLqCX)`ZVMx%_jq-e>%82-^!R>b;-
zZx`=E-)~*kdedztu7!+>Sb4qr^`WwaK!d|$pCUa_>nDSY+){?Hc|(3PMM*pnE!jyH
z)uYeZ!+-62-Jwu~ta&P!Dt0-`s~}Ke&Iw}Lnx7C<;#oQDITEx@QD=NHOe#Zdb{!)e
zsm58KJs<FeQG8Uip~vp)uCSnO|MA@6J`CDRF!(-ZX|X_uw)^qO6`^)VHE`Y~IE&YE
zyozAV_4|muc4A%Z#~*6(*~bUO5{N?==2z8J^PGA>Yx%R~M&A18GKo1zyw4=J?E;(U
z&Yk`7sgsFpxyZ<z;bL@7{k?_ET9MJk!jbmKs~)v-aG!ajM*Lvw6Jlv{j0o1PXXp3f
z6l^3FV;O-@@-Q+>MzDmZt0txqB1pOdb83<JHV!+?1xAY~>Z`e$z%#c_SMm8{6>CWp
zb#KzKB&9a$3d;{k@&=cz#{|ZV6NwBL+NQs)te#k%@ZsDpDT#mQ&*6LYNMxBMqX`PZ
zwNf}_-<4l{zmV6dnb<9Wb$yQ3Or~`A({<pm<c%AcW71&m``^<Nf`_g%b8&KZu>0w~
zp>m8iE<o_Ng?zNxc`ekqv8qS`VXgDn#Ts}ghaO<0j~9KwJ~WhCOl4g3M(PvpES~je
zPSz)I>{!@L><%}%0t&6Y%O+Gond>NQ4X)EK=#j&E;Un2-Yy|TG*10o7%UDT*W*qb~
z4jSo>8obb<wv^o!_%}jYlj^g<1V8A!1%DiE^W7+6n=c()EIQD7+2#1YnkT+biEmSI
zeo3&WK7!qU-j{)tgS|7z-q~2o0}OI9yx&)o)}U%T4WQ+q)UqzMZKIWB+#XdNTHoQp
zk5<ZF;*B)(_0}tE5|mQyq_-OW#jAzWh~}N0dD^E|p)74Eqk9y-nzB<?A4L-z)tC+M
zJ7`7cl%JAhDMYZa@!7K=0UiGj*fxkPS{QbZ|9)wsWXfzXhTpU9?k$t7LzSTFvnDaV
zi;YoV=mTj%?=H6Y%UP@0i_{Rz?wSZtr|x6WQD7R9-1GFa>BRY(Ydi#&;1?~y?=rkD
zG12m%p)pq`8g9TlK&ht%(wd~Vxfrz4rfBX^f5gOE<71?CUluV`e3$BN+2B3y8GcpT
zBn2c6Hu=J5&<oc(q?`Fw8_sB|POtofB;}}^uR$QWeiQAD)G^Ake5Eo9fBCBTN)F}u
zEyb<XX-PKGMP=<b9~?1%pM5cEc=6h~x<ejafrSpY{V)|E4Z@Dh-FLSL|I6J8+$AAY
z-$H%^!P52FWA;JSBX!wSW6w+U`yj0d*W#GN5^v%s*uo<K;C{_CACB?vXPW;h4<?*y
zO8=?&aFBC9<@+T}u%&*R`Tef^kO1D#dVYx!j26O_e@%XVSAEzP-7~~rVut$fb^nVm
z{%+-A3vu5<{gN=upH}`yL-o6zhjr|};rJ!luzlTc<oEXDcMA_e^}gQy5)jO-|5g2d
zSAPgB_sIK8$Y9p~t^R*F{JZwU<9iQ2zeF3>b^crX7Zm+&;9+Y0rvY}@*Z=3tt0^I)
R+^>QGyZm5vWbCKc{slEBAgTZW

literal 0
HcmV?d00001

diff --git a/unittests/table_json_conversion/test_table_template_generator.py b/unittests/table_json_conversion/test_table_template_generator.py
index 8fc7b216..cefc792b 100644
--- a/unittests/table_json_conversion/test_table_template_generator.py
+++ b/unittests/table_json_conversion/test_table_template_generator.py
@@ -267,6 +267,12 @@ def test_model_with_indirect_reference():
         foreign_keys={"Wrapper": ["Training.name", "Training.url"]},
         outfile=None)
 
+def test_model_with_multiple_choice():
+    _compare_generated_to_known_good(
+        schema_file=rfp("data/multiple_choice_schema.json"),
+        known_good=rfp("data/multiple_choice_template.xlsx"),
+        outfile=None)
+
 
 def test_exceptions():
     # Foreign keys must be lists
-- 
GitLab