From 0dadcca65990c24b8e7d2b311aef361395d12544 Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Thu, 7 Mar 2024 15:33:12 +0100 Subject: [PATCH] WIP: Filling XLSX --- .../table_json_conversion/fill_xlsx.py | 32 ++++------- unittests/table_json_conversion/__init__.py | 0 .../table_json_conversion/example_single.json | 32 +++++++++++ .../example_single_data.xlsx | Bin 0 -> 8946 bytes .../table_json_conversion/test_fill_xlsx.py | 34 ++++++++++-- .../test_table_template_generator.py | 37 +++++-------- unittests/table_json_conversion/utils.py | 52 ++++++++++++++++++ 7 files changed, 137 insertions(+), 50 deletions(-) create mode 100644 unittests/table_json_conversion/__init__.py create mode 100644 unittests/table_json_conversion/example_single.json create mode 100644 unittests/table_json_conversion/example_single_data.xlsx create mode 100644 unittests/table_json_conversion/utils.py diff --git a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py index b0f8d2df..8c9bba80 100644 --- a/src/caosadvancedtools/table_json_conversion/fill_xlsx.py +++ b/src/caosadvancedtools/table_json_conversion/fill_xlsx.py @@ -121,10 +121,16 @@ def _next_row_index(sheet: Worksheet) -> int: class TemplateFiller: + """Class to fill XLSX templates. Has an index for all relevant columns.""" + def __init__(self, workbook: Workbook): self._workbook = workbook self._create_index() - self._context: Optional[dict] = None + self._context: Optional[dict[str, Any]] = None + + @property + def workbook(self): + return self._workbook def fill_data(self, data: dict): """Fill the data into the workbook.""" @@ -194,6 +200,7 @@ out: union[dict, None] """ if current_path is None: current_path = [] + assert self._context is not None insertables: Dict[str, Any] = {} for name, content in data.items(): path = current_path + [name] @@ -248,7 +255,6 @@ out: union[dict, None] insert_row = _next_row_index(sheet) sheet.cell(row=insert_row+1, column=col_index+1, value=value) - # self._handle_simple_data(data=content, current_path=path) # Insert foreign keys if insert_row is not None and sheet is not None and _is_exploded_sheet(sheet): @@ -284,27 +290,9 @@ result: str data = json.load(data) else: raise ValueError(f"I don't know how to handle the datatype of `data`: {type(data)}") + assert isinstance(data, dict) + result_wb = load_workbook(template) template_filler = TemplateFiller(result_wb) - # For each top level key in the json we iterate the values (if it is an array). Those are the - # root elements that belong to a particular sheet. - # After treating a root element, the row index for the corresponding sheet needs to be - # increased - # When we finished treating an object that goes into a lower ranked sheet (see below), we - # increase the row index of that sheet. - # - # We can generate a hierarchy of sheets in the beginning (using the paths). The lower sheets - # are for objects referenced by objects in higher ranked sheets. - # We can detect the sheet corresponding to a root element by looking at the first path element: - # The first path element must be the root element every where. - # Suggestion: - # row indices: Dict[str, int] string is the sheet name - # sheet_hirarchy: List[Tuple[str]] elements are sheet names template_filler.fill_data(data=data) - - # Question: - # We can create an internal representation where we assign as sheet_names the same names that - # are used in table generator. Or should we create another special row that contains this - # somehow? - result_wb.save(result) diff --git a/unittests/table_json_conversion/__init__.py b/unittests/table_json_conversion/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unittests/table_json_conversion/example_single.json b/unittests/table_json_conversion/example_single.json new file mode 100644 index 00000000..9997f17e --- /dev/null +++ b/unittests/table_json_conversion/example_single.json @@ -0,0 +1,32 @@ +{ + "Training": { + "date": "2023-01-01", + "url": "www.indiscale.com", + "coach": [ + { + "family_name": "Sky", + "given_name": "Max", + "Organisation": "ECB" + }, + { + "family_name": "Sky", + "given_name": "Min", + "Organisation": "ECB" + } + ], + "supervisor": { + "family_name": "Steve", + "given_name": "Stevie", + "Organisation": "IMF" + }, + "duration": 1.0, + "participants": 1, + "subjects": ["Math", "Physics"], + "remote": false + }, + "Person": { + "family_name": "Steve", + "given_name": "Stevie", + "Organisation": "IMF" + } +} diff --git a/unittests/table_json_conversion/example_single_data.xlsx b/unittests/table_json_conversion/example_single_data.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..662a636603944a91cbaeaea8cc0c3bbab12f2f50 GIT binary patch literal 8946 zcmbVxWmp{Bvi9Ka7GQ994Gtl=yK8V~a336kyK8U)2@o6t1cJLmfZ$GW2@V%>?ss-h z?%DgtSN-%@&#GQsRqL&HRVm3rL1P2p;o$)gE*4sV-vsIDv!RojtuqVr^Xtci4uwuu z<j^DU2>Q#Hi^A}E#h>M*K9g}0w^P~^)`b^z1)csB!@@+4uI`L+^!0l%E?SbaXIx^X zmNf`h*A9nlr8WVVbPYZ@da`g0=lk|x@JOnFMCqMk6=lwcR$mVkdb`BQAnu&W%Z@{A zo9vqc1cl!^>8rc5KbVGX1#9m|VLDo9<bU&nZV8sRvw3MRO#<{1;#kupPb72elaVX% zDdB0sL@^6R^jtCnHw`WyC86RYvr5Thq>^nN_Tf%8#E{)54eq9;VKp~xIXM`g!w$D# z2D4hYc*|3OrQ{BCafmn!Wg>NQ?gqE8lw{%HLozrs4xfxL0to;p{jHhMp8nxs%i<1l zvM~mMY?wXlY@<~cV&+-V!AF{6zHjv!vC)mhp~fqw2CefxkH&D2F~<<inFoBlIm+fS zm7R8*h6@u+UFWqJx!Y1hIKr=8mFc0}hwFmsR~y-6rZ#M_0*MHiydQe6glX&qD{7B> zv?W<|nXclEc3TuKZDl8T2JVz8E!{0ApfpD;Vz53bb8mb9a_oB&pdQl~8`|VczFq>Z zQSl?3C=QH8mCC+SKrK@k>}w_kkc<IgbsC;tyNPUHf$M#`29KKv48_Bm>%RAaS<(TT zYs54gT`8--n5^qv0vO6X9s?)PgSQS{A;?JclrXqjD}3}#6@q{hPln<3)*d=2d5c9x zhn`E0fy~(WV)c{&)tdKK*L)Jbt|)ysu!M%$*)<BGV#mi-sCgZ0)Gizeh(_JX?1to{ zIiDu?)k;i2w&txwX<8JdEkh&MjNqCQExRYI4;mXm$^#BXL(Sn2X__5D`VXcY^gV~C z#pNgA-BTRg0$4nsavbG$J_}f*SAK3F;Eu)pycfH)LvmOCWfa?N6K7xrB`I%xI{x)I zezU*sm{x9SkQmul34D}pnq-PDOkVxdO5CRJnxu^4t2eJ#HUbmxEkE2E+%aP3JRaAs zq=(LQkBoKkTM{Q89rcd$q(>DP)FQ*Rvg*Zpk-&B;v*?%5Rj_DNO`gMLEaZ(Gr`E0$ zmMe$C(Q;XEexsxQfd%!+Rx`}(;0xjKQB>0v!gEwDV=mL+KSk69)IUWP%D<w@*~Qb= z%=tN{4z!gkGr6#Rmn(&RzbzmlAfVVJ(3=eKz0W9H(zTnIf`dbT{gR7AA@h88v`3iq zr0GX>IhQ7qrslWpGxw40b(58w5yL~IJs-;a0bbpA;~Z$b!QBG=+V#RmhqJGS;#8%F zFy(kDkX%S{yqfOp4f>&|+;v3?3k0gH1L{>$UNzg}fTRP;Em|n$$msB3Qpy<E8|Yy= zq>H3sTzhly8evB8bw=hYwD78(jN43{0(!Fh*%}P7ikQ+!dmAG?@(b@oUSOn=&+>Wj z0q^b&`I!yx+Pp4iY7GFP7kd}_=L5BM77g+BStVISI0|uWjN8)|+Z4t0`I<-_Fl_h9 zebM#Uc)Wry*0l)Nm~fK^GV`WiN0lQ1y);EDL45MOMfw>&rpSYy;YE~zAJu!U<}cr> zFd?g&nSGyHe=u^PJ~raa@#W21<5aATU|dT=k~GbP(V3m4AG7D$7RH`_@D}=Pc_BaS z)G40NCC@*KVJ)waupY%Bx15ZWWUPZRmF~Nht7W4%LML5>NO5Xgew2`^gOZwnS2pfc zi@x4uQ+mvncdl^M{%T^q_#|l(?A2zXwWv&}YXi#YQJw5ClSZ?t$d>v<5xrXC)z@B| z@7aUs#C{yTlF(J{>p!)%32cDMX2QF+*wjgtow3!rZ;AK(AaCB*7jMe4>=Yi2SG1eh zzS+v|COxFB0;i!>hr@g8FgoiASI(K7jr{iX3QAfhenCKaYDvmHH^+2<f6L0nDu*Du zBz!OPHr;w~Wj73exeH*-yrj#`_WzF5u=K0h^Wv<i3Yp=JvZ<&_Y5A+@?#BC3_cONT z+n8K~8~iVdqsf;Scapn`2bm8HMJQLIJCA8~y$jTeEH;z69|d(jMw8Ud<oHf}-$EJZ zzuaBIh1k<30vj|4f7x|boF7~;PgPClm~<XRz+gfA9z!Az2TelB<L7n6ufj?SqcNcX z|8-5Y*HdUkj(82(co?>2n`?=X+_(;whzh7QbiK-!9#J_8nAT@m%%N1C#F;a9;M<u_ zHP0pAs-{5iJv*-?8h?Qervh-SU5puOzxG%{7UML(gmpA0m0UQG+SnzbjmgF;8x)#k zJ-;XQXSY`H8O0V;S_*7GBL&5A4g0U{NIf#o9$R<foH`H*+uSIH2y=umU}Vr`*+hRk za}(%#WoSnm)3NXPZk{a-W&EYt_EnpKx)fn;kP#&^nb>T8t3*(H1t0QxEExPwf9=-D zQj)W`qWzLg>`3B80JbFx`r7Of-Q~vhLH(%f<<;ZB=JGQJbY&U<`k&VSIq;t_;Kg5= z#o5x#%*C1I*Bje29JFf6gBEzuTaPNyU3RQ&<|3GA-hUvsp;>^m0(R}<ej{X3#>I$G zlOBjU`{6DH)^S>YheQdt02CT-@f4M2dDz}g($Qt4T(vcig%w@+%D91Hl$m~if3mgS zWG5Y>6-;a4VSe<f_7$Ap6+d&1Y2c@7Qiwzjer5X4E<k*09&x93j-fX+Tjb)1DaPx4 za&NHHBUSbqJM*>+!&pD}InXU71og5ZoCLo0j7X<XfR~GhykRFx|4=O`2^zpds14F! z5MqjmG%gw8Cb&E;qQura)j|=;NtwFQH{ij80~fFAh80N(Y|cqaWxaN`5ec6sjB#7Z z+_?N2fuOGCvyL%DC81{gZEy^>vg#%$9emMuM5biNA8?d}2j?p$EF$P8yW(MP7|mQM z7|rVM2fx%m1CpioafiBdiZD$cfqulLY^FjMRSR!H7CK(cnq(n`&ie4P=gSN1a>zhi z%r_p9Ch8?>maSj0-y%xFnWZ%4E1A_P&SuTvlLHH~=!YTp9F@BpggwnUKGK9_2~57+ zN>f=Zu?r!%#X<BiS@2pF&<_a<lqp!Ps%zs&buZ8v)veub8ZY`j?cSMjMng2=`PNIO z808}<^gLU&x{3*UoQZkP<G|JNJ(V9W)=5UdZr8!`w5|P6e1LYUj6dQlsY1+^mjoV5 z4xA_7r9WatZ6eL&ch>X!^WVbsD2vN{@+3QFS~5oT5G^GNLKOEKfe2`aQ!cumI#o&P zpMmbwqt8hhiol?vj9@L-I6y2mjmQb0^uaQ3E1J(l2$*s8OMH(}N~`z1Rw}O@&&L8G zh-nWOXu~dl`xc1kK-J6Qap4t|;>rg8Q1~8URwm3oDDTMjWh>^1CJ7s#*pMPQE)B+P zB^gkuIHeb?>G_?QsewX~#BnBmo6^`3)A62QO*p5<J$ZY-PypQ3_;iVFw{KF?gUItH zY}e7V2b8kCf^q!$Cd#{Ig#YFoLmzw-Rv`oy)65lG!@H4N(#-9be)=g=0fh5)w|4FR zi$+X~ron;PAwIGFEy9dv5_61j^JoVJdIJZ2$;C(?w2^f>K8Y388@=}1ml76}X<7Er zLmKxo>wzr}#pu~VyxsX1kkD^GN_|^)K<xdA5{?q^bxJF5^^KQMcG5$sc3=Q2DtJxd z$18NdVVqL1Ee+d4xiq?Sg^o>$Tcjr}F;%CrxWNjMH^e~QoswLz+Wf`$&0PY#BlUGg zh5}_~8w(>{z5|uCd5q;xz0fsOw1Bt5=;-Z@7wOrKdC=r5VKdkvikgf!@#yNA>PRh~ zgD*l?Vz&gQ>kX}KdP!4ceJ0IjiMq)tj#+1;)i^B4$wuKTMtt&SqR0Cpq=21SY3$d8 zGU^`q;<F1|eQ-jt`CkuVNt80c8JVMKubeA{1OvxYW{A>u;Bv@+ZbL2kN8)xDcSP$A z`WRj-BubVre!DZ5`6v^AxwOHQc&+eRRcq#P0Ok?m8JIq>sv4+50|2O~{~y4_{tKA2 zb=~KA(0!K;SJ&SP*`0($CgSc`kp&ml?pjS8@ZM6};=35@8fb@7fAN((g`r?@t=CO} zYoFM@Hg8MEFlYC@q=<<kGRM#B?{e{~w_52cfsp3HI$Ky=yGXY(1yVsoSO(H;L5&Q# zPrmB$9yqLF>iG_UlICJ-$SMIbGMqK=NNC3v^8{qaRljg-G$mw^#g_<Zp^IUht(Vk@ zk}}4{U8>st_RdJkN1())cs&?T;zbNP++_`ubj`qg?27hJ$k3nY)|-l*-JYLR7o1_4 zyxC6hhb{ZOg`#C==aqAGw<R5W9-M)Z7@tQcXW#IItjti7-@U%EROp?!!ZQ!1TuTGz zLu^oqDTf-dh!grmUN@*6p7Gzn;Fj6h@YN4wmaO~x2a_MbcCL2$9JOeBLXtuFVYG}A z<VM@dULqZ{#42f|Cq|}5K{F)0(b{BW!TU0CA0;7+4oP3$bixhWT?Qk;rF-lUB5IJi zVF`|;*mnkU9)0fjgr;mjJIRo!3jn;Ojcs&**r6?-abG#<k_*nlNGp8*E^U<-(<md9 zrAqUSA17<Hq|CZ5i8yJ3$wKsx(Ni~9^}Z$%G|trOq|f>IGf7L4R7SJ7(h17&o~o1t zCFfr`(LWqItfJ5^H8R`3toAb%iLljjMa;-8pJFua7A}*e5)L_{T+uxq+>Mr~=KwZ! z>d^EJU?L40EgZZyc_qSCmaO8Z1K;|U9|&Q<H>C#^N-p%HFJSkD*ziVq9Jh!SHKrE9 z#aYy3<M{`v@f2tXg|c$$pu(teGzZKjY4T-`2T~4bo!OH)D6%m>^C@VrcP_Frnxb>= z-XjRKw^U|^qPz2gN6<+@2~O{@W`YK;4x%~}97|1HytirKCm84?`SkPn70@@er9F#F z_PxL4p0*SU%qn}eJ|G0U?w7IHq3q5{e|6cbSqblK7ZiUtZMCu!168u@yn&#f<<l(+ zwPwj@?gC;(U`mS=t)}MB`qKOM4MfGrIt^-%h6{QvDb})0`bMxfht{I}!KXT`!{jhD zyI|+2G*|Mi>@Xyhd_QDu7lNjJF-Cq2&Frur3SGSlU3(VrNQikAu$SWH9d=Ed^wVP! zJWxfzX0kK;K5=f-ag?V0P{z+>nu~Ei?E8A{`}RuW3YvGmSn8ki|HzM48qAM&2$j*c zB&8@~<V07bLqge*ql>ift0r`sMroQ69!ahqvn&U8FraLNuNxodo3ZWCM~V~XxmgnC zsdZOJD@Cl8MIWD^BIh!Vj&rnE?s3>&7ftoP<r1;W<jSAsA_$xRb=rlMo1UpaDYq4- z5`=kWSd+?#%@Vjz*N<x{FGxUn=G#fI=;9TOG+R<6U0K)#ms5f(0!pPH@kirDmL64i z-otH`P-b}AE#pLQ5rmOXh!aj|Mr>50IAZrbDa|E#iC*Mckr^j;`|Vq}7EV~3*oWkj z3vzvpbvW?>sjnav-VM(B7va16Mu-i(aw}8Gx_WpQDeR|GSTXtd3Owe~%aZ&<JSPi; z8f*%8!QVmwj+EOjZlnBXp_|8t4*gd2y}hEd<&W=DW|BltcHHWncsy9qRR#!WH3SES zR4K&i%o{c^@V+<`PBzC?MMb86-xe?!To^Xq*N*NO81y<cBlx<HlK35Bk;Yv&>$^Xs z(qxoBz$r<9O?7}rG*}cK<Me2mrsI-n^@4I5{)M$16BsDYK@gelLJb>>@L?0*pma&S zRb$&Y2a^^GLpnLQGeS`{(2OYHMTi~}T>?fNYp@uV9wyMIECm#}V(f0Yx|;_3nk_23 zO~moWjoQ%gK2i1IXG|9lhtPWKNpcVE^%=v`28MBWaTjj|+46Gi#~>o3>f<duc|>i* z&xVDI6W_jBOCY9Rydo%mgnWi{;pytVXcz$C!xNqS`zprYa7~V1kgg@Ky8er6I$X1s zWb-Ve$|cZIlcm2m_J=etu?9sj_0ro8QuHGn|7f7{>xxG<x6tN^E4}z$dF#rsIdgs{ z3e<k`QC|Php=)z}2v+P3<=n_ViF<i0*1{SjmCzHSTl}Qh`ni2gGAU?wDbS^*wL-ee z!H-QY=n6ax;gM(&Rn2Nw5Y~=@W}KT@rR(g|MC*>9JaX$*cmnioVud*ypD|KWVgnVH zNJnxf8e~Z+;VS`3I+j+!tIE@Dj@q|sgC5po%xBpHh9JwU$naj;l7Jl#PG%8~8UaUz znA51%S!i4ex7;F<4oAd(J1McQf{5`P#pE*^a~Mj@G;oQ~6qPVWZ&hkOQ(O0%WZBPd zqE=YZrh}mpg_C2SJ*+sTltp8nv^k5`$qO-Z>=l;4%a)&uYh;U+q-~5MEiNTdHWGJ8 z32&+pQ*e3+n+?Zy7~mnY)t9dbgN%y;;_cuu3|55Im|^C>ufcmm-b!+6rib}&{$yJk zD?MFV2bjgju>nZTiokGWqx)ie_yLL;GBGeQ<mn}Lo|v~N@I9gnUiqAD7fR6d7tM?F zH!6Y^x<xs7JMrc+sm7W)p(sU{6O{LEX31&<D$CgYiK=2oy6_+P^~Ba^X0HoyACon^ z1hM7#D*RY%YE+n@>X?%NIKG&M!RVTm9K`P41&)4dyU=FW5k)&yi%0GKzJX3jDI&HR zNZ<f>9>^BNQhTUSfmhqK{@2)In|oi9xJBqH)yD=K-#KDv8|24b2`LPyXTMhN*?|ZQ zR!k^WWF}S*5&l8{B_4Cfkb{fT>m3j~>7II^f8h)Tsm%y#`OO%wr+1HX2aoko)ugv~ zk7Eaqy|D}bV^EoW$4=Y3vP5p%2a{On3`P7!UAfoJODzoyWu2dR4;O)Y#03^NbA2c( zDW<2}VGIf$DM7B>M+({5$d$rAY#2dHY^LL=+8O95j!O|*b>uTFWWcco%%;Z*^2_L| zn~Xl{eC{bR`?rh>WZMUE>inkn<Qq~u9iI^eEpg_>&LuL&P~tvO*COCsiv4zd_364T zmX{UzxdtbO4wVMA2t(JrSmW?%l))3Of*z0dIM~-REThvXLN5kTYb9*`cW9P2#?`qn zng^&wH0Q(w3Qn)U>ckCYw&bukj-a=U@_o|I4~Kq(k4>o&+t62Q8NEuXZ^i+<<2qfz z$hD|iFNO6!N)wPKnkQ2qeV8cJFM<uLNI+myv-Y<%m_b3;rz-iWkj9=6R<5?lqD6*W zOF2gjyK-Q3HO<5ttXcUXDV`<+9-GMl63hDl#{F$NX-?DXM&g}&S3PtX#Y=K`{=+UJ z9OpA5grK74Cj5)VJ+$iYN)Rt?<`Jg#d>zo0=&fx^n#KE6()3k_?EA4=L~{L|>Ueoc zqmXz}49!m@1nsxEQ8xiPjOF2z^@R%z#f{)*#Y2%Sy#UBDnJ4hv!?iFkNkRyPmmA20 z_q?<Sf8?OdZ|<t{3AM(9B_0|^A{hKZ$}IT+ZHwQ_AlO{pU9Q8Z3JH>APppQAi<DFi zRL}H((WJ*fm9Uve>Mueif&nz9AJ%O1Yva$#x=y<B^+jWU^j|xl6uEyqW++&jBnoG1 zU_M!D&SR5#Xz5sB6k%LeYcyC^$+b}3UXg4bD!7(URZ01NRClV&lgW1>XQaJ&GW;(- zd@-bgbos=GX|VqZJPH1_QU6!5{P#leufln2;=c>$eeN0cgwKU@(n9@S4}V(MOlv#L zIf(iOLv(mPt1=!re(n18$46AZvI`?%7aZp05*=+d5Snj2bIBEP$&JN5wyFYe8W)ae z6008kCf**%P{kHXLpP@dd}$%{PBwF9ved--ed6oP)<dNf&j(VgGKh=x;%}UYmXp|| zZoU4G<3H^d2z`ETfyMAxw4`EsA$aMm_gLw#4r>abPOHJ5^0$hL-t}Q8u`BLLD{V4t za5XJ#a=dUg&1^$Xs9O(Zbe}S+JfoAyU!e@T+(FfpZp=xRDn4f!Cps<0)h9E&`p2r~ zlNFz9<<6EyPG+X6E>2eV7Qc#{$;llG>#XRZM*<<6ol8<8uPtG$l-}>y7m1W(qI!$; zq;o-i%D&)X0(%3sQgX)B20Gl@KJ~6YHk_4fPKKd)h*-QmrR*;Rl<SAM2_~U!Zd{dM zV<&x7TW3VxM3pkYk>piIXPcT(njx#jvyy3sEueK|`gUcp*`iG5cF$Kd6I50T3tub^ zr|p(Q5Tb#2z(=@NU9zp>g8S&k`tX8x&@>(k_xhF|Z;GA>FLo0B3Z>6jAXhNO50~4M zAVT1v1GI8`V#%dbJ=R@%Hw<OcpJkS(f&V6e!K`Xdu=@I?ef#N2lOaQ7AtXCi?n84X zizgMc4LX?o>qoJ$11igH28=8Z$h`GkxKa-boHv~d3X0@t*-Hvq-)+4NPwRga_!Nk1 zIpA=QLh>(H@$BW~yoNLWYG+HzJ1Sd$<`Ymm05|oGeo(F_$Z^QnXEVDBrwz}!H1($l zTJ5yKMhEH-yUD@zNv_r03QV{2PNyDYxVe>c9{z9=q-FaCgn2~ia#+j*WQIMBkQ*oE zBlwq!Xo((Qx^zRktxH_Mar{{IXAhE9Oz@;j%N!@7burAZXs&in4l7fMYDy~^56yjs zGtn+<(|g=oJfNN<p*6pjwGRRSfPw#~C4=!438o+uB`1)BGm8nx$?TbEgs6-wd}YN3 zUoxb$feyGzit#CwDTSnr$tBix?N|z$UFCI!AC~Nc0z}pp1TV94mL?Z`?({V~2%?gs z3G#TzX#w4YWa_*kEN508L6ASvFh5vhM~&e!65!$S&L(lg=ExOv@E~v<OAM$~NLub@ z6~EAJ3(lD9V@RUGi+-PeHkPEt@z5>PjzFf_2-^$ojNA~KM+_VvQ915Vsg#?>hmXk% zBuJ{^i81yW<72H8^nPa&lT_!Ti(*ma?WE+V%L~t)DPJqi|33Fs52&Vho7tLH{Z<XA z6n_Mp_Z{F&3JDUv{qb|i#)~^$Q8_+tgPl@3t?-PpSX%<YbMz$l?4yw(cd_Lh)NvhP zFnVg_sEWq#r9ed*z2-v)62$68TO#sDw`80e`>oWrI8ytD?qE6u_2aP@bt+~tia4Te zUbDB{ZwL~8cxH`r=1~eFHgfN5u~wxVC^A+zes*oQga3SU1$}rE{G>mzBCxHMMX}A| z<d@HsXbP5HpM19WkDV^`-+gA};PC9S>^OPQ*XOR_EslCuo&3%V(j-{B3^$Q^RHFHh z1DJU1o3hdo178dQC_JMyw{;iae0RM#n|Xx82IgSp83GZjzDk?9qukgo4^Yt?uW2|P zFmmp|V|<*0x`!VTl|nR*N8sYJ%uO&f0(KK4$Ew>eeBae)71(6oulnIlwK5&v;V<7v zZ5m@F_lBx(!Z2V*M2~2~FK;4z670A@4Bld!4?M}Bd4s(?F+)|MYv#{HT;uz3r{+=z zA*Y(kwxA8FCD(mLT)a$MU6E@5)#S1+%l%B-k$Y^D#8;S7u5ZIkNhjrH_hl89r}s&Q zd@g;$y1;RXvQJI&{V#6qZ|voI^JJCGC)@o!x5oP2ZYHkIE+D&KrW^T$_!R9StBWP@ za|>#4@e{R?Jp5{xaZB|O&Y(W|NU%3W;9<;L5+Q|2!F$mU=yMp>O)Ly_045Yv8Ya+v z5s!37|H>QU;CGv_jGx_R^(bJ%`XM42No{ybu&s-}jjW)g@|rMHh=Wve991}DM!u%+ zuR$d8sZA@-g<xHi`|w_$?7h5~MKxVMx>^E}dD|8ES}fo_ui@1hfPd=KtWTcKe||1q zQINfhnZ1jlnx})Av;OmQ4Xf)ac9K4Ksw_QThS*W}q`7$?pO(sIjl!<hb|=gF4<9Te zWiv<4>{UyE$v3l(Bv3gsE6lP#wDaYu$4GdDVJgWv=M4Y_hd>j~oY~0L*FFBSB9%_O zWi8xz!8{rPg{U@A^lFH9R2Qq0DP2nS@2KKV96Th_G>DTF!5hMf`-q6mAHYtns0+G_ z0LlK%dKQ_@FalL12{3J<7S%@{q`Dt~3hc)fX0UT8RQ0Z7GV9@n4NSoR8l*^%spA)- z6We!06WEYj%)ka5U;=*KL(0IIFtG_{p!djRacxC^WPK#fN4&<{Z@N=1%J8syHATqi z`%y3h`Y<bI(U~~1^i$k7BOq%Ba5;D3E2$*_&?-fFl-lhM?&ifK7D<WUh6(?}t@E|4 zW{3I|<d;jX7WY=g4I%f`SpQsYJ3S9NCZPSApu1QJHG05B{w@7JHq^!Vd$Kk#@G5WK zcDE%(au8%C6p6+@aaTuO`Rp#fDO?Wwrx4tLeRdZFBsSo$j{fgW?B|aDf68zD{XYl( z-lu%-asMSWPq+G`<NfF0-y0myt<1j!{6r9*DZsxQn|}`dy#)EZEB#9X;QoEz|I-io zQ_JtW-v87>fc#&z{3>4lspWV2_n%r0o)Xyq+Ti!O{Ms-7spWTW^h_215_-H}TK<MO z{;B8p%=*lv{u0%v$^AE8^{0m4asD|2|0ViQ6Z=2;_|M_L!`*W-_)9RJ8d?7y{=cc= z&(Xhw$}=SXCDKH{js6QD|5WgMnE$5&GGYMWzaWv4EX>o2005AmkEggBBYFPzKX)Hq Aq5uE@ 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 3fa03aa6..35f75703 100644 --- a/unittests/table_json_conversion/test_fill_xlsx.py +++ b/unittests/table_json_conversion/test_fill_xlsx.py @@ -26,6 +26,8 @@ from caosadvancedtools.table_json_conversion.fill_xlsx import ( _get_path_rows, _get_row_type_column_index, fill_template) from openpyxl import load_workbook +from .utils import compare_workbooks + def rfp(*pathcomponents): """ @@ -35,6 +37,31 @@ def rfp(*pathcomponents): return os.path.join(os.path.dirname(__file__), *pathcomponents) +def fill_and_compare(json_file: str, template_file: str, known_good: str, + custom_output: str = None): + """Fill the data into a template and compare to a known good. + +Parameters: +----------- + +custom_output: str, optional + If given, write to this file and drop into an IPython shell. For development only. + """ + with tempfile.TemporaryDirectory() as tmpdir: + outfile = os.path.join(tmpdir, 'test.xlsx') + assert not os.path.exists(outfile) + if custom_output is not None: + outfile = custom_output + fill_template(data=json_file, template=template_file, result=outfile) + 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) + + def test_detect(): example = load_workbook(rfp("example_template.xlsx")) assert 0 == _get_row_type_column_index(example['Person']) @@ -42,8 +69,5 @@ def test_detect(): def test_fill_xlsx(): - path = os.path.join(tempfile.mkdtemp(), 'test.xlsx') - assert not os.path.exists(path) - fill_template(data=rfp('example.json'), template=rfp('example_template.xlsx'), result=path) - assert os.path.exists(path) - generated = load_workbook(path) # workbook can be read + fill_and_compare(json_file="example_single.json", template_file="example_template.xlsx", + known_good="example_single_data.xlsx") diff --git a/unittests/table_json_conversion/test_table_template_generator.py b/unittests/table_json_conversion/test_table_template_generator.py index 670f7df1..5d05e213 100644 --- a/unittests/table_json_conversion/test_table_template_generator.py +++ b/unittests/table_json_conversion/test_table_template_generator.py @@ -29,6 +29,8 @@ from caosadvancedtools.table_json_conversion.table_generator import ( ColumnType, XLSXTemplateGenerator) from openpyxl import load_workbook +from .utils import compare_workbooks + def rfp(*pathcomponents): """ @@ -52,30 +54,19 @@ out: tuple 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) + with tempfile.TemporaryDirectory() as tmpdir: + if outfile is None: + outpath = os.path.join(tmpdir, '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) 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}" + compare_workbooks(generated, good) return generated, good diff --git a/unittests/table_json_conversion/utils.py b/unittests/table_json_conversion/utils.py new file mode 100644 index 00000000..0311e8bb --- /dev/null +++ b/unittests/table_json_conversion/utils.py @@ -0,0 +1,52 @@ +# This file is a part of the LinkAhead Project. +# +# Copyright (C) 2024 IndiScale GmbH <info@indiscale.com> +# Copyright (C) 2024 Daniel Hornung <d.hornung@indiscale.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# 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/>. + +"""Utilities for the tests. +""" + +from openpyxl import Workbook + + +def compare_workbooks(wb1: Workbook, wb2: Workbook, hidden: bool = True): + """Compare two workbooks for equal content. + +Raises an error if differences are found. + +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." + for sheetname in wb2.sheetnames: + sheet_1 = wb1[sheetname] + sheet_2 = wb2[sheetname] + for irow, (row1, row2) in enumerate(zip(sheet_1.iter_rows(), sheet_2.iter_rows())): + if hidden: + assert (sheet_1.row_dimensions[irow].hidden + == sheet_2.row_dimensions[irow].hidden), f"hidden row: {sheetname}, {irow}" + for icol, (cell1, cell2) in enumerate(zip(row1, row2)): + if hidden: + assert (sheet_1.column_dimensions[cell1.column_letter].hidden + == sheet_2.column_dimensions[cell2.column_letter].hidden), ( + f"hidden col: {sheetname}, {icol}") + assert cell1.value == cell2.value, ( + f"Sheet: {sheetname}, cell: {cell1.coordinate}, Values: \n" + f"{cell1.value}\n{cell2.value}" + ) -- GitLab