From b1573034c957c31b589df1c0b19d56c169ce2ba0 Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Wed, 6 Mar 2024 13:43:40 +0100 Subject: [PATCH] ENH: More extensive testing data, better foreign key naming. --- .../table_json_conversion/table_generator.py | 14 +- .../example_template.xlsx | Bin 9592 -> 9855 bytes .../model_multiple_refs.yml | 5 +- .../table_json_conversion/multiple_refs.xlsx | Bin 0 -> 12238 bytes .../schema_multiple_refs.json | 140 +++++------------- .../test_table_template_generator.py | 81 ++++++---- 6 files changed, 105 insertions(+), 135 deletions(-) create mode 100644 unittests/table_json_conversion/multiple_refs.xlsx diff --git a/src/caosadvancedtools/table_json_conversion/table_generator.py b/src/caosadvancedtools/table_json_conversion/table_generator.py index 07fac819..8794496f 100644 --- a/src/caosadvancedtools/table_json_conversion/table_generator.py +++ b/src/caosadvancedtools/table_json_conversion/table_generator.py @@ -198,13 +198,15 @@ class TableTemplateGenerator(ABC): ) # and add the foreign keys that are necessary up to this point for array_path in array_paths: - keys = self._get_foreign_keys(foreign_keys, array_path) - for key in keys: - if key in sheets[sheetname]: + foreigns = self._get_foreign_keys(foreign_keys, array_path) + for foreign in foreigns: + internal_key = ".".join(array_path + [foreign]) + if internal_key in sheets[sheetname]: raise ValueError("The schema would lead to two columns with the same " - f"name which is forbidden: {key}") - sheets[sheetname][key] = (ColumnType.FOREIGN, f"see sheet '{path[0]}'", - array_path + [key]) + f"name, which is forbidden: {internal_key}") + ref_sheet = ".".join(array_path) + sheets[sheetname][internal_key] = ( + ColumnType.FOREIGN, f"see sheet '{ref_sheet}'", array_path + [foreign]) # Columns are added to the new sheet, thus we do not return any columns for the # current sheet. return {} diff --git a/unittests/table_json_conversion/example_template.xlsx b/unittests/table_json_conversion/example_template.xlsx index 6d9c7627e724144e7748dcfb36f2292be49b036e..1b8a8cd3d4e68c17d7d9d3fc2cb8c54236b39c22 100644 GIT binary patch delta 6786 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 I+n?6|0IK~St^fc4 delta 6509 zcmZvB1yCH(vNp~xu*hN?+}(9Uuq+y!;O;KL6Ch+EA-HdFCpf`9!GpWIYaqdbYktVB z`d{v=*EOeRrl<Q%Rrl%d^nCM2=~4ltrihA0gaiNpkZfLBRe~6hQ6J9L0?6tpj~9p# zRBE`c(W_J*BEOaLirb2Mk~k*2Z^&9Rp+)00S`KW=`sb_fkxTAPyX~alcaAt{*OgjD z`FX@m5bA@*i_KY#p5?JHb(cu1=V%vewlg1*PUJfS3&fj}f}dgH6JZ@g)Y~iZ6b;)x zuSV(=#;u>=SfG1g7+>;eu)%krGM~(X!i?at7G;?XFuq(5oqUy8#fbHe7>H+g*)w+t z9R&uJNmWb{inMuOJl=&qV0K$zdfu84;P{o0z=1WBrw6S5Llh#qkC}L_|BI(?HgrP{ zQZ2r*U!JFSC;_i^`U>SFVNT8#shw|AL^BD`fMKH<yJD9SOtCH%gUx5pqZupR2;rRo zPEM$h<R@A^TXxP;HggT9n9(JBABMsh$^1ld94Q?Wvr9)raW@WTb`50|Z;j;YmOLwm zcj2?FmdwOPDzh@18fA^HJ4TWCLW;-&O4!touZgg3A9TTZxrzWuB{eo^4rwDEp)@v* z!H>@kYaqxQB4!2V1co7{66u2^2Dkz+{}ovjs}p~{@YlD2tYX3g$G%nfXJsm16%M$m zjXh7<gy(-6q!t}yi56I~D1jLh8!*H*X~ljd#eL#`%Y3AS^^snyS(y!bq(%tfqwOnI zal+s!)hYMBVgWzty*CCB85g~R(Wc5ce2rzvbf^%FJ!)Y4l1ZG*aBI&68;6Te3vHF+ zNXmG8+>o`DHCNu37Ae_`(m0qe7|R!*5;-KB`uI&naAI{U<O!ZR6+!j!JXt4>IR@{C zr00W7=1jf|r+H}+bqYh4C6f{LFNW6OBpV*}hhF?Eb3LlUOWoyhH~rsyGFb2O_IEQa z7A7!nJ@4>}N7U}mG<}`&J(=M1mPxj%Cyqi&HOxudlhnb(v8p%ON$t2;gRUuftw|F~ z^T+#cFAC`v&(m%K3ELs|>v9uRW{2u1muvTqFRH2R&S~se8MoEv+|GT|`ZA%-`j}$N z_fk##9Q0xsoLd!gmBHQ){GK*`Wt6uVYKoWu<TX{IT@)lF3Q{B_HAU=y3Ix#vU_*>z z?*1*3CIHUgYC#MDMc^Y5$C@Z+HYI$t#C*@Rd<p9!v|<^f8Kip4R}yYRhk_<4dTFz! z-7kL5)OowLwscR<o6!Po_`l8U&XZV+;{g_ie^HMNj1VrwVO1ij4<8MRo7%z8?p-bn zn|c8jR?ktGY`k(c-S(m5(Jm2|!OThRD!nohL>yhyv7v1&JaE#k$=%oA=$T~$=k$Uc zj$<iAaYCa>7ga3=H4~&w!-z%`30_#n?h9V3wMb@()#x7;sT*28vuat+isxo(S&a)W z-Z+64Odq)IDMepL+(qMu$G&-i7>R)fYzqZ~J$WdcMg;q)KN5qBD=vpolN3V=J1Y{i zIE^jN=y`AUlHn4ttXVJ7nLh6GMSuT5pI-@WYlAJjMk|qm9Z2`IZ@3*+IG-HffiLrj z`smVu_l)u`s*s>UupQOg^_!;!E0TbQ>OJy5=4xL6fT~FUH8EVR<d@k)WE#KNDJbIg z^1@*8XSB;M`qluKke9^*n1wwu1S`l}$wM_6L~g&OXW&T|3Z9vDTMoZg)Jz2`N`;3v zYNJ^SCx3c5{KRzk<}W^LlEcg(Gx}|GC*TN$kI|;rmlRmek6ts$OFVswy#T{sT8kKR zDsLI)s@>$~Czxf&%qN^_C9T#=F~G1+XGdncWe5oK*D2>uQ)2#5a$GS9?EVdM7&H5} z62N=|>V~UJyH$EKor*1+VZD(mr22rR2OlOt9zj>nyDHzxV~MQ=UKeSK4kvF_hM*x^ zcXjSDDh)xJQ4}v32Hz@pj?#~t>wr?or~BA!R5@Svx}&ccbQs1H56f%E58OvTwOoCD z%Clv#^nBuLN=s!fD;B9@$U#%t&tC$q6s`-hr%`Z%lj@Kzz0M_H0A?=h*{+Fp=DTp6 z)@)UB*QwDR4s%x$*9)q3`OK1mJ$Fy-Od<2p(Sk^b(5W50EXZ#@<k-#+gL-=Bhv3ly z3P`_`2^26Nk}v5bt&t^^!>UGpEP(>z<`_NtBI(pwGiDgzLe?C?wJ@+>xE`zQJo&Cv za2Z}6P8hR2aEz7<_j$8{EiCFRBr3jg1e8B<dW|yfI9qh*!StS|Rv{v@j*iD=HGutl zed(YvKT3Y}8$4OV7~x@}9Cw~w-N^`~Gy$UH1#*>b!&drAMztg|#o7G`=K1Cjr7?4$ zY=4?&(4FfQ=ESTE`tQv7&<MOza9~=={5+i1$UVhhN3lW|AzVSGp;oqyEk&0*esD+| zDGaS9?KUF~VmC>^9jNn|kLRgHZLt|4ux3|7I>34^iR5mdJO?CIpg|dSk&6A}_WgY( zfqI@@{c6NZ{$BCR&Zp65$8z@J12hQ=O^#I#QRcDumogKPMEr$x;;H~H=N6^XyfJt$ zmOP{*Es5`nT|wpzG1}{IdryuGsnC-5FT}H<SA4>MB<>U%uYcoU;@XL|nXQNRgxp;P z*1M$O&*itDU2ku6dln0bSjcW)@YySKoZOA&4!Jj~G^^?+VCo-AJc81<`GY=dG$bS< zyhl*_2a|M>9t1H8q(-FSK7q%eq!YoGZ6{cFJ-1s}>u^$L6WFl<S7-HcCOlPK!yXF1 zX=leb{Jb)kw2rJ#k6E!IIjvc<k7sKPoF>{T<@~_q-P;#8S2qE6xfEUESauOT+f)VH zJ$=-lDXpS+$t;}x1E7?t4#Nok0flD4D7E<<V3Zki76**Nd9Gi95FQlEO&tasu&ck( z4wQGaCK;Y}a2%zfp~_HyMYxt^BMZSLz`U3cd@<fJkNCspD=zg6P<vFczRt10x<9v+ z(j4+8B41#YGg`LM;fy7C`iNz?*EiCFz&tv2Qw*X)(|`#Ew<L<EAYujX<DVb{pIT=Y z;dukqlFVlnN3pnh;eRrE{objzcEBo~lx4N=k1RI{W7Kk&Orem}o%6X4VgT?mc-2V1 zQD|InX{fzbIb^&e&9KR`(<BD+oy{io?J@b1vfUeWn531K@O@X7Ze?4qJ&20SrA-k* zA;#evA{KQ=pYjca7hu&GO=YXAf8^>fL`y1;L~Ye|O=3s|Ppur~M@tnTpwOTQqEneT zLOUk|TpLZi$rsS~VL^-k<@L?&kBoR(8I~BSt*Y?L#J4&g-%<0T&0owqg>&@r1hxRd z=?GslGJtkH^kjRwblE1FY*#fo*5D$B$E?A}Xw#ZY5yR~-Akm1Md@%7U5r*J?rL5Q4 zL$hn_fWbXxz4S-tMq;AO8A(&EVcbU~!TyQ3S~LE%;Ugk$rhF*s8dnCr<T(tbmZJ-~ zX|jzrSMw>C;!)udTC5B<ff;QNh*Wv2bY+|?AL0j4V>`v_kN9jlDeaTx;igcKTcg+? zg}-0V@8#AV6*Oj7zj$z1@N4^%5h!mzgRa+j7(P3}D^j`!eDC4j-=d=PZA#+#rw?gv zSp~n+vW$bDZmG%b)`>9VPdk%3%}vMPs(|y2sL<=m-v3H)50ec4-Zq_%;Z<NYKq&I; zs_GRe<a1xhSb95yfW<Iw-}gS9jMg3-Q;c_Oq6ryO9UltOQ!l?KD7<_F{W!IsD^^w4 z(Mkb7E{dv%CYX@z{6gTG{oEs(_C%IGF*2k?w$qb-J)gjJAw;(+up$fG@8+^xN0F9d z67>?-GjK^`FZ&6fLy)0>)b>!!fOW4H|K`^xZWB0-PC*>TffU5DJ5Jc2Cv+j6f#Yox zA{__pp4kBn3P!dxsCirhG1)Ikhhj>sdp-H#o3iw2ZXL_^G<)9J0nqEOKU$oOJW}_} zjpbN8UG?M&IrZgOKDrypHTd}Xls_P@ZyPuEWXaQf&7TD#FsC$;oM|Xo$o${o88V?B zmlwctU~EUT7f>9)La<~@W54mA@n-^hC(IS`&cbYxpnr|8l%9g|CqGCUK6I@pDhF<S z#XiQYBeE^uv;KwutCV2JZi!ko&Q#G}R-yOWw8sw?V&NIdN$%{S7<Q2KQ^9gU{5--} z#WA`!Vl@Pnz4%LD0!Xiv71{Pq_*PdYJo3xxWfu)QR>|+*Rm;pt&$X#1`aOl{iTbPg zQTnWA#4pzo0>H6%jt5@f;3T1I+Dg5h0-7zPTAYj#owr=E?)nyyu@D|=jL?$r`07S_ z=Eg16>arPjgX+*O-kvYNgX+5?2+Lc{tO&<OLP`Pn!ETeY&W)(N#1?v-r1fb`ZYay{ zi@!0iR|DKw-YyO64-4aApeT56EwdF=rgqBX+g37+Kz%jJ2gy-8;4iuI!%T$6KD=EV zWUB9?;cn$*ew|M{xXjzPKVkH?wfmTo1?jFA0H7&Yv*6E;;FpF5;?o@-i=M;30J($P zgGSV>j`r4g(#GW@rZjj(BF&r->y3Lxt}34zlNixb)TLiCtW~U!1)A#B)5tQ-7>1$c z5u(KuCMmxEpxX629e!xyh}Sb>DeWhb%B;Ga&2q&P-@0<QsCK`WAq^F5SxrBVlx4ZU zAvbn{byg{dR#RTNidW%U2X+l6n^dz%^KPE$*a_96TbYCsIuHE$q;!w^2>v@o#*GW; zNJvgk{x9%<+yEX0@~{sq0ErOIL;$$^XDEKlVR;sG(5`3p@*{s;f%PwFwN^3%v5i;p zInxK8V*gJ0j;NEL^^8>AZ5T6}(@!C`etSJLoUn`26GbAjXM^Zw9@gg~MiXt5R#e#w z$yXb8XJfn|T7omRUY3kxOpd{k6N!A^?o1i~N&!h^Vt)P@zLqMEH4G>$=R4e7$*jvp z6b7ccng;Xs^iHJaj8=VHiY2Cn4QYNSmXO0d@mpVKbVmR*lG)av(%?l><WQNi{jO(< znLT4vu%SEF#s(!FEWhUG$|ll=!)PZ~Jyz=5`eOZ|f=@&F-9}tFV7E_0CEl+?-Y>+& z^nN%WOH-}=jp=;J@F@vyLj~XLlFM$ZAo={zp<dBX|NI>)=1*f%x2?gxAe!le#-eE_ z3_Z8mPF$Xf{ESa_B;4++{#97I?VSCH8yMR=Ww!=mUgpe(<i;^4AI0vIa@=A_OIpQ~ z-nX~ua;;jD`v=$0CVo^16>^|3*D)_|Iuwtkh3HY|fUvRAsm|5G3h*tIBn#2R!?um& z7sTnbg_f0km2GB)9ReU7vp*<zp9Acg_()2Ph)`ZMJJ+ASEvVPY3mZ1@Fb&OTG|&n* z`#xI@W^Fc#XQ2F;BP_eOYH6H=fwGHRu*~7RQAM-l5Mm-iZ(rG)^wOwh1W`RMh}t*Y zfJF5UtctO!SI+OjGz`y@pdwLvZrozlCBm^l37s#Gi8hS0^3T&;S*OHBE*E1PXoHbu zd#<;=#j3%&D*@oDb7*4m=Q-0cg!7H?=}C&>O^Di95zbo&8n-DnfQ!pvRGM)_OFc+} zQZ`9SPMaj;bYdyLY?$^tEDvqRz2Uwe2A8wijayfen_9cftl_S+5VL}ddlI?iE_Fng z>ki!1Gfj`8wY@lA+wuCV9C)Z8Z68#YU9?ow5KU~5mO3%7-2N(!eqV<Z8ltDCQO=MZ z*<p=5zSQ)r)~ml2=eRcN2c8OTxL0o#?ru6=nE^qGcIS`*02{!iTgN?6Wppx7R$(Z2 z8bqA1Ks(^o3-^3xIfdKkP3YF)@uy1Z!XUYf-sdyc1SrP!<E}`qiPcxiD*NEYCg4*u z9~ZrROeZDopCSDQYhR-Z`84G;<2*IwB2urOWDJe;-TbKsl1)>%v*qc^O)69(zvS!^ z$yIQtRL^USDjYYIyTu&(j6ET0|8V<5$IdyJ<7dnZemL^R0QkuB3uSe!g}~4sQ*)hS z%h;UjtqqZ)Pkjx>4)M_eTBy0#5)H`a!h~CfNh~`YIcZ*x2N}&BmO3bgO<jBUh6mc2 zB$Ydo<B@qj!q#{jCfPQs{~i_{0+kL<Zd^Hh7TEFGo_Z=*($7a*_*veN*MfD)FWEt0 z{6!_4${&L4u^8waO?iDjo36v?z!A=E-`&Ul8T=7jiE%sc@Q`vluHg)wol@|v+*`#q zmqOo5XPaMz&WM7pSP9tA5;;XFU&GtyZRAYZaVgp;Vo)gC(Gx_TO$o%Ef!AZEtt+Le zgY+IPPH%71uj8ZU-L(<e>C}s#GISH<cCHKe=*-WaTy}R_u<*SmGWEg3kqmH0LHofw zF&ObAk%HjImN2bbW~<tvjie>76|ZQN0wIW$>;^r2vPq#Z8x38L?x=Xr=-pA6A%_Ku zjigK*^BRk*o>E1bp_WogSv(U^q342DUDF;_F4OI}dB^B5YR6mLDkBlsew$j22(EAN zV+F4j%p@Te3&7sMJ&cqip}pRrUuL3R-_7YhbM(97tBtb%)IFKJGEpecdwTalJ9YPc z6?Ydfx(ncT&;5wG3yDN;gdZ@so9KUGZZ#Ph(|<nvrN~*}uNHW4f(~zp-{Uq9sR)~r z>-Wfz2pIT#0Kpd(8twf`UxwmDV^vb%5{*V<X&Br2(ZS&3xN^-!(MIik-Z;>^FWl9! z+CP*FOIIS#`~-df$grCq3D&f4=u*d?Dqoe+n8(A!B0aw^8f`sI#-LOq5jnN9A<sco znP?a&3I_<n1w($#-+s3}Wqobhtj%<qF;Pn`;V|WUQ_!<JprFr!U1fFrc7WK^bbM<U z($OGiq>>mOytlJg*W1YP8~GNmDOrUCQ426qE<WVrc|p<yI9pXib8eqIzoglI?G<et z#l3;s#AwQ{^!9$g$Bh%wV6O%WEv(Q=>vj;Joeui|_mQ@GdG^FXuk;<Y%p#>|Xv4y; z&YZV)exG?uk!4ut<XWE$U4zPXNSk;aUDS1tiEUD#OCm-B>=WaHoHw2){v6pt04WpU z7<xA3mA8#KVOnRJLp@@YKm9e>yAFtH1KK~{<G7#ENrqm(YsRk({!UxnQfHafHHgM6 zKqox&vSeF+x)-a-Y;nMnT%KKO=QTfpup*wm&$RRxpedQ2AHmegVLn$Dw#VJxUE<sV zf%moGK<i_EnQ0Y5YLStU+%f-Gei1NA2Ka!=cOLwv6XMvz(2ZOf)4U?Iymd)NhA_#+ zh-PI_sKwmD&qLQZA<i0beR}%wo1UMJO{M*&pzePCNc>Q9BLDT;m|{rw%#}dX^8imJ zp!PJDtSu%<<Vi_{L%##+NdB~G8XEZ<dMnl$`Wg=vjR;N?SeN4_aX%t_UUSgf7LN7h z$scR<Rfec?SM(_0HpCf6YUx={bKYumo^f)OAU=f@keJ1WHxwXlPnpzjEIP`^+^_I8 zJt%w}b*`jd`@Cbwn$T%ndvAyN((n9qTo#mPpAm-IUwudOiQE)ddKF~*dMUtL@sRn1 zwI2a}66gwM+7+esfabd1#;!^1z{$Utl_@V*$gMgcCy*DVub9c*+rKg7)B1AzVF#t7 zPl|olV2Dl8dTEf`kM{ZE{%@cLxPP@$$UZL&CzJj9?NrOft2Tj%V0RcxW1gi8_{em0 z*vynOH8TlM-q#^I72oUZ3VqQN=)3B`Yy>pAQOocfxy5`;i++vjjVCf864KrS1NUDa zx`=Zs2Do#_W4ZZ;v8~TDc#LYXfaV4E18XQnY7F8TxhxeBg)h{-Z_r2mPu^DD8D#Ef zmKH2qVnW_~)ycXmsbgr1<n5QqW)2w`XLCrK#q$iqBBSs}QzR;)+r1$<K{1T)L4xx= zKXd}o8iS3|(kxRy4TG9PiYUA~J<}78_BLnXA0l(JY8ZHADzG#U$|XH$%W#id)wVWw zTPmEQ#E+p4Sr@X>s}YOi-7;omOQ_sbB~~xX1bxq!ZCTCvKE67=Kswyv+yrVgwRJgu z-iOwjXw4;r|8mY!Ho5hW2ppdqD2(jKmOU_I{(ZhKl*!Gj>`Ha{lh=(u;Sb7VaF`&f z4YRiotzP=W!u{{y@X$>CWhDW$t^Wb1embGQ6h11w2<`)wulql&CPI;(^Y0^HdUBLZ z48#C{{DIWOLNwFk{8bM=sB1AF)QkvR1{|74=YQFUhc@#c=>#DB8Hf?83<Q4-|Ls)& z+t3B{V8{e~ob4~PBFY(vQDO=IdM_{#{C)Ew133!r69f(;%iqcVEzt)Q{zrt#5Som1 zfXB%m7YyPZBMAx@1wx&Y5K+X4WBjOR`X^NQ|F|L{ty3Z)S-!GRb9?3D&TZ!6!u7_< zQB4sUg$U{Y{|V7un?~|aeDFGc7!Ql_AEBT|NYD@?SeX73nTSaPLHI*9emua)e;b}s OBQ%*9F=}WZo&O(pJ;TNT diff --git a/unittests/table_json_conversion/model_multiple_refs.yml b/unittests/table_json_conversion/model_multiple_refs.yml index c95a97ac..576ff1d5 100644 --- a/unittests/table_json_conversion/model_multiple_refs.yml +++ b/unittests/table_json_conversion/model_multiple_refs.yml @@ -4,7 +4,6 @@ Person: datatype: TEXT email: datatype: TEXT - Organisation: Training: recommended_properties: date: @@ -21,6 +20,8 @@ Training: datatype: Person responsible: datatype: Person + Organisation: + datatype: LIST<Organisation> supervisor_inherit: inherit_from_suggested: - Person @@ -31,3 +32,5 @@ Organisation: recommended_properties: Country: datatype: TEXT + Person: + datatype: LIST<Person> diff --git a/unittests/table_json_conversion/multiple_refs.xlsx b/unittests/table_json_conversion/multiple_refs.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..34bea9b9b9d29c9308ec5cff496c91e7ecaa45c9 GIT binary patch literal 12238 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 Zf5NlMa*$B3S3!Qct-Q>nMf%rw{{;qPXUPBn literal 0 HcmV?d00001 diff --git a/unittests/table_json_conversion/schema_multiple_refs.json b/unittests/table_json_conversion/schema_multiple_refs.json index 5678f72c..7acf5e0d 100644 --- a/unittests/table_json_conversion/schema_multiple_refs.json +++ b/unittests/table_json_conversion/schema_multiple_refs.json @@ -45,21 +45,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } } } @@ -81,21 +66,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } } } @@ -115,21 +85,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } } }, @@ -148,19 +103,43 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" + } + } + }, + "Organisation": { + "type": "array", + "items": { + "type": "object", + "required": [], + "additionalProperties": false, + "title": "Organisation", + "properties": { + "name": { + "type": "string", + "description": "The name of the Record to be created" + }, + "Country": { + "type": "string" + }, + "Person": { + "type": "array", + "items": { + "type": "object", + "required": [], + "additionalProperties": false, + "title": "Person", + "properties": { + "name": { + "type": "string", + "description": "The name of the Record to be created" + }, + "full_name": { + "type": "string" + }, + "email": { + "type": "string" + } + } } } } @@ -181,21 +160,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } } }, @@ -214,21 +178,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } } } @@ -250,21 +199,6 @@ }, "email": { "type": "string" - }, - "Organisation": { - "type": "object", - "required": [], - "additionalProperties": false, - "title": "Organisation", - "properties": { - "name": { - "type": "string", - "description": "The name of the Record to be created" - }, - "Country": { - "type": "string" - } - } } }, "$schema": "https://json-schema.org/draft/2020-12/schema" diff --git a/unittests/table_json_conversion/test_table_template_generator.py b/unittests/table_json_conversion/test_table_template_generator.py index af3b3a2d..8115c187 100644 --- a/unittests/table_json_conversion/test_table_template_generator.py +++ b/unittests/table_json_conversion/test_table_template_generator.py @@ -22,6 +22,7 @@ import json import os import tempfile +from typing import Tuple import pytest from caosadvancedtools.table_json_conversion.table_generator import ( @@ -37,6 +38,47 @@ def rfp(*pathcomponents): return os.path.join(os.path.dirname(__file__), *pathcomponents) +def _compare_generated_to_known_good(schema_file: str, known_good: str, foreign_keys: dict = None, + outfile: str = None) -> Tuple: + """Generate an XLSX from the schema, then compare to known good output. + +Returns +------- +out: tuple + The generated and known good workbook objects. + """ + generator = XLSXTemplateGenerator() + if foreign_keys is None: + foreign_keys = {} + with open(schema_file, encoding="utf-8") as schema_input: + schema = json.load(schema_input) + if outfile is None: + outpath = os.path.join(tempfile.mkdtemp(), 'generated.xlsx') + else: + outpath = outfile + assert not os.path.exists(outpath) + generator.generate(schema=schema, + foreign_keys=foreign_keys, + filepath=outpath) + assert os.path.exists(outpath) + generated = load_workbook(outpath) # workbook can be read + good = load_workbook(known_good) + assert generated.sheetnames == good.sheetnames + for sheetname in good.sheetnames: + gen_sheet = generated[sheetname] + good_sheet = good[sheetname] + for irow, (erow, grow) in enumerate(zip(good_sheet.iter_rows(), gen_sheet.iter_rows())): + assert (good_sheet.row_dimensions[irow].hidden + == gen_sheet.row_dimensions[irow].hidden), f"row: {sheetname}, {irow}" + for icol, (ecol, gcol) in enumerate(zip(erow, grow)): + assert (good_sheet.column_dimensions[ecol.column_letter].hidden + == gen_sheet.column_dimensions[ecol.column_letter].hidden), ( + f"col: {sheetname}, {icol}") + cell = gen_sheet.cell(irow+1, icol+1) + assert ecol.value == gcol.value, f"Sheet: {sheetname}, cell: {cell.coordinate}" + return generated, good + + def test_generate_sheets_from_schema(): # trivial case; we do not support this schema = {} @@ -133,8 +175,8 @@ def test_generate_sheets_from_schema(): 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']) + assert cdef['Training.date'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'date']) + assert cdef['Training.url'] == (ColumnType.FOREIGN, "see sheet 'Training'", ["Training", 'url']) def test_get_foreign_keys(): @@ -163,29 +205,10 @@ def test_get_max_path_length(): def test_template_generator(): - generator = XLSXTemplateGenerator() - with open(rfp("model_schema.json")) as sfi: - 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) - assert os.path.exists(path) - generated = load_workbook(path) # workbook can be read - example = load_workbook(rfp("example_template.xlsx")) - assert generated.sheetnames == example.sheetnames - for sheetname in example.sheetnames: - gen_sheet = generated[sheetname] - ex_sheet = example[sheetname] - for irow, (erow, grow) in enumerate(zip(ex_sheet.iter_rows(), gen_sheet.iter_rows())): - assert ex_sheet.row_dimensions[irow].hidden == gen_sheet.row_dimensions[irow].hidden - for icol, (ecol, gcol) in enumerate(zip(erow, grow)): - assert (ex_sheet.column_dimensions[ecol.column_letter].hidden - == gen_sheet.column_dimensions[ecol.column_letter].hidden) - cell = gen_sheet.cell(irow+1, icol+1) - assert ecol.value == gcol.value, f"Sheet: {sheetname}, cell: {cell.coordinate}" - + generated, _ = _compare_generated_to_known_good( + schema_file=rfp("model_schema.json"), known_good=rfp("example_template.xlsx"), + foreign_keys={'Training': {"__this__": ['date', 'url']}}, + outfile=None) # test some hidden ws = generated.active assert ws.row_dimensions[1].hidden is True @@ -212,3 +235,11 @@ def test_template_generator(): # TODO test escaping of values # TODO finish enum example + + +def test_model_with_multiple_refs(): + _compare_generated_to_known_good( + schema_file=rfp("schema_multiple_refs.json"), known_good=rfp("multiple_refs.xlsx"), + foreign_keys={'Training': {"__this__": ['date', 'url'], + "Organisation": ["name"]}}, + outfile=None) -- GitLab