From afe5dd5ca937e500f55cac3771b82e3ae417806b Mon Sep 17 00:00:00 2001 From: mavaylon1 Date: Sun, 10 Sep 2023 20:29:42 -0700 Subject: [PATCH] placeholder --- docs/write_foo.py | 21 ++++++---- nwbfile_test.nwb | Bin 185992 -> 800 bytes src/hdmf/backends/hdf5/h5tools.py | 39 ++++++++++++++++-- src/hdmf/build/objectmapper.py | 6 ++- src/hdmf/common/resources.py | 66 ++++++++++++++++++++++-------- src/hdmf/container.py | 1 + src/hdmf/term_set.py | 29 +++++++------ src/hdmf/utils.py | 21 +++++++++- 8 files changed, 137 insertions(+), 46 deletions(-) diff --git a/docs/write_foo.py b/docs/write_foo.py index 319ef294b..b1f1f3643 100644 --- a/docs/write_foo.py +++ b/docs/write_foo.py @@ -8,7 +8,7 @@ from pynwb.ecephys import LFP, ElectricalSeries from hdmf import TermSetWrapper as tw -from hdmf import Data +from hdmf.common import DynamicTable from hdmf import TermSet terms = TermSet(term_schema_path='/Users/mavaylon/Research/NWB/hdmf2/hdmf/docs/gallery/example_term_set.yaml') @@ -21,31 +21,34 @@ from hdmf.backends.hdf5.h5_utils import H5DataIO +table = DynamicTable(name='table', description='table') +table.add_column(name='col1', description="column") +table.add_row(id=0, col1='data') + test_ts = TimeSeries( name="test_compressed_timeseries", - data=tw(item=data, termset=terms), - unit=tw(item="Homo sapiens", termset=terms), + data=data, + unit=tw(value="Homo sapiens", field_name='unit', termset=terms), timestamps=timestamps, ) -breakpoint() - +# breakpoint() nwbfile = NWBFile( session_description="my first synthetic recording", identifier=str(uuid4()), session_start_time=datetime.now(tzlocal()), - experimenter=[ - "Baggins, Bilbo", - ], + experimenter=tw(value=["Mus musculus"], field_name='experimenter', termset=terms), lab="Bag End Laboratory", institution="University of Middle Earth at the Shire", experiment_description="I went on an adventure to reclaim vast treasures.", session_id="LONELYMTN001", ) nwbfile.add_acquisition(test_ts) +nwbfile.add_acquisition(table) + filename = "nwbfile_test.nwb" with NWBHDF5IO(filename, "w") as io: - io.write(nwbfile) + io.write(nwbfile, write_herd=True) # open the NWB file in r+ mode with NWBHDF5IO(filename, "r+") as io: diff --git a/nwbfile_test.nwb b/nwbfile_test.nwb index a6df97f88087ca7b47db29fd7c41386e477bb9fe..82daee09f5d9bab4d4ef6cf1c24d6067e7457f8a 100644 GIT binary patch delta 41 scmeBJ%e{bYf(Ek!Gs8q}^T|rgVq6^z5U_%2Gnb?t6VnN%$zIZN0L6m{`Tzg` literal 185992 zcmeFaON?AsnkJU2>grZ^SG(1owymBS0~a|1XR<3_Bt?p1m#UOvQc}7lie@HN>ZXWP zWW>$PXhudv#e>OYyN!VX0~*Z0(>DC5je$125sX>%!i(~}y6u&gv$0WtfkrQDSu3Mf z9)I8eIOp6O_eKVjWR)mIR*9K$HqvY`}yqu{)RmA&;I?V_Qy;1`!oCfxi(*TY2nxL-@?MH_WTv=|1YoWgCl#sd*|lO zg@tb{c>n&}zFzp-Z|Ps}*@GXwtABj%4d%bU(hzv>=H_iK@KZyypV(@zy`z8pj^R}q z|GvGjAq|L!FXMk}!+zfE^qThi!heK6UdDfpzpws*f1|%&!Z#`Y`Z(`>Q@=klyj1*s zsTO~K`s>=BW6giQeh_f{{n(biY`;G<{Bekk^5JN&**oqJ^5HOV?Ty;U`7j@}^P#qI zJXNgrx9WfI-1>0qB~W$8yUg&``zhnG)9IhuAKvbj-_tQR{+|Ap!-zkBzF9vuzj@ET z{@LsLhxhmDHT`_e?g4A|rTz8N!mkzRfZtHORA@#bg#Ryy!fpaaXlrmy&UyltQRrFop$@z<=u zOAEiDyn+GlCF5*gdPyHBBfWrD`r~xsv;ROpe{Rpeg`MCV-}uHi7nR zr?C3z+Y4{WA1^KZ=ECpd-Da=*Fdq!ty{>-$JK6VbZ{J#Y?Kc-*`*$TeudROe{ckTQ z=8>pWea2Fdr=}%=SKe@tZ_@RJ{5|c)IbM`n>L6r+XX!VO@)_=kt~=+qQGzk$wIz z?e{;m-~WUC{tNs4zuNEr&3^wI`(6C?v+vvIxAgbR_WKq4{k!)2Rr~$-?RRFs7vH+L zu(0sie~f?dLk#%$@SlGs$yiE06Zil9%b0&+Pl}C!c>Ce`m>O z;!yheckua#kT<=5qSpWPD(3k|_ya#YV7G7{{Ci&6e~gFz8BG`d=j1b4o&OJ$e)Ye= zANb+>e}jMgGlp9DqebvGKjOFg{r{7E&!hKWCFA{FeE#10!onB-CtGX#fBuK@GyD6; zV3hoOzJCBd%Rm1LKL5>kv7Y~fGtu||$wBI8b2-==;}>sFJg?z@zlrbpULAi~&j#J#yl|j+HK>8f08#xd+ip!&u>{DFZ{WEZyy(^{|3EmIDs4G$Nf$N+Hq^(@ZIni&iGvizb`1* z;^!5UZOilX{oSzdwKeI9{yeMwefP$P?@OP&i1Fk7QBd%fdcHf;3Uc~?N-caW*= z7KS-ALapI~%W}Lw_|xrWuqMQ-hjg1iybt(;?+aH6X*qwqU9&fkD}gkbKcv(A*@azV zC$UTTJVb1}bk?MmZ~ORH~Oy7c<$YZu?Ryn5~0Hmq+9iR!!`HT9 zVLLm)wr9O=*1-Q;5Ak6<$g@!|8^CVYXdh<}8^cjH8sv>3z8J3QaJXgl$Jh0C$g2IPWEEcemRX{n_X=>m6htv|FuCp51H=Mn_o#-5=%I&QTjncQ{g?!5{RS z-R!oq_Z$1YL1WY#oa%GmC;$EahqrFN|3@F}-rCrZr5Q7`kZJRUOByAU<{PhFY^^pf?!U46TE1~*b-!`(>gvJ8 zjn>A&M&t6;*Tup@T?v2uI31s62kikMHazWORik#3fb0!g?d~Dl8M>V{uS*-R>AZf2 zFX1PXe;4Z35!yT49Iq~|U0vgu{X6!48P9&#o;yC>`oY`pv^)8NsZT{u#3olS?O(oj z1pv$sUca*X+UsvzTiw5Od4F}|;I(F}^~M{GjVl~xfx006P(Q?bw-{gJLnCvhI~fzwZ#>AQ`nikR(^ojw$OC(&+e}*9u9ir z{;VBJ<7^1j!4}|fb~<2ez|Rkfh%$Nu_;s9*8vL-9y^Yx>6Y6v(8=)7F%61nD;i`Gw z@NXCYY~@cUCmX)bl*t-qM|r1}owP?s?JkZ&dzkICL4ETN#+^|+wB|@Z95=e7_UIJA z`27Na+Fimmv=u0EIVHdog3o+ZcB%vF=4L~geHLaTJA2gY@C<;`fVLcSWmsV?Aj|8S zM20F5VHSU;XeWLK{F8b}{uK8ih0O4%(a(XL-EpS_+-kLtiCK%?@$r5>AdYl<-POnW zpa)9W8Rx@AHX_0T;A4nG+y`0sE{GV=Ykb_z+TB*W31W%w$mpY`JrRsJbHqXKWQab5 zEFL#TgZ88BJK4pl1LGBrnm@|=J(1ChWA{gc*@muKY*=0C%iXffBE?D;KPwD|7l@)lGz_u^)rfkEZWBoBSa_)t>1#$?ypJQs zL#A2?)$8l|3KDI)k{jTKz*`VH*xkXnN$z5p^#{F&?H0Cr|1^7V>w|YN*=PezUXPU(>@ThPtU+EMHHV3k~0uZxZkaIW|48;%9KWZ#%U4+C9X zySBE$-58IKdIKK+&2DRupJcm(_J}9^!w%V(?9Mpf&pY^h=M?PH`=|Kp?Yx_9wVOv! zi|~LpN&A~4cG36~Z0%_DR%gGncG!D}w!MM8pFA0KPLogjpc?EbJW^N_zUB@hb`ym+ zVH(MMLXfUuB&A4O1pDX+3>Kh)3wF>u#tnPWegw$lRbr0f`dYTBdn6zEyq%nhA=yD3 zl&rl9c1@cYb@~W*X>TCo4DtgU&2Ce7mCcv(PA;7A5_mjA^QyBFJK@a3LDEbACNQ4; z_0KclD1Q|1&AafoTE%9Ap8`E@ z9CC|*vY-#(bq*jefIdSKJp=>y82d#LCB0es2G*OfM&F-x`2yc8r9Yusf!UQ#uxB_%jVwxXhM(z>!TW5+A!BFZ9F+ICyvQ&_<+$fxFs~6AYZIiy z*kk4C4-W_VAv6oPS_OS0L(Q`om!SKeH=0M*uF>yz;E9KmEpx5H=&e(n+IDkS?u7}g zR}#VzwoPw6G#+uTcT{>b|F;?fXZaQW^s>I^d{A{S^QT``2+ZlnS9NEf&AQ5ddOtM< zq3fB;dT^&B8&`CEZ}+i%e)RgYnb(WfOPw8|443(Ne9?CH7dziNJJXL!=gQCB?PoN- zI{YoiW1Pir^uMbUdhZ#{Yu?R^1%WdDI-Y)9is#86^Lr|Bf;^*{po+Ut?+u;29EwG>L1fR+22bYK7^~% zpHKf_`ZP29`p0ihZzsk+H2LCt)7!meU;o5C;%k~aUHhrOP}A<0^>`Amlw)&0JjXBp zeSSQBlziPd?Na&%)o2ceUNiHRH@5f3+b{#$U>7{Q0@zkA6a&20ab&!SV8GuU+L^zG+$4 z78aKOnU3WB-?q>F|3;rXe*U@Px3??fyVK#H8t3TkUXdu)g@uiWzM`+j?>aj0*Z-NW z?{9bX_gQq{FF(?DzW-kh=*(9!D+J1PfM;EP?0)c%bZzF};?RuW>cx!suu@#GpSxv$ zvF5kZ0k?K6Ed0pu*55DVaZNX0`^U4(FV%7U^?JE4UP$>x@havs{ENHOQ6#gke&rxZ8n!73J|CBIfQ zVk`b#{Xn0_AeVa#@!CMFhW!Dz<@PaytBRlH@uj-Q^6llt`_bvSn{`*w5yFL`*lK9d zAY&{BxrJ8K{kZ|xXbBL5Cx0VLeV5m=?SpKL45ShV*c*=|9=D0Ojeeth%6HYTTfECx zEXFj9mmy}b(dnTTJ*pYIqftS5VGId)68GxiDDNjPXvAu<9o!WyesQ@*cb>TI(hdHfhdT9Ku{4t!#KiZvME>@U?kr6grGR!OQ=-zk>|LG zjFFJthZ4*pupA|eyti>ZndrTZ`}g%F4Zz6i-YULR=mNoKE3z2?T(3*t_89e|ff^R^ z5+AE4yY^{cR))iDMW+vL-U%Zqu!D%W(|HafLMHzIiFTKhv3M4V(m*f^B1QcCO$JHs z5!)E~orow;9v(Fvqe1{yf>l5&4rl=;oi-xuu|LSF^usZBOhbiWv{2Gv0h|_5 z9b(E?vV84uEs)3J_6xN0g9aj#JzCJ_0bi&Bu&18Tn;P=A9cOe#-0j5qNmrVZvOD=9 zZWa%n1gXqA?d}5&>g#n8vM%9l3^9`UV?+Xjh$8TYF9B4IZV&i4P#peL8h;wY@`UJV zhth+z%|1=X|CCW@IFTC2b8>`GMH>^CA8b7>J$yuc;uQPEf|e1cn!LxKFB+2zB+36hk>$$nD!#nhSf_5QTWHW7;$+P zFUC(3!t$w832cnb#%|RBbi@VYCYiVl8%5xKDhmuRA^f^~8V`e_N9m0xCP68}=w{7K ziPBfYlJ)u$1QSHAp+^h~4mVJl9Hkwj6#!6<*JLQ9P?qZ36T|D_veP6H_8}@wY2x%7 z6of3zS)s&MqgbEdW=$!>rr(k^B+!nS@qt)DO9SO}CCSxaN!)6vm;y<0(>Ph69_V3b zEP?ZSXINnRie8dcLnpDCRcS1eOODw-oak#J4~s42%^psi-Ch=*7twmKN(lGdlu0y~a_pCl3(gVjDx_@_XdmTmSIkfCG%#y>qt z%JvV({eEvS!Ud2r`T+wpTZ2Y)>c!}kn>SJdrXfOm0Z$MyPOqxB70MU*Dxr*;r3HJV zc*l({CUS^~Bu&?{9l!wSgy3(0nH~rtX(Y{<5+WUeg2*wFQXpv)625dm%Yuph`A(8a1AGLeR*T2RdG^#D4F{>O%Z8$cSvuv7_FlK(8Gx%kDza4;%er&wB))ZbBij0TACKC!M7nHff{>>H=K)bU#OP8;|`K+B|gIDCE1pNtw0u#>nEaAcq*7!zvm zV5Y-kyr@JT$c0z{Odl6LAlK<(LmhlT7wm9fHwd6?<-n~2K?9+eo%~UIzuiHqA>kbh zC%=j*xZx4s1x{JTl9X!HaRa+JyQ2EmOT#m~7~RCS7~vGGx2;Z84(e5e-3D?GTGUi% z!U`iq$#sV(GEQU{7A+aUl#AH9LGqKSoC2mPX)*A-?zWxICZP9}O)z06g&(pIfi})I4?O8kbf*7Y`Atd^*Su{gja#iKy8cFm+Q> zwvyo(<08R+4Jio6YlM1$3mH!X^+sqgC}0gvXd%1L>{Cib=&(q@_zkZcW1aCNM|fN8K*HE@2?8p zgZ|!RR)p|C#kE4`SYzIywvMSYxCzPt$ts^TWOTo)RJq@FfW&-q_a-(o70nhex9<*@kbV%ICIgStI z%@k%nWp*Qkz=oa(>C3aF8T}Ao z+OEe#Gy{nys}N_(y(5~2`RHwM4Bix|JEE=u-|9oWDNPGWy9zTcL!a*_iFwjIZ+Y2&W>@NP*4HsSvV5oRw0*KK{$c)2Hcvd$0c2)7v?HeAyEiPjCLhfBXFK>eUw#uiPKX9p0Qyu`V8e zikhMG8IVH$^<9Wk(56F8mJg0qWdI)u(N$CEgZ@Y~m4ODU9GPdIh9{q@DnTvkD{xvk z8biydJ_{ynJ;<3J*S$i_mn2$qR2-+S%GDoXMwke-7!=jsUHBapjW(XoU7pI61*TKy zEP$_`_S* z-?+MQbt%CH?YRo5L5s_SfY}7Sx4wd16>W&+Gftgg)a+v32hcX7N(xluFt+9<$Cnsy&=6-vv+c{qAwvU(udVRO?sMqZspRT`6eQ7>mt5M_8TJx~|{bP7U zz1fC!@jq#|-sEb4%XXM-E#pCZwljs}+^6CyIa8*-;II)F*Ozh~L=DCEWR}tx`^)^iPG=+|e^k?RgTpCFj&Egu^{23a zFruIog`+8=CMISb$w7`&(*s@TNf=ZYsuNuQ?8jp}uCA80>ND0qBe#ChP&K`l2Y;gq~ z588za8Z;l3jk7okL?oWs@)crh#+jGvrq_kO+E(hT!!;EljD}U_-#<{R$4MK;R-qtK z30kHUagj3fiNz&e5AmKf@+U5^sxw!N5H+veM`q2z;WpxR_61~}a1PJ--kdn=R?6o4~%1_St9&8R65)j-PNBfIMc)V2{ZFI{7_5 z{Y608E-{C)llRGm`!|(~FVZPD7dhp@TAnjh2`!#S1F0@50jfTj4%D!LQupDepZhz` zI%?&S9e0*s6$C4bFht1}HN7cs0B3^-Bp(yBBRy$=xE({1i{?NK?q2CeE-TKze9ESo z&fmLvXG;^7TlxU0i>>kiPX?mK;HQyP4!If9|5*qEd6T-T8>D;m3@4)v_rL6L44W3r zYRYocOX4AXUc{}`km+E|`W;+FQm!LR1WaoZo?R^5k?MDf-#8z_%Ra1>M|*!alPCA1jN;05bb7)%^ITNPPePfft9Q>9~X6V2Hu4PF)+G5ga^Iz%ApP!YLg)Vg*5Yc=p{h>}Q|Ljii7n$F{cFe2=d-S|{p_c9UVPLt9zUO1y!gF7zkB`K3wf_AUVUrf z3)|X9|CdWP)qCyt_Rs$N>hJyOk^S+-U;C5l-%kI(_!rgR`%|a4FaPh3@&3)1H6es}9Zap5(6tpYHl&OcAyBq5}C=C8`^_`IFh!Ys28m@ zzL1h<7{N4I;;TPpz}+HvL@6fPL6 z@Q7iV>SGRl1fo-@eIR)8EwmEwbJi$p_?n+E?w>yAaQxr@6FiRk7*|!rS#j%uuxW@d zyVXr2II9ZG#YIfEQ0$Kb+;OdI@=_W-A5&Ez+XO@DB{#*&<-(`ekQ~6P5Z_y*ZR1q3 zQ0QOSyp`;5CEKrhhG-&9NnyJyRW932E?Z*>>d!x7NsbW+gb*o~Lxvl!#MfS0-O839 ztz@Sc5Y0EpF5{zybny@aD^Z*`hU4=ca}8QLGfg(no)R5gTlw*utRWn6$Ea8Q1T zbzHb02eoQPoZ3umm~O{5*MTKhY9MZ-_Nm&AJ5u|?MSEGi7$N&?fygI__zd>}e!$?I z5DluU^j(Zmt4Z)8o{^AL=spzU=*CFH6KN^VO3NUfr->QL6v5#%NemoaK4d5jl!11W z5LQKUAYItOQ-$|=_bZ$n)dEiFx~AP+lF}GaS!Vmd#yRnd;S_R@2nFXw^EAcYD~ZIJ zda;7;EF#4-TNX|XmWWtP^Op!KBzDzo$}E%K!lk#((wWeEY2D?Ct$Dw(zv@|?l}r=F z!_#gPbeqaJgd`%+67#cYMbZrDA`yJ67SAKp_!fnp>HmvY*(I~j5cF~h@HnIe2F)flM*bdBk3!D%q5+OTc9tem#XlT6R zTJ|AB_u{Mtg)tqtLon(KgGwZ@;6A98p!pJE4~_~WANPg=W{_CPUI3GD)*lauCaHmn zF91=J5U1~J3=?*rOqf>X=EZ=&jQm+h_BG3{yd~1oX`OL%AYUeNMVG_%K)wBiXL&F^cNSJeDK0)M$8h3Z`>gql~#rYz0 z)-YSfUl*?;u$}zu>g7u-uV1~!$;n@@n~z3}UguYh?8-;>!t&~+HGIHB{suFqB+@)Z zDu8>)Xg?s&wT+F9kA*~VqA1Cp!p5M7)F$kiIaee`+=)+N^_Efx?bM8fU4Pi?U;{A- z=CpqK5*D=1;j%ZgH`cCPzWRE;`bL6=abO_hnS4`Z)Mens>lfEH;wC1X}Z!s6Yl(8OL{srzY|>d-WrMkuR}q&3ln&%Z&>a z$8n5AaFT*TJcUxc#s!CH?lv+^6lW{2l~8bQ2&O}X)~#ONzo3(oxwmW&xJ17*WdKC~ zk5SwK*HW`V(>cb`1CH2k8n(rk2n;NM&%RT^&o8^ z2r%B2L;_6u;Ham>jSd2W9tpKIolW_8ktp#%E-Ct?zL@L*=;T_Kk_*>|NJzn?iI|- zmD?wcDu@A+kAoc}bxJ+J-a;lQ zB=o_(2{$*Y{ZwY}_i+HRH=@-h93kUl(LB9EiFgeQ`BYJwn2KYj$!U4`hM@Pverb$b z5^Zsd>YXf{gwbd{-ImVQ*-E`MqVF&V!YRc+WLqI&@IhG+#mgKl*?wM0gC4i7aA42C zc6)12=bECukUl4JC1s|j%+LXKnQPex%-lfj2sk4VC6VsIS^&lDX%QGcc%lbIwq4JB z6gh*iA-LWo3z~QQ=9^i{lU4LL0ghnzcvlOfxq3usuGw=0sx?ZqEkJ!cD9CVXitWl4 z0*oJMY-*V+0hC)Tc&E0BWLp#1ps!jv%6H4G9kQLEdPo(Qb`THldko)NMLD>ZxO-CM z5hg>^XK^~afVwKSj0dpv$Du!vGi3|xJ_08ov{?vYnwKSE2ZSR}dq}=>7ks4QIu<=i z;yf7mkEnYSc39o=$0VI$Ef$;uXoag@7k57RIcV(Wm{5ljAzUS%P;OykqCT2Cnw(2! zK`dd8T3yZ75iu-o*)Dggm?j4Y>&b^m6Q`)w&+<;iu1MgFuf?7QLQ_QbFyR|G=HMpt z!Q`U!INT1-oFDXBV@cgP+o33|uVzx9ltA==Q*hdiS9i+3VVi<#Q#m)WX3P%I?@TUF zK_LAkCgdkGqZDukKbiUA5Hl?l2~#M=Leb#PO ze*xLgr4AJkdZxEgEuiLq!#iA^y{Dl+eDKMld>^O!$)bZmBsP6pvJT56JE7-!M_ zKmFdielGc+rV2Lm5pW;3;NliEY+y90l_ASyqR1i22ho&e9G$Q$ zRnmv>RFf=~vP$oIC)%p42?@SXS|MSl&Nsa~@4h{4Vy{K7K!aGA z7!=;Are+d+$=obG0PN7LIEa+mr{|vSrcDmY+h`HF5Ak^$uM zmKu9fMjHLq@#WEg9XuFBhc@@S5NWL7d0_4$BFfJFF6Mq0(89Cc$lULusN0?UT})Aj zyE}GN!k9*U?sqZwyMXUP;u0ZjK;RhWeivTCP2DctBjK0lci}8$sS+g^+6O5U>^5qZ zAYkXEuNTc28r}hiQiio44uxv*$H*S>sG!geQ7z!$3aad~)DU@A@mv-+?sOh*h!a8P zHWFFEe#@et5+5U377Kp6NY_R4nw!$VY>LW0F{08vq%nGP$zOxp?Jk*8ZwlXn{2_dx z+og86gO z^@Mdw@mqZj@5h<4M{JCln9LFt5KlHF1Zu8jf1D3`tBkbZ8!&u`uw^OLmTkI83fr=gq>#6jSPx?cHv1wx7a2a)6Ag$fzq05tqRtYaz6 z9L7yQ8S!hwPd{#g3X*0q^)bK^By1wBgI}I(l8psl_;eV$#xLu(te>6vD$X>bj+*iUHCDx0;v_d8tY!D)0=R$g(fxa;_ZgN` zcyRBr9{wt&>u&H2!rqp=SP~<((ktMI{5E82}g=6Fc!GHMZwS{X) zjsU;RTjqq|0r5p7Oom9EU9k5|{X@S?90ISQ6~@3W+xf(h>S-L1&hBdN4TJ7j=swUK zKs)$=OV@TM$Oyp~S(YJ43McP=r*XGe>1YkRK zPJ*>`MFy72yqq=B6X7IWPYuvlm#^;AGDDd%XeRury0R$v+T%E>b2uWtq~bY!9T{Y( zkr+yms?a2b{4_k_k`r7ZT%)5-$z#|p1Y6)#q{yMTUNqqYG-#|NinEPC5}|2QQhGf< zO*uM9-;l+%g&LF9lR zq+JM~BJYIzPEN9500JuD$R-}tAZ7?8OR!zY20d9Gw)2yzi-@TPs>ehM^8m@IHWU21N>x^%tf;fwh0JL`eFWCbLF{)Tk`4>vTrwYbV>4{ z%X!e^0CE#AZh=$E8#uL{2Q8``0mf&pJeSag5>q@^p3jx%^Pok@Oq&VAIuBY5xl6uk zL5l@t%T^Lt;To4hh}sZ>bo6&N1*2uGP$j3B10Wdmp}Uj7AIW&3yb80tFnkGJ2cPML zi|s*JhgOzhq!TfkAZjecw8WGkjqfq2tkNctseKlA(usW(W_H_ z51Fqt9y6#4a9PGN(9o3(kdI=GVPQGY(gSheS3idC8O>Fw$D+xddwy%yp+9B=JSS&o zqNKKsT8d_mSuaEi;2RRkib2?b;())ck}Z@4!LOwS_^XaFV@x$Xrc9bn3|hhMY7 z48F-VaICNoB40CQ2`Vl31`Mc{{W_;mSD1)~MWiC#6g{EHuc7|RWN2~Fv&y}3<)L@4nV=6nM;*pyHxV(HsOzPS2SUW?AtE7_=gB1 zPg{ci95p7gjk{Esg!DYoC{LDh;Jl;Eo(kfLMi8l2RreMdncZ67Om?FxV?YQwT}0b7 z!e*X7KoTI#?bt6!{s3HTPoM)ebz-3Mya}v6p3pd$0kXSL#LJ}u^2Dx5!=d0pR?Ptv ziBKa!P43ETD4TF$GI`iq_P%NTaSa+>q&^1}6Q;jLYF@CBG;7z{y44D^xnAWyV1Wv| z#*|CMOe6{mlV)HV7A2&;YSFv65R3gtCNn*{mX{U@+ZMgXRjq3Y z-2CF(j#m&j9eu%W2(AH!39?nl!Y5th19OmQ2Gx*X$NPt$fvM))>ZvR9t22E&aB+wy z9FJCe2de@;P7dQ+^e?PY0$I~&%Qj-Q58EwfsnO1b!2!^fKO5$U8gslXRuz4S4u{}E zpk7YxEc&{6N(c+4SF>oxi6}(igf~zI)HXDk{x%^qx%4S|bHtm|ILF+C0##bYY>8%! ztKyPou#m(}WMJ{af@j74=YLq~Pj8O#U1lN7u>~6bFM9F`-FgVj-BJdp(nPbl+L}=_cR$d}`fF%_Ptm)Pv z2c70ow-B*v-T?FBms~g7mNQ|RGTO{g;&Q;8J+N2Gea82EZxSjNERJrfgr-4=h1^I= zt{&vMRIJ<|cOIO^PC#Lt(212+t+FDR4IT#aE&Lzbc`p=M*XciDho0|3IP{F6#8iAG ze#Ag$ukJ;VL~wylNju*%E1;P}Hamqy5*uWxxh6JIr7&HI4pnvwSWmD@gS*uoMOu@D z!hZ(#V%`9_1>t%_Io6?P1Ss{K(j+5lU)JL%5(=rX1@3dcT%V^zN-ncxF2Yn#9K0LI z0*P>UA@QQvqLK@4+UdIyasnh$LIV(*cJd7RxGKd7$%lWsQ&>n>OMojXk^4sz|PbB!d>Kn_73T=h}$i;heODTXi+fRP@}RiTyE6i>%}9ud2s z{5B*hT}J{MjN=|*xVb?QkQrCE&EPHoaVH48Kp!%ka2t9En#+9EBUwo*_L|e3w3vC|cW+D~Ns` zRH!Jfgr!6AgX~#;`om0QQ*z<%vv9zmi2=^g9Om;}B zPZIA?zp;%?g{EgljK=w|I!9@rTC;G*{wD7!y}(n&N7^|dWh}-l$3A5#qL zzXDx={3a>%Lw=z0X_`1#QL+j-myE0iIzXsn-{le`9>G(vJ?lw}-6zgsJK9ImYbf`U za@z^1u<_N%QAr@;KvnAqmJHdUXSJYE;kO_LQ$!Fic>+)cZ9&}gf>1k8z_;sW8Q%*@ zEoM>(*>(G#PHS+3#ErweMYePB@kDATGYr!^(+)G|6_2aaPp8 zQ|O*tvNBQL48)Mco@yCaCQ07&bPvU2PR;|-K1vLq;u$0=Juh_7SA|Z331M*wK(;{P zkali(goWaKDSK#9ugf`zq)>#g4yJBG?o>QR{%z^S$O`X~42^dI_;2bz6+M96i z2?9x{Nm^m~ni7*rH6l5P4#r_3nwG%RMh5e^fiTRn%o)L9q!g5ew33jqUFOCXWX%al zb4L|q`(Uw>b7mML-TB-izUV%7#9a}9R99(kkY-ObG@uY1>4Ak`YqmYLxbb^LN|3#v zW0A;WxURs~g)M-vyFUP!!Fx)tZ9!FGDd1Ql(gEQ|zClw<`xRQV%V-P{F9^B`1ZAsy zzx84$Y&$(bd7HHlq~1H?DnbEhRmEX5$?T*sXsd^(1Je|CgM3vs-e{RD+tZOM#~dK8 zq7C5G8yq67H&X5fR+*7Cp0S@eS32u{DVt;>Xdyo2;H+iz;(o}uTtGOp1<{!6vaM9X z1U!$ehS~^B3x^3l-T5)JR=#J_GC7gHs`6E%DyQ3&BUTxX0geN^NUW0F(^P2)59&ff zK@`GKYOKmxv*uAAS(kD(jn1rB(EE-356~NlMD-Mfd?X?~2qddM&n%srTYlsrOa1^8 zpBbRnTxF(jNL2Z5r`<1h+q(b-a_F)Ry8UmL*if|{Un40 z2j~_4TK>)>p?`=)00Z6|Xpt>-xuv^rB7M|m?JLzbtD77d=_j2Wa(UAz0#a6*+v&5N z+v9W0?J?&zpwD*-w8zhOV%x|4yd{q8#qAUa@ZQmJs%U^&nVL|ueywE<!0*rj~z2UHpl&Hx$ z_d@_U6?I*EY7?nJsCfK^piC0T~hzNZ25Epref{LVjvJ&_-u zY_F%%RA6OXdsw8BOUNC2&y!@HHTLr)iD!-dlvHop`631c3vB{@5QBBx_3XyZi*XU-2SR!gj$fQX&y^R|EVjZSg)7L`f76 zA$I%53#&aDH#7s{1qXo5OfO{5@}i)XH{IGzN#2$Y$Tc@+sJfwsA*nn{H*ak2-rTyMNycmiCB+lF^xT zM~ii|8=X?)mF!O5M=UGVqe=HSgRIx+z%bC#?-jEsd6xsU(t%h9q9%fjTaiQ`iji5?^??@hi9dx&}gImZh;RM}MpJ^CO;tqlFzM*oD z;vGPj=_(SaCZ{5ktPiM3>@2U$9}P@I3XEi_Zqw=*sqq7B@2X&;9Amc=2HUI24SqP9*dKy(t=7pe-P0-z*# zqko}n68BAYNJ(2&0H2uCwN!vZ46309UIo8}HZdB5%yF4z4V$QfV~#I@@?5X$lXj%;_x7 zBpe-RRc=9EhmyixX8#}D-Pz@4N%{^9rS-k);0wJ$*i5c9j9GvdH(~P7zvny17eRc9 z4Vzo*TbqF<>tgvfjZp~1?=@-HQEfxYF_2rL8?{|5xK%63U8JeP=Bo%R`>h7XWc$q5 zf_Fl4TnoMK1>`Q76>9~$0ujAaBx4n)nHwpzllv0MXJHa?=ghuBU``vyuERhe1fNc5 zqYDU>9($ZEQO?ET3pb(N)ae^f4wfmzdv>T;{Uwm}N{~z$qXZGE!hlcsf232w8cY(` z>MD*J)WprOa_%!dP;gPu3KY+FRHFj5W3LHj#;KeG z{&lzExu9Q*o{n-b4ga{)ymoSTDfv6?#W5+m_9lV~H?ZWb4kZcaxu+zij7~gUtAVVjBOPWkv(6v$$ ze2s+)cOXm5x&j(r@e(W;-O6bvq8{fIxscLi;}=EHRg?&KU2x&}keLH|iQ-;lOt6(? z?71UEba#hIhGjF*0&g}kE-lwQC`c+)7|JsU>5c@aP+oeGh$Q30hkMB@Rl`k*Qj49? zlZz65)znP76VlsO1JQ;!=n_Pe1R$mlxCqCc%dVjxe25 z)p9DQxDBe+9w8H6udDMT!w`Eyu^mV$>M>LpjMUEFs~T$x=pN*!sAk)$huSXOlEltF z%vrxaV_amif8qVYnKs)|)h@u8)`Kf;kGXP$ADUr#Fzi(=s}h7Mu9tQceHdE`sUfdC z`juUlu8`JXdclw)NrmN_O|o`(@Y|Y27P~@dP*;eQLM26 z#6p!+TSQDsR%tXFTt0abq1RAFz>^Xap?_M47Y3UP13k2+ObVyY1^W%~(nIJE_*yz; z8F1$5!iBWmx3p;xJ89J?C#i6yAItzgHq9QiTrLYiuS;vF=uF&SJ{@0P+2e)XXF=+O z6`*_^xS!Mo)=9U%RQ4YLBb2-0p#btJ=?zmdp|S#eGCXiRIm;@r*9`q(!l?) zJs1y{b60;u?umrDFfD1A@hmDbAo~`#pN>hKJ+n;#0fRMMoTlK=UOuqW&BT0zw!T2N zTwj_($7#@t1xS$=rvS#aU2>xUEa7Fji*xP$fnoGW2fO$DlZom!E_0cAZQ79T#(>_bX@ws(?JpalQbiZS|5pT)m@E9 zAf?U?e-ftT3z$vw(3xWtBvudZzZghvcviFo-vt^#Ok!4n`rtINUOfm)QF9~a zhZ$j|G6Bp?@LbetI{V{oI`WS3PCws!A|7;Uh`;zF|AP{Q7N!Zx`NFfCi)|)mT(SAadh-# zhj)k^UeO;SU3KqJd^lW{oB#t`%uXk6=Ss^{-9-2t@Uj`|(2X z5RG28vjT!v8_sRd?B1;!nD*{MoVC+zRCi4Se0F) zDokhR+XF>A(`4dAp+qdOD$Yz4!JKkI1DZJYANCqx?R77aG#UB1?QirWn|>jy@-@}S z2SG)BOVN^}`&)1ilh9A|&$Zw@wMx(UA}(hwY7_vE`Mj^()EoEs75hv;6Q9t z2KC|3PZs|et{K9Wz@$k0PsZ0b5o^*!W(zn~fxv1|(&{ZS}o#!5RMX73)8eR(J-JauE`(9X;vM;gxC#wJ?dX7S-L`;^F$j>DUUYqfL5T3C_W@C zGrtRgC`x%WN767y_$LiR7Nr7`1W^=`3)#iBOKTU2PXMZCN+P&P9Q>}OkOuD)6__FQ zLLr4L5RE_SL9m6YmGlvG6B*N?tA;#;nnNiu!;P?K!(!eg{fy8~7&I=eU0vG%_aKBv z*ncRkqe74(lth*YEUS{u%mO`h2{Ew&;-x_)B#IN^1_jf~COjgQm!M7s!Z{=(hQq_B zO!Dw2f*?y?}##gjl;=4K#dp>RWD9F7I7lXVM)i~?F8)s z=mOOyjSe&jFptKU@gCg9$)l`f{tBLn5};tu2(MtSu4$M^-siw67U5fzsrc^M)m=;R z08eQ@GhhL~6r2S(mjRnzTs~QqCX4Al!5wl(1##6T;!l`HU)dkiee6R|fjB6rfIx;N z{3azW$t67VT;2^XL1OL+fJ+EfyKaod zAg%<7G0kxEPq=c|_9H8i+pB(3CwvV;ML08bqD$YkvXB@GWjZ6rA1U$y+(*i_4z@3RE4ocoT=U}1 zP$ajqdHo59Ch&CAD>7s~OCxbr9wyroyjU5%4W?t5U0S=ce)-btSFf#K+t}F1-pt-u zyK?#J>-p*%$srWVJfIqP8WtxpOhtC&;@U=JBO}^EA>ld*p7N|AI*}oiJ-i@b0$GaV zF0my-+VS0EuwJ5$!2N$b8XbT{=^g_U5nnu z;?1sRr^;GA^7NhoE`#wIw?`7#L5wBd1?l0FMQeZys*z5YrC@;Q zr-y}uAZ$je0m7yNCY~BU5CzH(S|^MjyL2t(KoXo{LbjMANeW?M6NwFln2{w84{!`o ztdx@jAP~f)e5(rIq&zX@s2Ghq;-ap*x{Yi@FGvz-_-HuqS1M*)Wa%PDSCHfG(#%li z<`QyL;Hp5C3Su{}g{K*Ot0+WrkDbSh_y#(TEhJ`X^wIU}vPYPUiCz9m;#Ir3PeVH7 zQU5O5!EFJ+NtEhMq)h<=gvKG=`plh1naJ|cxX5ydlfbY>c4T6zITDEKt~5SJuV8}}*9!VGV-)edmo3#ZHuRx;cv@(I9X z%4MIlBr3xl@0z_^jawR5?_DX=B2*7$b$jE(BXLpyY2YrWAeGhhutq|a#0rXbNn#UV z2=JiM5L$fZxWipdxfG^^GHIxI&pa}8BZUE(=-pU{Xi*SSBXG^3q7qGBq$c7U>8oZ- zvTP0&;soSR$wi0TUyf0gBp8=ZEH1WC#iS$fJBmk&`6@l&+6hXwDTfa@K}oLW*l%{B z_W*7Ite|x8e5*2|CS-s|4s#8HQ|j5a!+dqnn8C{i!gBYJMArp52zVY%U~DYTveMXe z3S!NNl}?x;oF(N6?m-iR17ccm?0X-RqL@!BS zC0GtWV)2M5`7)hns!QGVMyh7x(vn#k5w4W^6LS~29VyW!(v@M8Wk%;}Ny3#@ZG4-s8OVMXAZM~GTyWqWqAPuwcZD__Wg&`qC$GMg4yT#{{9x-A0=8UMb?X&XCb4C=$ zk+e%Fe1%>bqzL{L&@6eIT9x#i5uG!lb4C=m_p>rm%o))+BMN_=7f0J(W<*2m1GP!2 z*V@H3l5mo$EH<8#S4>PTPo9&`>a>@H$c(_R7P-|JSrOw=vOhsB2IrizWpGr&TZwK~ z_@-}0p!+Y?f@WI4BstLZKcn$_0$k{aLV5*w(9Dx#Gxp8r7RoswV8*%f_%!%gt zqNvbZ0lc}AT`D|ydnLPEcyL3a_k?$*J1TYRaz#6T-p#J9?xXGlr;Cu%sEVE7u>rtp z9-Ee+I(9m&SG4xfmc~us`ky&ly=_~ZFx7R=Jg&+tSZZ!19;Ev~9dkIvJ{7`M_?1Tz zmsHEJnuB(i($%Ia+Dk05r&5GusSAuAnB6sbZv(KV=tsihBKJD3^aB^m5W z?1w;o@3DC2aZ@RPy$e%05(%JKi)5FPIy=Cc9r&QZzYK|#V2!8RpZyRY)j*#}1F~2d zinDyuHYvUYLyp@Tu?JsCrLBOO_g6Q=Lvl4vk<~bbU ztyv}-sGlT0lEZ99atkHa6hOEE!AR?fpQ?4T^Jk`@-6~B zG{04lzS(mLz_oimnV+=IXtEOh+ zIu6L06OvqP0xxwWByF>vD!sHDOJ#4LWN~Kpn6BXg=!-5#i)IU&+eGFz5!fh~9O2q@ z&zk$oku{Sf`g5DeGZRJ6WyMyvkAz_kyFzvu7GGu35Wr0C1sRkXkb)uw^r3d9Mw8Ie zV^M-?;f?{O*P238>pcj4F9379JEz&>VClj; z38RuSpLe9R2;LEq+x>o>pG^2<=N7egWj)tS7E~n+%~X`~rUlnLv@7}*sqi;BIq{lf zO03kS=Y30&m6)q~8sKGt*dRT^7?QDvrxO#`M$Vo=f%2_-SjMEv5~%}Hh{~v;gr!A( zu>PC+z{?1l50$AsR9vP@kV=gy6DvXR!o?~m)3jsEo09OM1}K4!yABo-^F_dbN9}=bnn~rP?zpXU1j{J(mRVJ8PGJV6 z?Nhg~EfXpPO%yVdPGMf}s+FTmQ>X;UqNy6~BRdBdvJ^V;TNJgyi)9ehpp)tnw&jg9 z)J-mrtFNIKoy?AuY!%>()F}2;-jBQuJ=+3WJ8sA6Jkdn9rbtpe-%?qXo@!p@r^-)l zgt!Au-y!p5z%M)>A?sX3Yt}+Q1!pT>#6s{^ZdP2fDFsu+RYcYl>`7OeP7y|DItY@) zP(2lDLfc)BOP;m%hVaOe)9Arb#OBy~Fb&EYWurcohsUFcE2r>9nw zGoq3SaG}`EX~fx+c!9}qZ$2GAjY}>#<4tEPfEj6Mvfxid)!zOPNT_yUB{M~2Eqwn} z=sH#R6{Y|b6zlYV4xMT$_Lq_TL&TgSkrACLxfo4qR7CgW$Q4%cewWJGYRGtb=5>fCqXZ|Fes<&Tmnxu+Oz6A&{?#Mcu|P=O!$7$ z>h1{XK0!SraVr=7XHd$;txw?KZTC$7S-4B5UL@1#_m1#jMqrsgFvAT$N_DQXbdcaZ zl3+l8It{vfDdE}f@+!iejTs}TIA>G~6_ns|R8+$s!3TLsg70Q`?Um+6@0{R#S$DTW zjXdLC4=1w6&UYGqZs|rBEGvFUCeLj!Ka|zVAoS!v7={tvj@OGGMcPdmmK_eap)n8` zD(M~)5hK?X*)?!6NSchoJZymQ=g^~G5=-{2HD+YAeubqG`n5fU76Otqw zGRsDGh>#mbXHvbPNg`yf-l_05<#09+BtUq%*Kw+zam~ER#MCYhEDeCclxgs6lm#Y} zW~_`aDxG^Nc+a@5@))J*K#X)v{Jl#n*_D;->V44)siF0tJpfmFiEFe9n99qQdkxzX zvm3ckdC7P+OOj|7GwoPGPEbe)-Q}kl=W^vZ%EQ~D+`xa>g^QOb_h1@q66k9QDGu%Ma4a_KSF!4l z#AaR&(nf;J8a2g^bgL8rh2x*tLX=KLrh=oPPR(E-5B5Yk@fM7Y zN;j;RieX_{0(p^AWwhaia~f$A_ex6=LOdpUN>)kA6pLR;;0iF_Gmmlf zsMMm5WLpxaNV&6z87honlh@YPRg^u5>M0 zFWDy;5YQ2rtw9`%6k`|PF2&9QBE|QqbK+4h(WCf|1pAgDB@@!q?S~G*>AEh?C?aLW zw6aI(+7nBx&~{snb_eRk?vmMr^hUv1q-uSF!h|@nu?$>eRU?}CY+>yIHH5I~K&@ad zd2h}cOUV#gDGC;MpV~MI(bvBh=N6QDZ4GHu z1*Gp5h4rV<7>SUv%^raJH~@u-Yu7sO!y6B0D;CG~) z^5qHPR+Ml-oYhY&i;pIstejRoTX|fNZp%FN#Z~wk4YEu4r5O?kO72$BPvhGbLaj85 zzLex(;5ZOf3U&rE2$dg>MM9`}!GSdzR#ZeVKAOomKlM=s~Fzh@Y zL4bRRoF8P(n8oPhl-r9CojZX5z2-@i=1G(0P9P+u^Q1|*x#mfe++>V6*@G znCa2X{?2-c9Fis^0<(p(cVGo2vKd;B=7bW9sy1e*$%by11R6KrR&3p}YgH0TCo=WE~*&+%7sI7s|JV zg%u5$!!lz9I544s1I>ziqe?OnSkC8*Qs_>2^fa8s#Co-Y8#)nr1u~uv09b@OWATXZYCOOTz>3~@)Q4*cwXp%I!)!ez8D)7+ z$;$EdJBSXD(sN`dOGh25K$qaJL(!S1=e`yCSztoOX1Xl zstY(I!#0E=R*_*xR)?$6de}fZU>3V>!V`w?ar&|mEaerZk9<|6xH?s7GC)^=R0OZR zd(SQv4HlTBTC4^?4&833$kc&SX`0VSlvv?l3L zqjKxx8=Mc^{;*iO0O~WZGWU2`^RQ9CQTn@lCCa zWJ`wbIomFrp+Z}&x`o+_j+)fBJs9MNQmbCKJZ+vp6tA$gUCqdhDHv3tmnu&T=e|-! zg`xyoxJP8JJb07s02+k2P4q_<=>&C3W;#ik_RDBJD=~__Ciw{C!&*WlP*$a7l9iCC z{9RPb!L|A3CyW1}ys%FezgK?r-Sx)zWROH_EBpyS(?Hmc#s(H(BtuWaQq_XzfVuYf z_LF&hr4i0IsLkz$iL!DvpZs_!qnn5lNoiUL<`!`F@n4LYAmM?zcOTKp&SY z$bYA9(&-e!&3yATU!@zQFf`oZl^B0a+Fu-|gu1smhh2NSZrEAzw%A!EkP4g*H1<;I zBH?26UKH0M&ONZ3JiKH;XT{#aI0^MQ3s{u{mSBcHo)yH>uFeN9mwX&RCht5goPGel z1Gn}K#9hKrx)nwVmo&8oc3V0pKsKrXci-QF7}3bYElw~~xcO*9!^O=!(7U+53;kTb zUKVvM698BvW{-`2ClG+*4o1fgFc`gTLwHE_O~G|aE+MuBdZGBaTIZLAUyb%;ND|El zCx{ZV`1)a63M0tPz+MV-O>Dn|MhgMiFz2(N}SYhcaPkJl9m?yAvrCJa|4NI=Heo^q? z$xoaurE`Zg9nHu(deT>zqtSVVImzSnO@$+865O9OpBs(lk;C)IVdi$98;zh8nOEAF zM-I=8Myeg08;#CqG=lJ27blA-c;}0Dz*dbrc(hJunGg8;WMDe10oOM@@O}hE0**5uy-iV#NdpJ50ezbd>beD%_ zd!5Gde#=trSIzN#6f2hiK1`5e(!?YoIH4BQfdzoV@p=U*e@|J?66t=-cORAEo4$RV^n0temfNhK*nA0Ux0bHKso^RTD>euz5d=&&F#ZE$1Mfx?-JXH}^p z{CI>iqzI3O+T@T1I0jTeJ7dcAWbrJ&&J_ttl<{{9*`lNdA7s#&pD8YUT;S=Y@dOHL zA1TH`Jw)7xbK?Z!$y91_ZHUnvN@wD7Hkm$)MNhreEy_1Q<%&wf?D`n{K4T6hKD@IU=d+kFe|`S}~)(8n*M zJ%7IW(&xV|f5;p9m;U>s^uw3#@8<`PAHR_B*tzxL7I9+PcIqepa{888ne~OA|Lf`P zKC`c1{TGgD)$Om@hJyO$M(m|f8|fAe>?W||JC$yKeyNa_TNlz_hW1Kv%i|& z?)l->7cV4U`R*VFdu<|;JekAAyI5UY3I=$~ zc=RO*GF^eE0KK096N#KeO5JYW*@H6{LfILKu!KAl2c8NSc_Yk&8{WKABU5W!2`qOL z)!Uj33mVskq@bS1pTePdUJq+t4{Kfz>lxO=nkOM|bdd2Pq(Dmye#O)P^CaZiCyVVa z4TSiAj@XJ%7LiXjZ=#}(TKUNvM4xJeUbJD8>^2dI)>AVj%#CDE;JQkFhUPkvO#JQ? zOC8LL;;o8I2A?1aYTo~3u`tNomEe4?1c9NL%wnqHf{Zp(2fifdlDH9ws{l975>AV^ zK(yUTZ5`V7)si0HuCw<~&Ol7=&L3C!je7cWq+6*vsKO}SP{wREgr7wYEkooE0xH+C z54?zscr_DUiIOn7TTvPFD6nkHKx++IL=;(%QDtaoGZEk0Y#A1E_o^1|kRG|PoFhFu z@+{{Uvdvptp1Jw3)7yvbd}2~z2JxGv*agps%?+D_)Zyxn&6(TOuz-)isaT2JxYKH+)v_2zmJar zEz+#_yFe)kJ_Z^|hD^aA0H*>e?JNcyoHBEu8RF%J{hpM@>e<~g}Hq-R;+)CLIDgBg4jT%R+xyO-RN`m(^*Csbw;}>KmfBL#d^= z$|#jpEq#ZxDG*%-|I@RoE;xLfx3jl##AE(L?295!NE`pj;(G{Tm%g&s5H^nh(zj8& z3jyrfGsstVWn%;7={{ME2XoroAYPZI#ySQvJ(H$2o)!?WAuy{@a0Wmj6|b7;`_{3) zhM8SXXVw6b=&lB*C+pl}cNu+(@*9I5bMTWg9gjg5H7%W`TH}V;TWANmDAC1eTHzO^ z?Xbf@ftm6}V55V7o8OL6Rg|ibs?|msDvQymDGJ&jOVeHW(rH?`ZF1k1Lp62^S-Mkd z$bGBB^14WjklK%FGbh3^OdG`6DU-n@1o-5{suYef*P)O>FmBHHAslzlK{nT+%ylSp z9m-sXGB4zY%okDF0LGB{oJxufPZ`Y2DCJD?@eMKwtY3<(n9%!6G9&ORop$qqWtd6E znd?vnom29;%E)fNIHlO3Lvc2&LP?>Pw}fL7AtotFC0ef$ii%jm9hMh?8Z4#&B|s#W z!0J;mLqSJ{VY|%f@y~(_kHBd{A;Tn34f^-5O7L~k75v)Yft7zvCM9F;#o%y0gaAb) zoK8oP8>OWv!%^TTTkDBd?shvU!ib^pMsHeOxyx~nI<*+&u?6Dc(ITE{_tB$X_|>@QthiVUFy+(S-Dn8FDOHmJbrn9w+QB^PSDGJ9CCy}&?$ zAUp3s0m4mZqUEFB2#J=u?S3CoEEMkofk_f%Bh?c$J8RTn;KidJBKeyG23x)br?K$f3Di)o%jFdHKDgIgW<_4@TB5(bh@ zj^Ruy)-Nyu(|jp%Xn8RRL`YW53`d(#k4)@+5UOLABg=@eJcia|p!unP;N^Zo>Vyk2 zh}Resh(I~a-2Y)!L@PT!SYTWl=V3F1uZgPoB#~0kKLp8M%(c=-+!N7GL4DK7T4Pl9 zfsI#Biy|{XME2T~Hy2aZ>8O^h&ZR2*XjjSoY#Ap^65JJ9hp%}6tsH^s$WZ+1LTv;~ zS+3@pC~7J+hN`?T8s>zROo*5jwCx~ojF2U+5W8QS#*k9FwMWXXL!`i4x_#r~($WQ? zA(A~AO@zdFVY=`_LJAT@oX`)~7ExD-5(cSrgnsl%P17QahIloohkyd%Huu*-eVIqb zi%(kh8xp%@^4@r2<|G8|W9k64B$j3_I_uUDjFhH;8p}$ z6XJbR+6s+0POns^TEXpgGr5a!x-_|*7U<-rl`_}Z=r?tr8Ob(rrk*TQ)ntd~ieIcF zyb(&&d{Dg}?g~jF+4^X`wVOTzJWrsKC0xN_edCUr{4==vmPO%BAI28460!h;ZbVk5WCcPSHQHny$qs@42*+X#G$x@JOH1+6LP}Mz z`I7Tf6ZxUe2VyDHw>`1o+NCSmF^e#P0<#{bgk|=7BOL8EC_z7OJXle6z2CsnkAzRk zRm|*sEGr@THzk{n=87lw#k87!LnpAdTCwfcqbG;*6gCrNRtaLtRVKqKRj9E|)Nx7D zqq2X`gH0+2jJ|J$fq7q)F+>T0P$s#Pf3dZJm(;}6%_;dcP}T%k3A)fh(Po|4*~NjC z$kI44tS%8;UU3rix@Bw^l1`F8*Ns*z3uFNIRK5&0agxRfMbs{y0f8&^R+CJrOo%0| z8Ky_8sOM*OW-?t>EGoo>BzCph{H$f7pMdR7ZZ^STmLq!?&ECMQpnoPw33E`G3MXB< zY2fH!d^R(QA)pzCsrAG#8IlM~7fJVWbqN=1j4N17QnW;*OS-AsIOmq1_F|HK3!PPN zTNwi_!$4`<)V2XZLor%Bz}vk}?@(>FrG2Z-ja42j**E>F+_B<&nzhuKZasp`2fd%e zK7?GQOrnPw;*M2LwWLT+F z%LhyjiaP*8H^c2)H|YXzncqSZSQk>T|SGP)b|3KjCFOcnPrrU0`&44C6%ynONc^@|Dj zi$vD3Qgm2|ioDJhQd-Qtq$3hNDkQL5iI{1qBHfUy8p~2l^S%kT?b{K$U~Yxkz`m?|2$hIZuLK^PsoL zC?ZjZS}^_4isDO+$RzYWhhM}_W2~p$Uv^O^tlV=a^+z~~o>+tfs?7Naf4YU%-72Lk zkw8x?-#MN7&g05=9#_2+M9DP$2{6kvs8T86q&Ikw^eA=8bn39)BTu*pehItreYuXN zF{m1erB@G`QF;*Y!co1GivXZx5yK=&G;!OYrd{B3G40Nlrb3B|#LbLXjJQup7sM?P z^3X8|oEUmR;@}zgfZvh0(KaEY6?H^Gp1q;0SU%v4ifvSnq+|ywsdt&G9IYxHo3IJRJB`gN&|VGGCL~IA7AfkXAhqzI$Pp+wi%gtgyeH?E z`~yc1ylv!Utg{uMKj+BHO0=)yD+vt%DZB{AoGh2!_{8E6d6P_b%TDL*~pavl6;boJyRYY;QaO>m_F#u8F!G#zIFJ*>eCMl`f52# z{NeHwz|*!!*m;^8p#qZ1wt+t|Q%-H&&sh_tPxLn%{Vd%*zTI4m{f(ur zsM61$Ani5wqT56I>OfRLGHCw6rYjx$L&F=@_eJDR6ps_fpKQofbw6M&ezr@5!rPO_RtxHwUMf}GRDiPp(wGoh|GK|bNqRC5}+v4l+B2sBU?b46)drRXto>EVPI zpe1#8TowI*oivb6{lNv7sc}q)h+p7xA(_%TIfQMOR05b$vS8R8@FlUP$i@X$Zwto1 zZ9nRh?Udb1dXjv1->^XFe{wKuymN&)QK84PVHpw2&ZT$CVaPnYW8B{bBO4)$c~>%^ z=LtsMd=V<`g@w`qgPV(HxRf4z}CnR(x=%^Ewvd7f~sT5EXhiP7bm)5SVU%vGE z)obf4i;MW7H`cCPzWRE;`bI+gpslyicN{+BG+K+-4`{@QN7^O+NSi#AWNjk~brEeD zEl;WpVS(p5YaDoTnUDu+3ABn?_anIO6kzxsU7I z#}$-4jVQn+=<*K69kPm1nVVX8 z8~dz4NctsiZ7;5mt8*9$1s9FayR4XQ(sXd76VC#M;|wJZb$#==pDjOxKPt>>!n%G0 zmy~t>1MehzoZKa>!j4;HW+f~5RhnBhoG_>&r(`{knnJa_CQ4Z~^nkN8?QHNPNahz_ z$0Uo|it3ppnu|@y6#FdxiU@dmyFf^Rj3LS|l&ozYDKPZ1TZM$KN!-cpgV9Imrbwd7 z^YWqD^kr^U4E=7fp<${emS0)<+Z^=J*xqzd6>;UgL1SxX%Cv2I_2%tw&>;k7+aVzh#|&(5eVR$=E(EBH}m zPxS^64%WEYc>tb5udNv-=E6x*CD=nUcTwj##CAl76m`8Ym$GGe3 z7K@jjdq>BGo~6Z}wRn=gZ#0ky7v+TLy9Vmthmp-}mZ0qiuo^PgoGJN85jl9z1WE8{Vm-A(N%E19HMJ4nhP+ej0np z2*J_Ru@^i}7t9QOZTTK`54D(Dn43HFSK_M7YX(^yN`!J#@jXr^2*+CLf3b`p&GW!D z+{P%38rd>1exEGfTHpL+ktawU-F%hPr1g*liZ4UKQ6o0*xVE5aN(=K|O3uwMKzzo& z!v7kzdtYb)+EyVJa#c!1LrD-u)it1#OB^_E3>P|G5QTq9tq3aV(%jO5vz5pcO~iq# z_hU9Zg;UruWDIe2_qaLzn2H)8_ByCIiC|D1UEDMI0HGEmh8WI8XftX}0+$+4o>v;& zJ{C_TOGy|Zy5tIoxd>g4(Q^@6^Eb?Hhy_n0q+FEy^E|*any=@1faiIDnYQ8uweJPt zu#R10&I@Y%oQs3i3MRe4>(}zW=aSM2{JU~HQ|vJ|r8z^HEvcq;_Ea=;hBC0nl;Y+# znYm3SA2i|MF=r?#+$AJcDGM|Nwc6N3cUCNsFlQ*w1GPCrIcF$eV1{yv?c^QgCWQJl z7&*jxGS#kefH%taj2bVcy7V;3czf)wGDnS0Vs`dP+q>?wM%llEH0DpX+#%k0VV8R+ zM~vL0@d~E0ZtJAo8Xd`Y!$oWKIJsV+o3*#Zhy}BmwSG!C)b?As|1zuDw_lo{vHNx4 zkg!upB_;5HcABmiQy>ywc*Y=!9aqA{W;Q!i?1gi}1*&1>1EY`ajBtrBJY%@Tj(?GG z`2kXSsyQj888g^{Px#^-#d6BTqdf_ok}W=2fxe8a zw*`9h>>yO#eO^sOGc@Dd(9h0{UzlQ@ z8DDkQZgKQE{8B8A9Q;MMH_963)<%t^nk$Fq%AvV(Xzs8(ci5df>{@Blxx=nIjKM4A z2^@9<4;!qFC05O?ji_g7&Z^Ja-uRLBTfy;G*c(l=Q%J`JcPHe9%C@&W_K~6&t><5Qg)gUVCb1Y;7kem4)GZsgq|Wh%Aha#X4cX)Drsn4?LaX)(ioa1 zyepZV6xQ8D{Scnbyn=05444nQ(PZLd26~uAC+W4VZT7THB8AO7N}BGCCc6Nrr%v{k zW(16R3z4h|^@vekvi%TMF`idKxLUvS0*=J3i@+5vNlDy|-_8uy?XKm3WnLeNSRNt= zmL@7>Ubdp4-<(9z;%Npz9V7%rGZkX(2y8Mj1gJ+jF`1@8gu5#$63muhF+oZVV!|-R zNM7v|A~&K`MXrR?ZP80U;GQ@K(-{(}jT-c~l-{etagpQd##yNpw^LXAu;=w;$pTk> znmbACtkb5l7X~p}JvDnL2_%NaP}!1;vStYS2=36m!4?*&Gg8|4rzPN(2LyCcXvpYj zPjW8f^(1Ft`2~#@6Qg{(o|<{rI8|5AST+-72au7Uh^HEx6bjNCGSZ<1<~;=p(n*3L z_hP=M`sgV@AYWXkz68I|{hZ!5C~4C{T)PzNDWJt2$%I7jd3}T4={pP+ zw<7H2@5SAgb;w#R$zY1Jy(4)INwQ0gS283+&qvZuyVo$lo1wf^qXQ(?ju87tPhKRm zwtA>}7kNcFWv}l{7$<4W&paY&9q>u;Ys`9tRqe4TV`Kq*@l;L2%NxDU_!uEKL=xaU zH-rcoDrUKpKn+z$P%2Q0$puZ~XdpPcwdwFRBVkG%$w>E1fLjeI#RSYhgcGC{t4r*Q z7uJyD3T$p^T^Yrd1THDZQc4C8!wa4U47FhiwzpIFRWCiFvTGE%7m5O~nn-HN3HzZ`ZlS}5wW}cGNnKwVo>G|!wa_4P<|Ce#&r-0zu>^m4K$+%%jE_n2d z`_4^PX2_d9?``*Sp5u|fW2^r1p7<$1(2bYdHlj9&F;&1MQ> zhskK0b{)PIW>PrFvrl6Rn8P&w|Mt$NIgTSa!=w}&u{ZnVuuq=A!2oT1kkCq@g+j5S zNl{B%5*Yw@b%5xJ0nh+uIfFsW00h9@|KO{CmVb_qe%{Qg>gws9!2kpmyHYE5L13o3 zx;rZ?EA!2-IA$@DOtDs`Tl>+V6V)}PSjZ(y7&R?B-Y+Ds0>+&j;V?~0<2i=$<4L9x zm7ACL9?IK8a=RW(_O)~v2H^9Z;D)y}RiC)r@jx81XiW>a0{O87I_e&k8Li>o=1`a5 zumd7KFlB|QJvyRd&OTLtD6dP~E~=7_sYHCD)Y$Tn;f$e=OcXtwp6MJSf0mlTR_l}O z!&0fTxh>aEb5oj%PG)vaB}S5dn5MKeN~>-aH5jYIU7k#8RRBG7($RaWOE?EN)fD?9 z>SRTuqJKi^zP>&*H7O=%hMdB+N6K0{=S8i)w-qkt0}M2h6s_jpgp@@JeOn)v^osF> zO-~Lr;)AOsu|NxTDj8g@cfO}z2&!qIj~3>;KkU5Xd8};^d5@hzp;)6?w57IJT1c}r z;xS*Lma&U|6TD{mh#IiEg zr889eKxb-H^Q^AF_0BPyKXj8&1u5@amn~l?YAf41Q?@Fzg1v;JZ062=Jg^cXi|FGO zX|tVXzKP}_I%?%?`&l_LIN$cAxSLR$zOr!5&)x7~#+|Ale8D*8um-Z?Zc??4kB#FZ z=Oz_4qs~IA=;NPKOxZi-+JDsU143ilGsgz&-6qJ~2oz^!H^G;zWLBY+i z4B<|ay-|Xe8$3wpjM>ZYZUb|~U7$PJX)V!P7GWZktGd<+ z3_|s9Lbe-0jB6xC(|t2BF=sW>IULG8dNF@#3`wK2H>}$e&G)3M9f}%?Vr=$;2I92p zN2S4ZWTT^15qV1^J;^;a@!(*lNlvED20H>-g*I2tn$t9)uXiPm(u8*`jAqSS(*jD$ z-td)fDv0Jk(a%8)&3ePz3ZXP#wlWPOsA<>@1W>{*7vJ_Xb%kjE)2c~j1FfjZPI|t? z;@N&r*1L9P&kW`M{`sH>VjJ|eDEz#!&$N>xh?F@p1nr8>KBZs?ai!YhQEp^Aoac#7 zl)4JxsQ)5r#`I%0I6T}txtLx*JzZ@V-|(?-xObax|1;RK(=T?*otja zu@zyf#K#yxe%|_m?G=L&dyO;Jln9Q&43omch&k_0!faQRpjD4Vg*ad)2C>KvY4%Gx z_JEmW4Qal4^5Wb3{Pw@6-k(Ol>%V88`@er)${^$E=z?!Uukq)@-Vws(B{l!B|ICT) z#)o4uW`JfWj1^u|G(u?1R|R8yt}g!F6WN8;*n|8|nSWUyt0+@%|4p!~&@hC0SiO3G zmPc!+#}w+OwOe29==bRtYvgaz*=)X@8P0RJa$cMiFKxawEE&zY)ox=#2`ZH%Qu(56 z)lY|KU43M`;+ldqB|Z(^aV+=s(g*6bhLjb@1Y;gSO>LIY#LN_}4JY(qgChNTN9a(g z|4ngv=C}}R;$7Cz5xsanrmQ+IK{gq+vDKUVy}8HHXk=|jC}0&zVqI()(^JJ=EtgWv zM~I984(P%~MOTf&ZuZh}^JsF8GK$jeD1#Jq3N;LhFF1UJZE3dwzM^ub0f$P;1-sb_XI>qZ;H;eYW?4R_;l{nCZ*Sg(s0dzkv~k2kT+w-k*ZOaM-7 zKb3zWq4;L|#dE&U_^_k+kn?b#tsM$1hGy&rGHP_TPbBpj*mxkeg&Et%YlF5hV=@k` z5qI8-uPw}YjUJ2yYU}7MZGpN^2r!TaxQM=0qgu6&&M&S1=Zny;3^$oOxKY++KFHJV z1@@!7@bdkTv~0a#Dkgi;}SU&r#~)D^`kD6cC4g)=OS7O)3e6H z+iu-p&SCYq{{ch8Guq>W@*IjVR%Qu7%wX!{F^zjsYsnCJ6MFV$_rW|N5IV#{>jmPi z^zLStgKEH77HmM7Z)ibJk|EJK3jRSSmdS*j$xlQ4Ol9T)>GjQXHoCa#kCb6E+<^!5cu{yppbU>E#?d1g%p>4}hH3pVF-e$wp?P}7iGk}F zjTh~7AE$YrH8~udk01}#g~+xt(Ps$PT`kbFeEK_BsLJCmykLX@ zzRz;yFY&%>R++1L;jCeM@N+vSRrn8{#%1gZ^Zt|sS#V1kM7@(Sbtb5^Ka{WIz#hUd zh$$%sgiI#cl123RX7iIU-)-!EHqWp(d81RA-W-+pa6UUCSaV7aD&jG~sL`Q7fgHK{ z^r;|jT^i72pw3)|PC6@gKRS|~pPtg`9#q}KdD)=Z#JrbvC%C{jcfMx=E!-m<2O-W_ zr&BmKENZsb*?ekmWP81bet~3RMFP2uM8n=2Vm?p(6w%Wlbez@zrBKsgX|$*mW1%rR z1xy5=_6I~X;zi7s$74V#`-b8JzN!#Xs9+?~nV=xolY(gFoD!WC5ovUAJ}9h}PAtr6 z4=!T3>M(NIvZ>udZG-pKz@!6g~$7pt{R0kUGL{NZ0id3qYHIwq?9R~_1(3Jit{>udQOv6&( z7jr-Hgd9K5^Mf}^(@M1LR3@_oQRQ&Un7^7bl0DJy+?zeaN&9u_(!L-8YEYaUg{rtG zricX@S9-p?^xD`vsMK4id49K%u}DqyB0D=BOzWwy6a`hy&HMXzrgrLusu^k}N+3C$ zKn4v|_yW~tqS+jkb_TeWfo%~+lyfvVH9sTE=Fg)%lY)T|l*`r5)oKTQk7$D0mmX1&4az*B zQAdbMa9P`;^YZv=V!M_A6Dt#;1Kkuy*E~AQmsjtWOHQj0O`pC3}IxL zA*KYBr9IVG<;=3vnN zW#`Mz+NTd6YTbYH+uy>a)el^lm6A&1FwQ%}&ZYev%S675#(2(#p?l#dM8ilDZf-oH z`Ci6AsIv;Xx~(VbQQlbXHjb!QBE9Oo49C?-ylXqh85lBgJr*t!a^3#OyP;L%DCA6BGyXXW8)=ivhbgErTmI@hu1?c?;# zC*d1mFl2BjBCs^M50#r?6IewOlR8m$r3ZqDxe!_rr{ggK^cwc^@@f@9Q&mA^D>jCK zd`AK@!F+~9IE%@~uFT|KfxoqN-H}XA7*g2S?D!Qig~5WRNt-jxKI05}0HlZn7Z+Gs zxXF@I(uh95h?5Dp0oPR!1=h$ge~agre4n%22nzEFa9X%vv+=A@P^|Lf;k}kIpXjGu z#W|Ym9<)mv0R34T;QCXt^GwjsrG=SFs6y|6rS&=z7r}dM-sHH0iIP1V5oL__p7xTA ze7HOLQo_C#r6jtdd0*K8h79DiWCml6w^nLw0`yrZ?Ndh+Qh&k@8fwTG#mto60xXyL z?SKeL^x3naya5{frhfc#^Re2W}b5O}@q(R`}6FjzRme85Dk%m{$&)WSq;^O3J$(*(kYV z^5Iv|t0{h#C+GImvmT7b6Xpz5pEG9|@nAjgN}j}; z({WmM&x=gft_VdUgF0%q@&~OISvkPOi+5Kc(x*0*r6C_I;0{sEGc3FUYs8k_2qt8S z*ZUNOLg|uB<;@`usfF?2nKll;9`+~J<-0^QOzjFY8URs6H^K!8+eprq_W*?&@v22i zZcZabylKrFwOyP~5|%a`nJ#PWZ3>MJ#pFmepOdJVK_D|xswa}Y`&hNlS=C}k9*q>OwKwBLGKOF(pa!i15GgXx+&jb|#gsWm zDa#V4k!Q0WSY<#Ijec8=>mQ=%!Je}~~CrDk_&`kt36e(4a$H_Gn2^@J#XSLE3z>$={KJAQUyqtxAy$gv<6>a9t~} zNxGdQ!bq-bQ=sC#%X6>k?v=M#@{ASR;D5lhWg(E5rka^D4h;gH&I1Jd7k6GO-$JmPo00X<2j+RT-TM+7@p z_~uE71{QXW!mOIRlZ~veal8j@8v7c|b9 zCR>MSER-vOmj(8d1r;o%ZK2gfz+N~y!dARk7Gspb?d5tq?`HFV7+YXR92opI_}Hlf zokfb59=tf@NYX=A94fy{Kf~Q!0A_+@0)WwK^B>OEta$D3R4!+}G~-&WJV2Mn?ExqV zdW-UPLVrj~;*3EGr$lVY$8UQR)n(JDBw???8$PH=EuXBFs2>~&`7jbNIS;^s>C;1S z8fgkgN=%6HrQ%$vLufo;On9OTDi_5Mk*snG!pj;$*gzl8$mo<3^8uNK+mA;_BTWcC zmfXovsoDq{)fWUzN|o9$J&Gs@kg}k(nO`*}bvZuQcS*@+3dNO9NBHbilZcym=xeAf zhEMtc8QVX+q7mIh6{iGUq5YX6VXlhK!3bZDT+N0mIsDSQ$R5BpFX;Z%TS6;tcN&Nz z)FwV%@L4N!nWV&;3f(S#m$UwbqR zRPwbP=6&#VfJlq!1cltadcAx3S{_$%2dgKb&@M|jWW1$H?DK*JT% zuQ)KdKUls+4~58{ZqVAEJ0orKsi1b9+EmB$A_!`EGOf)mQF7A*7rb->z{_EdS)!- zql?qg{&9V{t!e#Zx==REjq7l~K6bC5odgjA^>2vnQ1gWP7jzeF{(>@x4R2~Se2#pM~-$zZo4+5l-L!M{#)Ac~w}6 zFPWn93E*s`&P&Gr#XwM0C6%((h`$I5-Cu4>cLZqF?=UVAS@XmI5sWS_-rjXerQA zprt@dftCU-1zHNU6lf{XQlO=(PjfAgRJlmGEo_xt|?)wa40 diff --git a/src/hdmf/backends/hdf5/h5tools.py b/src/hdmf/backends/hdf5/h5tools.py index ff605e350..c9191666d 100644 --- a/src/hdmf/backends/hdf5/h5tools.py +++ b/src/hdmf/backends/hdf5/h5tools.py @@ -34,6 +34,9 @@ H5PY_3 = h5py.__version__.startswith('3') +def create_herd(): + from ...common.resources import HERD # Circular import fix + return HERD() class HDF5IO(HDMFIO): @@ -354,6 +357,8 @@ def copy_file(self, **kwargs): source_file.close() dest_file.close() + + @docval({'name': 'container', 'type': Container, 'doc': 'the Container object to write'}, {'name': 'cache_spec', 'type': bool, 'doc': ('If True (default), cache specification to file (highly recommended). If False, do not cache ' @@ -365,7 +370,13 @@ def copy_file(self, **kwargs): 'default': True}, {'name': 'exhaust_dci', 'type': bool, 'doc': 'If True (default), exhaust DataChunkIterators one at a time. If False, exhaust them concurrently.', - 'default': True}) + 'default': True}, + {'name': 'write_herd', 'type': bool, + 'doc': 'If true, a HERD file will also be written in the same directory.', + 'default': False}, + {'name': 'herd_path', 'type': str, + 'doc': 'Optional path to HERD file to further populate references.', + 'default': None}) def write(self, **kwargs): """Write the container to an HDF5 file.""" if self.__mode == 'r': @@ -373,7 +384,27 @@ def write(self, **kwargs): "Please use mode 'r+', 'w', 'w-', 'x', or 'a'") % (self.source, self.__mode)) + # import HERD + herd = create_herd() + cache_spec = popargs('cache_spec', kwargs) + write_herd = popargs('write_herd', kwargs) + herd_path = popargs('herd_path', kwargs) + if write_herd: + if herd_path is not None: + # herd = HERD().from_zip(path=herd_path) + # populate HERD instance with all instances of TermSetWrapper + herd.add_ref_term_set(container) # container would be the NWBFile + else: + # herd = HERD() + # populate HERD instance with all instances of TermSetWrapper + herd.add_ref_term_set(kwargs['container']) # container would be the NWBFile + if herd_path is not None: + if not write_herd: + msg = 'HERD path provided, but write_herd is False.' + raise ValueError(msg) + breakpoint() + # TODO: when writing herd that exists, replace or make note that it won't replace super().write(**kwargs) if cache_spec: self.__cache_spec() @@ -1100,8 +1131,10 @@ def write_dataset(self, **kwargs): # noqa: C901 dataio = data link_data = data.link_data data = data.data - # if isinstance(data, TermSetWrapper): - # data = data.item + if isinstance(data, TermSetWrapper): + # This is for when the wrapped item is a dataset + # (refer to objectmapper.py for wrapped attributes) + data = data.value else: options['io_settings'] = {} attributes = builder.attributes diff --git a/src/hdmf/build/objectmapper.py b/src/hdmf/build/objectmapper.py index 5b8ad62d4..cf7dcea15 100644 --- a/src/hdmf/build/objectmapper.py +++ b/src/hdmf/build/objectmapper.py @@ -565,8 +565,10 @@ def get_attr_value(self, **kwargs): msg = ("%s '%s' does not have attribute '%s' for mapping to spec: %s" % (container.__class__.__name__, container.name, attr_name, spec)) raise ContainerConfigurationError(msg) - # if isinstance(attr_val, TermSetWrapper): - # attr_val = attr_val.item + if isinstance(attr_val, TermSetWrapper): + # This is when the wrapped item is an attribute + # Refer to h5tools.py for wrapped datasets + attr_val = attr_val.value if attr_val is not None: attr_val = self.__convert_string(attr_val, spec) spec_dt = self.__get_data_type(spec) diff --git a/src/hdmf/common/resources.py b/src/hdmf/common/resources.py index 135f123dc..0dc19f75b 100644 --- a/src/hdmf/common/resources.py +++ b/src/hdmf/common/resources.py @@ -5,6 +5,7 @@ from ..container import Table, Row, Container, AbstractContainer, HERDManager from ..utils import docval, popargs, AllowPositional from ..build import TypeMap +from ..term_set import TermSetWrapper from glob import glob import os import zipfile @@ -408,6 +409,30 @@ def _get_file_from_container(self, **kwargs): msg = 'Could not find file. Add container to the file.' raise ValueError(msg) + def __check_termset_wrapper(self, **kwargs): + """ + Takes a list of objects and checks the fields for TermSetWrapper. + return --> [[object, wrapper1], [object, wrapper2], ...] + """ + objects = kwargs['objects'] + + ret = [] # list to be returned with the objects, attributes and corresponding termsets + + for obj in objects: + obj_fields = obj.fields + for attribute in obj_fields: # attribute name is the key of field dict + if isinstance(obj_fields[attribute], (list, np.ndarray, tuple)): + # Fields can be lists, tuples, arrays that contain objects e.g., DynamicTable columns + # Search through for objects that are wrapped + for nested_attr in obj_fields[attribute]: + if isinstance(nested_attr, TermSetWrapper): + ret.append([obj, nested_attr]) + elif isinstance(obj_fields[attribute], TermSetWrapper): + # Search objects that are wrapped + ret.append([obj, obj_fields[attribute]]) + # breakpoint() + return ret + @docval({'name': 'root_container', 'type': HERDManager, 'doc': 'The root container or file containing objects with a TermSet.'}) def add_ref_term_set(self, **kwargs): @@ -418,25 +443,28 @@ def add_ref_term_set(self, **kwargs): """ root_container = kwargs['root_container'] - all_children = root_container.all_objects # dictionary of objects with the IDs as keys - - for child in all_children: - try: - term_set = all_children[child].term_set - data = all_children[child].data # TODO: This will be expanded to not just support data - except AttributeError: - continue + all_objects = root_container.all_children() # list of child objects and the container itslef - if term_set is not None: - for term in data: - term_info = term_set[term] - entity_id = term_info[0] - entity_uri = term_info[2] - self.add_ref(file=root_container, - container=all_children[child], - key=term, - entity_id=entity_id, - entity_uri=entity_uri) + add_ref_items = self.__check_termset_wrapper(objects=all_objects) + # breakpoint() + for ref_pairs in add_ref_items: + container, wrapper = ref_pairs + breakpoint() + if isinstance(wrapper.value, (list, np.ndarray, tuple)): + values = wrapper.value + # create list if none of those + else: + values = wrapper.value + for term in values: + term_info = wrapper.termset[term] + entity_id = term_info[0] + entity_uri = term_info[2] + self.add_ref(file=root_container, + container=container, + attribute=wrapper.field_name, + key=term, + entity_id=entity_id, + entity_uri=entity_uri) @docval({'name': 'key_name', 'type': str, 'doc': 'The name of the Key to get.'}, {'name': 'file', 'type': HERDManager, 'doc': 'The file associated with the container.', @@ -546,8 +574,10 @@ def add_ref(self, **kwargs): field=field) else: # Non-DataType Attribute Case: obj_mapper = self.type_map.get_map(container) + breakpoint() spec = obj_mapper.get_attr_spec(attr_name=attribute) parent_spec = spec.parent # return the parent spec of the attribute + breakpoint() if parent_spec.data_type is None: while parent_spec.data_type is None: parent_spec = parent_spec.parent # find the closest parent with a data_type diff --git a/src/hdmf/container.py b/src/hdmf/container.py index c41dfb296..6ae7ad6e7 100644 --- a/src/hdmf/container.py +++ b/src/hdmf/container.py @@ -330,6 +330,7 @@ def all_children(self): @property def all_objects(self): """Get a LabelledDict that indexed all child objects and their children by object ID.""" + breakpoint() if self.__obj is None: self.all_children() return self.__obj diff --git a/src/hdmf/term_set.py b/src/hdmf/term_set.py index ecd1d8deb..80e5e6ded 100644 --- a/src/hdmf/term_set.py +++ b/src/hdmf/term_set.py @@ -183,18 +183,19 @@ class TermSetWrapper: # 'doc': 'The TermSet to be used.'}, # {'name': primitive}) def __init__(self, **kwargs): - self.__item = kwargs['item'] + self.__value = kwargs['value'] self.__termset = kwargs['termset'] - # self.__validate() + self.__field_name = kwargs['field_name'] + self.__validate() def __validate(self): # check if list, tuple, array, Data from .container import Data # circular import fix - if isinstance(self.__item, (list, np.ndarray, tuple, Data)): # TODO: Future ticket on DataIO support - values = self.__item + if isinstance(self.__value, (list, np.ndarray, tuple, Data)): # TODO: Future ticket on DataIO support + values = self.__value # create list if none of those else: - values = [self.__item] + values = [self.__value] # iteratively validate bad_values = [] for term in values: @@ -202,17 +203,21 @@ def __validate(self): if not validation: bad_values.append(term) if len(bad_values)!=0: - msg = ('"%s" is not in the term set.' % ', '.join([str(item) for item in bad_values])) + msg = ('"%s" is not in the term set.' % ', '.join([str(value) for value in bad_values])) raise ValueError(msg) @property - def item(self): - return self.__item + def value(self): + return self.__value @property def termset(self): return self.__termset + @property + def field_name(self): + return self.__field_name + @property def dtype(self): return self.__getattr__('dtype') @@ -223,23 +228,23 @@ def __getattr__(self, val): This is when dealing with data and numpy arrays. """ if val in ('data', 'shape', 'dtype'): - return getattr(self.__item, val) + return getattr(self.__value, val) def __getitem__(self, val): """ This is used when we want to index items. """ - return self.__item[val] + return self.__value[val] def __next__(self): """ We want to make sure all iterators are still valid. """ - return self.__item.__next__() + return self.__value.__next__() def __iter__(self): """ We want to make sure our wrapped items are still iterable. """ - return self.__item.__iter__() + return self.__value.__iter__() diff --git a/src/hdmf/utils.py b/src/hdmf/utils.py index 16e3f34e8..d85eb5c8c 100644 --- a/src/hdmf/utils.py +++ b/src/hdmf/utils.py @@ -207,6 +207,7 @@ def __parse_args(validator, args, kwargs, enforce_type=True, enforce_shape=True, * 'args' : Dict all arguments where keys are the names and values are the values of the arguments. * 'errors' : List of string with error messages """ + ret = dict() syntax_errors = list() type_errors = list() @@ -272,9 +273,11 @@ def __parse_args(validator, args, kwargs, enforce_type=True, enforce_shape=True, type_errors.append("missing argument '%s'" % argname) else: from .term_set import TermSetWrapper # circular import fix + wrapper = None if isinstance(argval, TermSetWrapper): + wrapper = argval # we can use this to unwrap the dataset/attribute to use the "item" for docval to validate the type. - argval = argval.item + argval = argval.value if enforce_type: if not __type_okay(argval, arg['type']): if argval is None: @@ -304,6 +307,10 @@ def __parse_args(validator, args, kwargs, enforce_type=True, enforce_shape=True, if err: value_errors.append(err) + if wrapper is not None: + # reassign the wrapper so that it can be used to flag HERD "on write" + argval = wrapper + ret[argname] = argval argsi += 1 arg = next(it) @@ -321,6 +328,13 @@ def __parse_args(validator, args, kwargs, enforce_type=True, enforce_shape=True, else: ret[argname] = _copy.deepcopy(arg['default']) argval = ret[argname] + + from .term_set import TermSetWrapper # circular import fix + wrapper = None + if isinstance(argval, TermSetWrapper): + wrapper = argval + # we can use this to unwrap the dataset/attribute to use the "item" for docval to validate the type. + argval = argval.value if enforce_type: if not __type_okay(argval, arg['type'], arg['default'] is None or arg.get('allow_none', False)): if argval is None and arg['default'] is None: @@ -349,7 +363,9 @@ def __parse_args(validator, args, kwargs, enforce_type=True, enforce_shape=True, err = __check_enum(argval, arg) if err: value_errors.append(err) - + if wrapper is not None: + # reassign the wrapper so that it can be used to flag HERD "on write" + argval = wrapper arg = next(it) except StopIteration: pass @@ -615,6 +631,7 @@ def _check_args(args, kwargs): """Parse and check arguments to decorated function. Raise warnings and errors as appropriate.""" # this function was separated from func_call() in order to make stepping through lines of code using pdb # easier + parsed = __parse_args( loc_val, args[1:] if is_method else args,