From 2d0219b70694d921a9e29a8c5d0ee75b3040d37d Mon Sep 17 00:00:00 2001 From: "alextselegidis@gmail.com" Date: Thu, 16 May 2013 09:32:00 +0000 Subject: [PATCH] =?UTF-8?q?-=20=CE=91=CF=80=CE=BF=CE=BC=CE=AC=CE=BA=CF=81?= =?UTF-8?q?=CF=85=CE=BD=CF=83=CE=B7=20=CE=AC=CF=87=CF=81=CE=B7=CF=83=CF=84?= =?UTF-8?q?=CF=89=CE=BD=20=CE=B5=CE=B3=CE=B3=CF=81=CE=AC=CF=86=CF=89=CE=BD?= =?UTF-8?q?=20-=20=CE=A0=CF=81=CE=BF=CF=83=CE=B8=CE=AE=CE=BA=CE=B7=20?= =?UTF-8?q?=CE=BA=CE=B5=CF=86=CE=B1=CE=BB=CE=B1=CE=AF=CE=BF=CF=85=20=CF=83?= =?UTF-8?q?=CF=87=CE=B5=CF=84=CE=B9=CE=BA=CE=AC=20=CE=BC=CE=B5=20=CF=84?= =?UTF-8?q?=CE=BF=20Unit=20Testing=20-=20=CE=A0=CF=81=CE=BF=CF=83=CE=B8?= =?UTF-8?q?=CE=AE=CE=BA=CE=B7=20=CE=B1=CF=81=CF=87=CE=B5=CE=AF=CF=89=CE=BD?= =?UTF-8?q?=20=CE=BC=CE=B5=20tests=20=CF=84=CF=89=CE=BD=20model=20(=CE=B4?= =?UTF-8?q?=CE=B5=CE=BD=20=CE=B5=CE=AF=CF=87=CE=B1=CE=BD=20=CE=BC=CF=80?= =?UTF-8?q?=CE=B5=CE=B9=20=CF=83=CF=84=CE=BF=20=CF=80=CF=81=CE=BF=CE=B7?= =?UTF-8?q?=CE=B3=CE=BF=CF=8D=CE=BC=CE=B5=CE=BD=CE=BF=20revision).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/logs/Development Plan.txt | 50 ----- data/logs/Development Tools.txt | 19 -- data/logs/Project Tasks.ods | Bin 8134 -> 0 bytes data/logs/Risks Table.txt | 42 ---- data/logs/Version Plan.ods | Bin 9708 -> 0 bytes doc/Unit Testing/unit-testing.pdf | Bin 0 -> 50015 bytes doc/Unit Testing/unit-testing.tex | 131 ++++++++++++ .../drivers/Unit_tests_appointments_model.php | 14 +- .../drivers/Unit_tests_providers_model.php | 199 ++++++++++++++++++ .../drivers/Unit_tests_services_model.php | 186 ++++++++++++++++ .../drivers/Unit_tests_settings_model.php | 167 +++++++++++++++ 11 files changed, 690 insertions(+), 118 deletions(-) delete mode 100644 data/logs/Development Plan.txt delete mode 100644 data/logs/Development Tools.txt delete mode 100644 data/logs/Project Tasks.ods delete mode 100644 data/logs/Risks Table.txt delete mode 100644 data/logs/Version Plan.ods create mode 100644 doc/Unit Testing/unit-testing.pdf create mode 100644 doc/Unit Testing/unit-testing.tex create mode 100644 src/application/libraries/Unit_tests/drivers/Unit_tests_providers_model.php create mode 100644 src/application/libraries/Unit_tests/drivers/Unit_tests_services_model.php create mode 100644 src/application/libraries/Unit_tests/drivers/Unit_tests_settings_model.php diff --git a/data/logs/Development Plan.txt b/data/logs/Development Plan.txt deleted file mode 100644 index 88e9131f..00000000 --- a/data/logs/Development Plan.txt +++ /dev/null @@ -1,50 +0,0 @@ -================================================== -30/08/12: Τέλος εναρκτήριας φάσης. [ΟΛΟΚΛΗΡΩΘΗΚΕ] -================================================== -Θεωρητική επισκόπηση του έργου. Η φάση αυτή δεν έχει -πολύ σημασία λόγω του ότι πρόκειται για πτυχιακή -εργασία. - -Εργασίες που πρέπει να γίνουν: -* Vision Document -* Μελέτη τεχνολογιών που θα χρησιμοποιηθούν -* Πρόχειρη σχεδίαση υπηρεσιών του συστήματος -* Πρώιμα σχέδια αρχιτεκτονικής του κώδικα του - συστήματος - -================================================== -15/11/12: Τέλος φάσης εκπόνησης. -================================================== -Το σχέδιο για την εφαρμογή πρέπει να έχει εγκριθεί -όλες οι προετοιμασίες για την φάση της εκπόνησης να -έχουν εκπληρωθεί. - -Εργασίες που πρέπει να γίνουν: -* Έγγραφο Απαιτήσεων Συστήματος -* Επιλογή Εργαλείων Ανάπτυξης -* Δοκιμαστική Συγγραφή Τμηματικού Κώδικα (CodeIgniter, - και Google Calendar API) - -================================================== -30/12/12: Τέλος κατασκευαστικής φάσης. -================================================== -Η υλοποίηση της εφαρμογής πρέπει να ολοκληρωθεί σε -20 μέρες για να τελειώσει γρήγορα το project. - -Εργασίες που πρέπει να γίνουν: -* Υλοποίηση εφαρμογής -* Review Κώδικα -* Unit Tests -* Auto Build System - -================================================== -15/01/13: Τέλος μεταβατικής φάσης. -================================================== -Σε αυτήν την φάση η εφαρμογή βρίσκεται σε Beta στάδιο. -Δοκιμές για την σωστή εκτέλεση εργασιών, καλοποίηση της -εφαρμογής, documentation. - -Εργασίες που πρέπει να γίνουν: -* Δοκιμές εκτέλεσης εφαρμογής -* Καλοποίηση εφαρμογής -* Συγγραφή Code & User Documentation diff --git a/data/logs/Development Tools.txt b/data/logs/Development Tools.txt deleted file mode 100644 index dbba7d19..00000000 --- a/data/logs/Development Tools.txt +++ /dev/null @@ -1,19 +0,0 @@ -================================ -Windows -================================ -Development : Netbeans -UML Modeler : ArgoUML -Server Stack : Wamp Server -Design App : Gimp -Documentation : Open Office -Text Editor : Notepad++ - -================================ -Linux -================================ -Development : Netbeans -UML Modeler : ArgoUML -Server Stack : XAMPP For Linux -Design App : Gimp -Documentation : Open Office -Text Editor : gEdit diff --git a/data/logs/Project Tasks.ods b/data/logs/Project Tasks.ods deleted file mode 100644 index df6dfe754198b39bf6bb8e7cc3f13840bf1edfa0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8134 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 diff --git a/data/logs/Risks Table.txt b/data/logs/Risks Table.txt deleted file mode 100644 index 9b13127f..00000000 --- a/data/logs/Risks Table.txt +++ /dev/null @@ -1,42 +0,0 @@ -======================================================= -Εβδομάδα #1 03/09/12 - 09/09/12 -======================================================= -01. Καθυστέρηση στην εκπλήρωση εργασιών. [ΛΥΘΗΚΕ] -02. Προβλήματα συμβατότητας μεταξύ των Windows & Linux. [ΛΥΘΗΚΕ] -03. Κακή επικοινωνία με τον καθηγητή. -04. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. -05. Καθυστέρηση του έργου λόγω εκμάθησης νέων εργαλείων. [ΛΥΘΗΚΕ] - -======================================================= -Εβδομάδα #2 10/09/12 - 16/09/12 -======================================================= -01. Κακή επικοινωνία με τον καθηγητή. -02. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. -03. Καθυστέρηση λόγω εξεταστικής. -04. Καθυστέρηση λόγω πρακτικής. -05. Καθυστέρηση λόγω εκμάθησης Google Calendar API. - -======================================================= -Εβδομάδα #3 17/09/12 - 23/09/12 -======================================================= -01. Κακή επικοινωνία με τον καθηγητή. -02. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. -03. Καθυστέρηση λόγω εξεταστικής. -04. Καθυστέρηση λόγω πρακτικής. -05. Καθυστέρηση λόγω εκμάθησης Google Calendar API. - -======================================================= -Εβδομάδα #4 24/09/12 - 30/09/12 -======================================================= -01. Κακή επικοινωνία με τον καθηγητή. [ΛΥΘΗΚΕ] -02. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. -03. Καθυστέρηση λόγω εξεταστικής. [ΛΥΘΗΚΕ] -04. Καθυστέρηση λόγω πρακτικής. -05. Καθυστέρηση λόγω εκμάθησης Google Calendar API. [ΛΥΘΗΚΕ] - -======================================================= -Εβδομάδα #5 01/10/12 - 07/10/12 -======================================================= -01. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. -02. Καθυστέρηση λόγω πρακτικής. -03. Καθυστέρηση του έργου λόγω εργασίας σε άλλο project. diff --git a/data/logs/Version Plan.ods b/data/logs/Version Plan.ods deleted file mode 100644 index 7530ad6801852a9768268722dbbfba4541d43c5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9708 zcma)C1z1$g*QdKf0RaI)nq^^0>F!(=1VLC9SlB?84gmoP1!<%d1?lcaNkK&vk&v|L z&ZYbol~-Rs{Xg%4z0cg4Q*-X@%-P>PM!NU}3^+I>I5_im&dQ;1nMe^F9Gv41whIRV zLqOfUU7$8DE^rvc#tnu-0z8m*A}AX)3@w6kfg) z#H#s+Sg2UI7Yg}$G+p>9bd5YcXybuqrbaj4@cR!K~J1H3CuS|KQ4cogbT{W z-31#5_aBq^lN{?GthuDpF!_QZAQSUWqT6QkekoBALM<#p< zb86%>s^aV4?B`Z0b#~>U<*e^m?yc~3O3kL`Sjtx~CG;t#&(ofgrrtek6p%08$gQsP zNN*(CA{$pPta4-5V>@l!{QQNpY`220E7h->SG_MwE54)Tpd>V^&Srv~^c=NYSg1(K3T zycym~UhY+Q)~1GDg}fCWi96hjO>})d`q9pEh{>HZUwuQN(AkdiCgQL!eR!!2!yAZuhl^ zNLmvR*$#hc1`AhDTR~SknNiRj7jQvFU^HRZT{Q1yNwBhd>Nw|gwRcX!b1=+zEC0=Q zWDRF*d*s%51+p#EXY9bXOOX9tRA|FvaQ)a_XG2rx(@V?{88E27vt&xP_)_<50tiI+MHx$Y93Mq&llrsiVx!MG(9fB$wfSs z#8Zc}@4ESARn?Y!X|H*l;QCV$a6`Q=&SxL-y|TVPaU= z))*(}y`cv?(wA9FXb|;6#I>yzE-yMh@=Pk#5AoKE2?aYAdVd0ao-zaqUd-qk(ETb5Jy5=!>)EbGfp z>ZYtfV$e-B<&lVTfSUHX?Z@P225@mLEY%m%`(rinoW{6fMVI|r2-^3#?LV|vq$U*; zd2n-60zxTxQDYH)j(&O9H!exk$aFd@d!|t^BYDa1tp`2Vs4cjc(A%S>!ZFvFvmS4% zC$DRE)qduwNkT!}v>v5~&D&=}w^hV%MP^y-RH<7|Dp#$#Z@u{5k0eRX)z09`q5PHnlfM>kYHq&L>( zZnFTo;QTo1@*uy%M;S{F{+n-9`B+T?L_%9#wxj*Fd!# z$YcT%s93C z?HP}kE<+$y>Bk;taHT#Uae@6c+n=A7IE@=HG684G?}hI>jI}#wL|$BVNr2^?#ssS4 zQ{2-j7aXnhDEi~E!jre^Uv0(lK9KVyJ* ztkLUED!$N|CR5Tr;y<6oEz6Fk6MSd85e|sLD+%0Ima(N3)~V0gYBH;krk~FwmNu-j zhnY1-^@lktKMW1(h{aW7?c;$;GFk>{U7c5ArBhhVVldTRxfWWV?4P%-wmYfB0_RPN zvXnHq?fQVwDt|~BN}r0PgPspnMkG=KYC8>aapj9@`WO@Z53_5byHUs8iiz4~j4KTpk@30bV{`Q5E%?zdv4N z?uiC$aC@l5HXwuLNY;6G!T!jtHLifnH2%Ji3%cn8Gd%swYvhOpN^q7eVB9;P|0>}6 zEy}N!maEl50yXX$MuPTXV0IZA;gThJHy}r*pP|m=x#u zV-Bj5zfhObU;-Nq^NjOeBrV;mLCB6XQ{_63)IcvR1m#ycwelDS<;Kqie=42x_cDkn zC5ntQh3pA8wL1x1l2@_{`b1&o{-{dn&~7gS7cwc>JS4ge^1j1W6#zWs*%z#9P&{NN z!B8Yz#90mBb|6{>eOxS@J6EPiXfuxy?;^rQw9u_F$qr)# zOa``Xd_sj|Dj&}J+P|<*Iy!GPtEp87QJ-2VfV~IBDVkmr-5h%39e4!nBMQXH;3ax< z>V{R@hMf;}O!Jd^N2m6W?(0l9&d>Yr(LhwRm8)_yx5zZF;YiMT27g!zi1-jaGAc4P zOmyR`6kiEnAh~~PQ@Vl*-p8j0EO}cT#X*nd4l*MZBE3ozFxu##*`CsYuxHwQF`B(n z9U2V*g_1+9jHZKM?l5lXDW}Zh5s>99Kb5HqOr|b7y3Z%IBcV5z!h0KJHF2NF8G5?AY908Q_MYZ-Kj6H+WM20Pa>z&m5PT|14UKkIY~T3vg` zS3EkKm{JhO1VIl{))g18TD`pZZa7+`Aw{tgx3-!-$OD}yamse|k|DdRrMN5(77Ut>@$ZbI#WlDhvf4%bidVABbS*c-G zK~VC8t{VPwvDT-y9*!|8!9}O=bOQq$8$@Q9ofh2Yozq^HlprK&zjy_AEXs;cA@kFD zHgrD=!4|$~AFHt-43TivKz!1JUA)tle7f8jts}~A zr#$kJV1wM`0(L}ZidLL4U%$+T%}Cp$wHksI16kjZwOxQezH5dLHF9a6T*^rjlh97| z;1QEAX>|S_k%CTGr@>ayxK*Pf+08&uQ5sPUy^n89?ZvI|WSd)rL2|upu@%95;gJvD z)RMSK=zopQH^0f}VRi0&MlC>7T26^D`Bg)ijz{`%F<83wwD?lfMiQ;GRylghC)I*_ zx3tVRSW-^&T>9b@e}|o1f&8S)^b!b2stT8p#Ba3ehJ(ZBE)kvTh?iD$Tkv38x3k)-Fw&A!^Th> zXAI6*jGMs9id`d)jy3?!yo=~H@;!DpY?W+s}rZ%x7nk*(K@-(NG$Y`!Z0I)NY2MA+G*sRi`uJb=9lfb-eE&S zX6&A$EQq!uuQt5lA?`$rV7{=c__cEW9llwg6 z{-?+N2zwp)^{15d=h*aymK$KLG`AO|`BQ`M*{PF?b6P2GaUV`R&bsloGb7k_(H(Uz)asC7eBH#|uR&nF zpADB%doKCZRA6#@26D+{IzLU-KyqsQVfE58lRi%W=8(A!J5ydxY5zU$5Q~V|bFoip zI`i3Q$nz8#ch;>Bo(l2j@LGu2WW`4r#}vkxK@)0&y}ywt?%1PeBMQ5)>EiGhn_L( zQ7~P?d@LM4`_)l$@7oEI6frqSMudYi%64*socx$Y@E%Cx5MYlE?B^Sb0d=!EK0o9X zu@?>xs4E(aW5{y>MMSwcphyVH4u*7)=eh{i5SHdrR3MW>+1tY)P}vh`Ll}#J*l=J) zk!acPL-Jhiu1MK$Ac!o|1_4FOxUoZf<3}8S z3Ah-S!bxVPKYaSBP|gk_`(xTb zF<~GO`ws?!WW{A<#egDW(sF>4@ngnsfkL?|D8r#%9AGqdDLBCFVCZj5Cu83|{%7qT zqiZm<8w|qnEq-n`ws5F01m%u&ljjoW`re7f(cmY2K(Qa4DBI%+oOFwF0e(CzcQn*h z*bZtBLqhGoiywaj)Z*#@8LTlu znRbN=kHI@=DqX9;;p0C-TtLpgI+N)n9G!* zgSbRCwuhhT`WlY(s@wPr+#`|+OvP{?PL}}aHh075wfxJPuE`4wS()dN-8Q?iLCE=O z;YuDcN>BpQN!)zJR(2`Nm`{|J1X!#4x`0`xZ?eTUPj_RYY0GqqGpjag0d_X4I=A8L zr`>Bih4j;L3k7P2NYDEwT6~=|#rE4ZdSHB0#t+MC0HEaD+L4~C4%Z5IjFjt+#Y=xK zOQZWy`IBwb+xNGwrkgM5kD2)o+^A33G4eUL->tFUB^0o8p{5cgS!|5&Zg)lLL0N=k z3`=6#9p8qOR0~kzs3G~T+HlZ9eQfSdyGV*;k8l*yfEHRE9vd=1gc+&yoAoybOiv0q z)(Pg6q}!!hbah^r7fLr`hjurbQ=JdbA2OhDcoIHP=V&j3-$vg%I#O~3MxV=X)*%{W zo@k?zx@-OQ^Rw+44GHm0s+~FIo+2HaurDT7E5b-^Dy`vbJ5C}uUpZLaU%R+L*}<}# z`MUhHYtHIx---qyPA8^Mu?oA}-r29xm=g!|Dl*;kH5Ukh74VB)hPN&XI`L5GM{Q}i zc-A@OFd-*K9=W(U4?rD)Ih8w?T}&aYgzn2O>GRWBsvN9_7mqnDoHi?tM3H|BTN zX+mAn*Mj03F~Bn;MN)Ve#&E|wMmv$z5voNuT$d`Y3lmxohT4dB>#|W=Asc-!<>+ z%XvT4Il4o8-Q0rW(%N&m0Wi3hiv0$0_YoZf667TQc|W!z?~eH0Qa{EQXIf~^_ot1p z=`;$*aq19X+~cFT)4f;Up!CeB{K!ezaFmyWSEM0a4BTwQa0w$yYlBv1%eqVHRrLah zGWC~fKBy-Tl5??}%guhTEdE5DBwGZ<9grd9VdZ~RCOA8~*irSli@eN?>F(X_(+MP3 zfJ|>+7I=|mWnF*L+bVcvi4RR@*c@zTLj>l?xq449JAh0`_^BFy;*wMA)glF~)e<$kT6=^cc`p2izN&oZ69O!mms;npU4O z!RQZUnB6*7LQ!u7g^kO(4o$>;dZ7ZNSqnMC!usbB&VaUeelt)3R12VnJv7B9t} zx^5mjoobp#n;j#GUZhtyXg|lF&6tl_pjU6zcqYD0@xIYiF$A-Gq8%2PIRw-ZFw zv~+reG*@(7wzyF5_q3wJ`=CHTf}H9wiEqz$=BU5#E1CQi7cN52&h2R1dT!ptPxvV8!w&29|zMV=bj+wDK+qZmVumv%4-K zOq@6H#xU&SSB@5UkCN8{94#K=gbHMBa@2i~y$EkFY{Yiu9?f(fQGca4fM2xC!5l0q zb}8{%6F*sNC`1HTTYqT9J%(YNkh{Vo*d#g~n`8X=D_n6@6Po|Zw_6q;Z$Y4P}kLv_F{}aIQ!6} zx^b^c@Ii9`iJ3E@ipeMZLf^OLSDwgk@>^3By`NLx&)AyqN8j3z*?O|}`ZnHFfv_@v zvsz7+bh3e@d>PkMHtsl`uy~y8EIDG;DGQ*3XLY>5a}9Q;&pY_e`QX`M57utYwKdwv z&~bTr`Raj@B@=2)x=2Am@1>Ojl@3ky2qk;Hf})|h^x=_!!fK#vw}kxj@jaVH{~dT1 z$3D!IQ29gJJ9}v$;uhOCi}LcF)fl3nO0LDBuvGG7-oc&@C!t zqF76OP-!CYQ2dE&ykFj8IM^94wGypUrMk_$2ezvqFd8Q#m5vNiyB-`TknvTKEZ7_n zV;K9DUqvB5%rD~RkxB_kTF9LOz^v-Tp)LcloivHMyy>jWLM@fkddTu93MXKezu_i> zzuxYn4ta?IduLR8QFSO=vT<)v-OGxdQ(LTwZJEkb55k{ z;8z5OSXisABnO_RSa$UpanotJNUfyvoTur*+R9UcX;M45h8jkFfBUPN7m#*4$S{eC zm#ojd`Fg-Pj`}k6uDBXeh{bZcDCySV0YOmyVTE20-D})kYvYugM|_SgZ7>`eWj0RL z*kJp6tPhc;0b>h?gD8#?q9#}EY)N^tU8aw^94*Ud@9=3ysEJ46ZVFAj zjHiEQeJ6@9#f8q`x?c88zsxjgbLQ2=u}#V@&>iJ@ujErbspC#!M(izRCJGEU=wA9Y zJgRtmaO=tKsM0i9=7ZIs-kwxn-JtZNn=ESJN|s?zR42Jxy!D|u$C+>fZM*~C75vNV z;v@(0-`Gmb$4GLyUE4?)Vrs$H9d zkDkkIkvg1flfPCPh0(W;zQru=-jc&+WZW{BN=teekM4~yv)B$WXc^KvC@-OksjMzf z>!GPVBf_tLsa>P=u?40%o;PA6)khYfZJLZwY_4QQO|D%xaV1SRfpr86r}!&SvP2 zL7N$sIH&Ag*iG99^seMz(chaSytIMS#K5@ovF=Oow)5S=2`Y$U=@EJJo~Vg5*NfZt zMEdmQ2>pirsxutf`BB1Db!-(aA5^Z1hH@0uM!m6rbz}SDu*kmE@)6PT29nxTqCI4wm3Zupjr<2>sdo+Y)Oyev*l7JZyIaCf~x|0*7wilfm|!)#p-=rxg~O8ZZg zeX15S8#aqn{TBw-IhA?!bSBK(-LEW_CG(Q6rwZ{yMdcB=yHcOb*duokVoggSbo1v0 z2LwuKiEH^WcClZ2&B)oESSl-cX0qCS($1Jq4&~g`WHe8&8hY~8yMeZ^%)xzAj^7I7 zH)#^EKzCNIza5HSRP(?jUTm^{`$-?M&EJ*&tk3v&V17#B{5C~qY$$)%@@u`u-}M|DPBa#}yzy3650;`B@KgvSLmIe}(>zUlIMJ5bW*G7e*&?e**@8%5~9T zi=D8D)GRzgg1fsPl6Pl+dA}|9Jonxo z^XawbsyVuPRCRUDt_DIG0U=6SDh5cxt+V4hNERF#9Lp~zkQ^M4)I!E)b^se{Av0Y& zfB-PGLs9 z3&xmSh6|LM90>+~_3SJk0ra(T_~9H>$t#w-Td!_shPEX=WwsF{zA`61pSC_Qa(lKt zJ?|f0Y*OEBe2+`8yKmMmg-(oYl(KDf!}4^a@1^YZ(~(HKtVwO{cLf~pTRW4V3a4hq zWxlU2ynedhowK}0bhI`|G1Qhy-qhdN?s_CuVe3PH85hg^yxLry8nhTTefQN0shCNE zYuNuF&^DG*f3tR2WL7J->TzcNIKvZGZ-w&p08h`YKg=9vbqo9667dFTnE*O~WpqzDvy-${uly2r%Tj zd6XWibvNPdeFx=fXlJG!z9!glxAhRKy4|vn z$uXCCRC|B=anBC1o-f2j&jY@=Z5%92Q;NP&jRumWJlmR|A2YN=oq=~fA{+vfnq({* z6`nN0X)Wkha~*cnqvPB7mg0oVtgS;goah)@#f2pVG3p3Xsn?}ydR>%-48e&yx^|kJ`#YFC2?k4)GrC9T)-SoIcsfAT9C?j|1f z@c7s%qtC?Zi>$#wAHRCAClxydI9E*2BZJxa6-}SMllwF|d zMN1*L-qsUn2xH!p-=R z8%Th43Bx3Z8#by8)d0 zlNy}pTje*bj~)n?G8;?iw!+#wEL~u^cbN4c?L(m$+i9AQBLS26Ivn1KkA005!@ynS0n(_{F6^zkoc5!pCeuWIgFwET}N$_-85J`=^ z2f#19vAS0dx3NsNWCieZ`-G83lt0pvp2@p}2iE61i__L*t)$EH!^u*57s;xj_{owX z-S;bb>_@Ra454Lk%)?iOiSE&I%~Dook4msvaW1*YMqJwc zaFg76sU%pud$S@@CgjV~m~v3S(}yu*nrLLV>OeafOa1*&B~MY?&}}Mu^PrfGwSvf_ zKI)X%W1dy=-AEimidWmd-ptZzh+-MgUYguoY$ykguO-og_gVw%PFsJ-@=!tV7{X&| zL219cZus`RI=wZazcX8*$6mRC_&^xEsPG7bx%-!Xd5J+$=Y~Z|$&En8Sfvo()MO=Y zDM_f7q7_U32v~NR{ijocsys2K#6fimP1mm0)T?LMa-rwy^A4>RbHuf!qj0;_E9B*IfS{738V#*K;h^*%LPEb_r%?qA5*V)GoRN*Hty~Kxm-jbxpH+$IPC3#`62~<4G+}CCR5Nd;cbH zv*wIiv4$I~$WNndsErZW-r0?hIK|nEajs%MC70_RQ;+O}L_R2MyIdLR2`^wXlxfzu zuq=}S0*=17-G|XqQ|TKpW0c~`)@M`!xs-M8DC>}f8HvoGhbQP8o%@Oymc5|+x$#$> z%Ed$ndA=L0B`U3_je7ylo_BMwF+iR#Y|n?GYo2!~CPXKqkN^w)f2{g%pMItatbe8q zmKJuuQU_Wb#<$yYZ%G6l4)d>1Z`lI1jE$w9Jitz!`Yq|8RscBJQHz@E8UpzLYWV(Y zL^XeA7XJXJrTr8Aza%8gG^~G1Nb)dR$~Wt)ziF?3W+8SHjq~!Qd`|#02|77&IZT@;Or+?jQ753zKEwwHAs&6$%+;hbInV9oAfy?uAyXT9?A`i=WH_j&2Vsc$#7Qe#V*+P(AZOMM(;`-z!hXr;8el56KnfvN7;;l>2t&@PRf zQw>+XJzqcHf~>8)!|Y&gs6C(u@iSlfa>SN*+SdSU*~_q@iHFAn1O~s#P6$(W`2fY#KogdSIc+|KSkL+A3u=nGGSm=nyCKy&Bqo!a-xD!!hA`T{IYQ;?{5Wnnboj#!H`~w)Blv|0 z{CN%(7byrrM2*{^IGK<$&z?wLXV?>&92=jw1+}#~T9o`zeyOfk{G*EA{l^M}7+8 z;SyzRLG(o1olo5+dwO;8n(&u`RH&mld4_zA)vpIoH|_n43I;-ciMaYwg-B0OQZ>Nw zpO#u;0$W38;;7XK-5O3x@Ru$OWGx+h($-uksafU{opW_yu9*D;DtniNhQNWj{P+BF z2u=!EeGUy+?GrjaCX;?Yq}r$H7&^Rc4@y2guzWwpU>`vlKk|JYK|3w;n+cXfrvU}t zz8p3!R;aAd7$y2B90V~bbIy#w=d@($fR>uDu^dv>kik|^+$aq^qEO`vhJj?S41e{n z-pNbq&=qI2ms7_e?7Yx!Fag3U<=yG;(Hd(CH9KJ9uZM@)!(!8MFu+?WX+){~zjVy# zx6z%RbhzU;LtUgfs|!(2`QhB`^PZ(8h%?fwyH(odS2fQRQ%-`bc8AkSs>HK~)XQfF zPSBIDyiaq52d+icIS!VrI19_lRbL3TGeue5Ss3y)z*F7}c3Y7N*(F;?#13*?}Rh68iygUrAwDrx;qb$#3P34NCcO zTYg<@HY8)^ysO!f*q!`L?a(dm+Bc8Mbw_KuvGluhhswRc4SjT-xqjLi+E=$?N~{Qu z8L4{VkM3pq9xlGjEUBgF5yO;qqA>;&79EBtk98BO0xyFfuX}?4B5boNsP%{JnQ(bj zTm9~6p|}18vlcBpt-E_2WEEL>&`AUE35@ReP-Ek+ixGz^u!z#{NX*GH0C?2=@Yh82=b7Dbz`=I0AB z!$O;MBZjrK5PvE96=eJbwvD>8aKkmp zabb~0C^ZJjdrGW$D=+M|77oNNQPK-Q*U0pKk;G$)QyY(NGL? z9R3tULZ2}}XHT&0B+3^1ON!tu;WIx+ehOP&gmp~M%*1+@V^|l0y6WSy82oxss;?B8 z`fLmYyD_B-D$Eqym@yt)##FdaUqjBsy3_k&E16ji>-tH~Pjiqt7ya&;tMeVTy)8rb zpEA-3=O5=7KgDUB+F^mr+8^S8v%_ndk52S1LQ(;~xFOd&2qLgPoWFb-R_Xe>r@KJt z@`GgJk*J2EE}A}^oFOO7)wy;i%JDoDP zjS3pWD3l#Y>6V>(=yZ^Hp_cxBAz`)AnMTLlUa#`=WW2!Uw88 z4}=bHY_FxogLkSCt+N2_qw~#{8oD$;fMMWeVP*V{!G^qH`AE zP&fOSiNi=eqZZyYG%-iizg=PhUq%`5#!TrE7{84O$pr4s@+mSpjDvVTI`m4Nb>k^< zXhdjP5uUEw+c9J=9%mij#A|%IGTJlQufZOqC$}z>-%4Hezsfn#^7h0}B~yDDx}7R_ zs$53+Tts{_iN;T|JA5wwaO^Xm**UO~?FxX@EVaw^Y>kw1@p+E9=YwWSZ#=GMo!Z`T zn`Qq19-qdZ`^?hu_^G+$C3bNj#=utEkTT6)XaTeCwQzL$6kOSs)+bGoNXj0c5_f6p zrKua9Ezq+h*9<0O){iKMOP{sZGzH_vHM!j1)M8{(ehO`mU8Z6r_Nb#`XaRfE6^MN> z@gg{5S0|{;*}_IHoJ*fV*C{^v`p}A+4i);@8M%vHp*^b@%Wqwx<*SEdNBvYdSv*fa ziH{SKW_Y#}ke%Kvqj)f}J#>LDf5l}CIA%i55GSn6_5KeB=-XSt%cmg>5QZtmXQklK zye^e{BrV{FBL!q)N3;YJ?6p3pd{MR-0ZBa1U4v=zN@xAeQ*hAU&xY0n2bu1Dry&lC zOa-1@S;Z74O>}c@`090%+ww|B*i*}m_0*${Hb|3O=t21w^RgcfT=G?5UWNL!OM}@R zo-?m%`du2WEzD@c6k0!tE8mLE^__bpgqB%;kH!Yg9%HEggy-P4gz+AfTyTFXz4fH$ z@G@;X5fx2Fn6;~Mjh!^Kx`pmNm8&7_vNoM|Hn_ns(|_TZkV)2FdvO*vgc8Ko0I*F3N|LQCI_h{hj~g)W z6~52NFs2h8jBweB&uCnaue0UfRZ>=EX2!aML>GhDEVRgSVXkNDhHjHshBjnQcY58< z&T(X}T_Dg?sLmnADy*{=HgB@RXgc}W@Zh#5@}+E72e>d%kd|oo{ml_}{$sHTpLAPs z`MJU?FrTtg8gnM?GFLB)Cpz)#haA=0zSkGvK9{tpe@&ff>Hf@a|9{V&nf^roFS#=f z6W!l(=VR5Ox6M4bl}n^&V1y?riaG;t5Rs!OgFqF#n3I+-ov)TwZBHU;61OwkVj}N! z*&LeO+=?~I8>u|CuLScvuK^7E-1e$3VwT_XcZW_+r9UTr_MAZBgb{ii>f5}?|6o4# zdJQ-A_|T_F%Gv&{2em?MOv>0+*~NKWJN9Lfpi*LA)O|?ec(!BhmYdr{d~pOcKHI_G zncmteg5i)g>gYXN;w^JYK?&W>>}Yj2!GN0b2RScLwK)+~tCt{K8|wNCm7{dgz6}Uk zT)*~G>a!=8s#Zu%25PdQ;GJb3auIUXJWyq?RM}aXlQ{y)D1ch`hMs;-R%baeJ z$smg9et3&KB)ZJT*>bBk3O^ug)yX<3(Qo3=Z2MNS6ggYaDk_Y6(poxgG1sfALie(y ziC1^;zNrc|UF~4jHH!-2DNTowFjVYl9r+(p)qo?gfSCnt3h~H?PdLvqQ#o|9(!akbRCDm42~1JvEw8J;ThufCpuM20t7R-G8Y#$)iUu^=AUtkU z(a*Jo(aQdA?YmKF!t;C>l92f-q5Q%S;PbiD2v-(ePSwObAl?;xp?fuVVT|#y5P1L| z*d(nwCNCMwS~a=kDA>NZ(QGy)N30JlXzBct#HI7bpmy#%*rFVEZH{;@z9lJ8dxYP<@%Quotc=`p%J+ zgvzjQSdXmJZ)W)(F#(P(nG9P&$PqcR5oPq{xR>ECcvaaE6RUK>mEu94jYc%+=JE)a z3{W(e>(bZ|*{$43HJrN6@yq%-=f!1T^6S~xW?Ck0ow{bF-0bEKwlu2CP5N*gDXg7L zqOo@@uuLVYmx4-0VT@YQq%AnzHU)IVhf|Ql^hxSA(Pf@WA|cN!!~-p z?CnzAzz3q5rB~0>INeYq!7qvGy2yko`9j6bfFy`;TCRHe^QF-0$^m77m;Hdn&7b{n zB?1Mvg~eTo@a|kJ#)1gzN-I(X^Q*9$fK;t-MzQj?cFs^`YwauANQZrm7MV&n( z6k<*hVtZZYztGiW_n{&CV7uqe967rmMR=)G_?D`97`B$} z_tY!3jXJZ--vB9BU@qvc;QO&jV^AmST(Yj8_TXRik4jWa!eQlJ+|s$aR2O8A63`1l z60CzIe05}xU*oC?l+$_k|C){B(WM;+!eAQ7yZ0%eJMwJM80lP=v=hwIvr(ZY9CA{xGpuxV}a5k;Td zyIqH84a369beb0sm#{3;1tkCT3gOq}QN63cW#xYQ&zHL%b^#X(h8dYK#ZokZX=Ew~ z5!RJc4H5SH?k34R+To9=P9{+EGtI4CMVAajx*J3T?{DSp99@z$#Kki5*o@cho{-I^ z{gmbB{J6z^zQRg<&#Nt&ye-qY4G)SK3y53QVKNrNkXvCXtg=nBrx*F?x5qKEv?phO+ zfjl~MBXLe9mR_BxJi05~v&c#Oo^bPM&S!BBv}bVM)kUG+O)-|>VtLFoNiKI{mtLIQ z(FRXlz8%gNgEe9C{1*bXLFEAjO&SigWUUr~JM@Ot-t3s3X`|Oj&YmBgA2)A$O*je* z?>qN31)-mgi%b>~*&AAzidR>*uWZcC2kYucJsjvmMQ|p1ScO+Bq7T_x>9eTk971k_ zW`ZAxR4<`-5F4(+o3K2s1pCC0=*5*CcNIgIl}ox(zVqE?RHCDpDPqvUwlD0uN~6>_ zG?1(fOVG8cyB-voOH!%~O=`FYg^`|+$=|phf{9((z{|m)gProWnVd$minx^Aa7@Iwv+ayNsWjS9Ewqx zLWU>gF5(W@5LO3X_h20MM>o7)5+C&&5pYbrK9NJOgT0+xMgv_0-9bmb}D+Mz!X_%!UUW; zK(1FykxZb=o4*E`Tih-PKUkCQVJ2r}mN1)Hi!FvV6s{b|JYrF85|}3z%e^i&Dfg3c z-EbbLv{3J-_nnvR^GZ=R>6%|xHf6)!W~^jZ*e{Tc#hdsq1A7LP-CgY5;F8uwo50Cr zdmZ>s6ZjOo{3u>?z!0KwnQ z{Le`XwGcfH?O(fq|1bCc{{FLlZT5z9<)ZI9$vz?3zOAssp}Ky*Mh z^yAFJ|~6rX)~K^Ti>8WP@iUa zY7|6T>K5CM=r&^`OgByMZ#-)x>^FOQSW~fJ2HIA#CUQowN06DK-NLg3s%k6)sDHQy zkKQY;$|FAR^)Q`OByQmgMqDNAK?4IV_W9HsAy{plJ&avj3OJwZmS-7}zyl~3a_)v5 zR!3%}QM{V+ftqmhj{Laf7=xzQ&afWRsuAR{ac(p|;m(PfSx|JR&Oh+qCSp?;!Y<`f zBKLlbkz9>W_S-B!FQz+cj%1!SjqGU*Q?1@NV>{oWXmPS7EV&sDM!EK%HI`!12v&Yv zcKY_w&wy|`QRrbY+d3E4ln|+>`(!tV67pHdHqPW@_N=Ba%67wXCnm~)_H(y|g0G(EIXzL!vDJ?-hwieR^g>Jb zW5h(MA2u%oTDCokZI_VHn3mWo$D`&4K820g&N*RK*}%JVcNs^c_r-$D4>`?dFgY>O*vCg&z~97h;#M;x@{a=5J_e|ULu3HB!Fbq;bA$bc-nSSSCE0!UP>1uYfE zQwXju!$hkmIAs4}JB8|{ST%EE=6?hntzcDm<+R1D((@q<&WL+3HmAJL$kG{g#5mB( zrF>q=seWJn*w{npGo=aF%w}AdpaUACD(hoAPg*XMkW(|X+lmTiC01JkG!kkJAXjlT zA=bJLT+X$@Wj-;fEcXMxh1nOf%Je#t)VNSn@DM*!AV8zY$0Y>Vivp}Kp>T99_iaL3 z9Hj8$DT?;4#v(A=W?QL_U&(v>3P0AO-7zXgK10+MfuQa`qWd~*;Pk}wC0Etw&`KCD zS1GD8n;OpM$^{0&s(oZk#ZK5^%@Bqw<)l&^+iO6c3)Y;n2o;D&`!dRLK;^IkjxOu( zG?~bJl{UykUd2(JAvAj$^0BxK^C%Z-Jt8o1^}}X*llt^rOZF}Yf_jUtj{>i~?df1H zck9OqOFE^{?JqH@l&fmx>r_$k_GeeO^1~kxBgu@CTBJ@2nv2s$E}PsV{E`Qyk;>~_ zH!ZQ6#oU3pZTf2p*u2l63Or1!<~`?9^FKOf&ZIHkG~u0d9v#6L_9eF|UoRb-HdLo& zC_xo+Ed(0s?kyI5E;Zda8#}z$jybOh@?0AM{g&A@z2N-p#pitWc(u+>_wf??Un312 z&A*E@|BNdCUuz3=^nbuh7@Hg0*{YKWSn5&A+v(cak&;-wl_scYNi`vP4_x{`aR2cG9mD^C(v#Bt3Y~ug{WBi^4U~bD z#1^1uXKZQlH+_HMGLX{!D!}|9LHF~N$KUj&|94dUhI~``_eX|5wElgMj+FkVo#g&0 zL{Ccp7d^iv7)a^=g(3Q%gZ;gVh#TwMs{b;F9L_K5-kP6-mBaa^*xywBMg7kQ)qmBkTVX{jHHY?XU6tLjF&( zKaKMX_N~L;TKp&3-|MA1?JwT{EAH>}H~%s?Mmjnwdd9zN{EPR$wD=#5|1%fAtn%;0 z>FF4#82&f<@68GIKgRf9(0^0%v;HRlu(h+r`DJ6dKdO23zp3~sqhM@j2H-FN7#Psd zvog}qe_^Ge*Q0-HY2U85R`2cm&sG=kRypMT`Gh>Ru%&|K8~Q(8jh6H$I<<_h4Zy+< zhyLfRd#gE0y;T-*=zo#@XXn4U{b%PSiWbIpI0|pN8e15W{>M(}{%t2RHUI~lUjzDQ zkH1{@&mRAru)l^&BB*QYjLU0fWoc|-Xa1(M?f-N4^#7&%zsC62fEj-Iz#rZJy7ANA z{~Yi?*w&@_i)Fo^O#i$X-mZUG;3wl>^!~c9_wzpOe`cTIzi9R^T>Kdkg^X=%-z@Yi zF1?*8(|!B$5ACV>E$!bd$?(T8{tQiW09#9Y8$Ez6&R_V{3eHx5H}f059Zma(6EOUW zJ&uU_&BlEdC}(>7?&qY^85t?u4U3LPNz&#iBv_CiM&JZH*In zJC8^rz(z+yOHac{`=$U53nLvl4IMEJ4e>9J{C#%dA2Ir;9@16-3*KKVCb zA70DwCky}OBqcrT-w#r1#!Q&z^1$`3J(J&5`L{G5nwUl4BuN50H<}u&oh2%=jre2*9hRiuO+cdUA(T-)AXsU@86PTLO#eJD| zXB4Qq*M#;g&JZy3=8BSlagj`N!I$L2+g9ZjxA z^iv5?fn0%XMy#5FC+B7D_nMVh2qB%rvFo3}G;YIFaer<4mJ4a^3h^Y^M!jN*D z06#>yXPKB4Q@|>1sahJt8DvgE$%`gJiv}&m(@)_n(30^`oa_0fy~zTWseW$LYSgd<5_uGAO11g;@kfAaQE!xZMfCZr61 z#-jgXR7QHHpRVxF)3KUyD-o$QaQ>&xklH6==DKDgSs_uCnqM`pgh$Fq7ueI#F*5EsT{2upvrk0*oeu)!F%K9F*rRiEhucW3YJnYy`2YKKS%Zr2I95o)~ zMcULSjamN9ovPv$g==Dzqt-wYl$J@)i;nA&6)y8n4rYgmCL5bo+}MIKHbl^3ykK#( z(EvHnSOJYNmL?!l8Avf6G$UZXQB+n!o8JB+yeQC5dr(G0aosp11TsN{h5D0&v0?pQ zU!t}hO6`d&H1Fo>V&vf%2)~Hxe)_VC68v%WAOSAy=o+$;Tja^G1T)m0SD?jX% zh&RTyg6;%YbhMDcTMSNFdM|boB8ZdRmxYk`P!x1yRo=Ui0aTZoF^h6KTNx3D1QDzj zv{SX09zQ${t=ed7SPZT~3~8B#Zhi{hj}v+h>>905eT#v`c<$jxPx*Ccs33BzH@wWb z$L#rYspw^9yd9*YRaO%j9UQq#glJ{s=zGoDWzuT2?qN^8yp|#{f2owcwt}UeR!$1b zQ!)2sBG0z_>-E4*hrX88HcpvMn$?`UN0f(dTETzgU%yk(pCRkN`4J=M1^|&G~oLZq8KGHe)Z% zF@0?uvk|Oa`Kh7m{mTtuxe%9@L)KG>82C z0@5Ve5nOm}0Qdo9`m7AIz%*pyZr-+cD~E+e}hxNNEzL>UxOt;#XP z<|0`_em?<7$N)N?l_*mddIfrgfpKS^_>aeM*s!?i_17%riDPps@+*8QF!rt48}?Nh zN23cB;$~NyY~(L5K;!om`v1n!erIFq4C?d@^qO=u>P&Cx*dLcBJ(D^U3!^5(+b^_? znv9HZ&D%;(&#KA9qRz<5s>w|E)-Y%?GpWP(Dp@^4+U(9&rB^X-n|&-aPH{xpFWhn9(%mgT=r9~Ra(fj{H(v8$__ z!c5)k>h6*4PP#KUUF_DdzQIKb0Ro+~WYF+H>@7c!gqH{(JUEU%0gzvg3~0BA`}MZs zlFzlC`qE&O!pB+2cD1^1T6t}Y>Lqo@b*se!=Al7W1=E($L5}%Pac_TV>LBn zFDJKZrWI{Bi-AD8eo;Wjf*fS(trTi6hm^;nB+ehd2+!RBy}~-W)qvxs`NK_=iLyQt|PDN^6X^n^22DSLuU}tgi>0#&< zDJQlokhP`obTHf-*0qn=tfTCE=n_EtebQPo!rC$&-)#c+SzjInz;*|gc6WW)A&zshsKxYetY56tycsoHb>+b6etUS$k z!cm~v=WCJQ655$o(k(Vmc{`Z3yxGr$TCvu=rax%=&(T|PpLD05!7yB_dSMF@Pmu3% zk7p7M_0o1Cr$Ow&&PSB%wRw7b_-Y9oP6(@XC>{0iUfpK2Oz@Mrpha4t2VCvJ-9ld> zqy;Rt$l{xTn5^zNTs}IhdA`#OB*S?^fVJGP=qhQ#+N0t6&S+D%Y`W|#e^`Y~`EIpg zgPnU7rKQp@w-LGFN1hWUcK4|@tw!W3pC_*#4g`C`c z<5pQr#L}&?DWN5=-FL(#*4!sOZF`tM021ihKDWsF0$K!;qZqu>9cm*TLLQXN-PAuE z&pnZ^p5Tlwjkk~}msKsW*g|fysQHZe%^N}NvoI!hC&qYQPB$;JSQR3)eUVdfTBVx% zrCx}gz{~i_$JsI5zb(-r7_qjVVYV*7J9@f>n2c*TYjOgucTa!bB*)jmG5OaMJq(n0K8-s1PPcC?-O^(pV{K z{#i>P)#Ts>-Mw)EBZq5IG3@et$gfo!^1gvW6h`L4-Sb#SCmPT(hal*YxLnK2Eop;Q z$-NO;o*^ASj<$W_J#8yIX+(M^mY0A|br7za^;r=Q(bX*>(=PJNqL>Owqo>V`XkF(p zxI8KsYC5HBe0`bk z?+eyql5_#m5raIYZIi7m2CnY&(g`aA=?Ee`CPBTgAl_ZM4J@E5UYVz7Rr!`yqaC3h zVWO?IFzq#fe25&$@ls8!<*SH37q&dlQ-i6?qrhrwMAnyP%mNsjen+XSm()*` z(En73NRr@P1uWh^Mj9#OrX|W8F=m>4lbI5SU?(ZXnulDv`N(ha(suR z+M20rUqGRFlr2@QUf$?@9`TJ;%Ze*ZljD4+2-3L_M9iaOPvbGGNUp-+2GZE zTfPD=2JD$0Sa=WdQB96=_kDXu7NgN+U#@wGlDgJMuE0(!Z`GQwR#Lv-k_nS( znLFcd&8`6~lqDDGQJ)rvOd!a0?#n-gsU$n(e9SHXBt=?NN#`V{17K}#MJMoi1Qo3} zLuU2CNx(&;^sY8~9MgJ43;Av;q}qXA8L;l3eIMuwdi6>2)4K!kY~Xzho6k)^O)}|e zW2{FJknS~Hqy*_XJzveMV?P36NU9wPx@W+^)}yxOQ(6o)Rb4m?HOa<#8BQZ-%Fadr z&xW)EM3q)ST*BzE9+-)$y6* z6>NWz({aDzC4SPU0gB@`!3lhCES5gU&1cssLp-Y|h`+KAdsemBd(DUf-B|d{B2zZt z9_K3Qv88Dv`wGVG5`c^5kz7ej(HuG|cRy2&0WgHIVfsF{9%Aj!;JZf2SF_^#!tG1Wv6t@MJii(nGhkT^&_ode_woX(Pk0gUecVy#d zDQ8s6F&6Qfyz*t17QGy6@FN#tNEwO?82wb4tF&}4voxid&iYSWtkl5BlL_lJ^VROM zYc6XrnY{+|90waYmFZs*RF-fF38j1p6IZ~*>nI^=AM1bydI1JIDYkhU5x#6}m>-ID z!UfIG??}#guJLHmEv3Wgwu1x~fxQRG2898j@)EMsAZ3c#NtrBI6eu_q?pJ%16_jhO z4vyNb1$az-3F#fZD=L9yEtcB%I@k@H)an@)$DT&^mf8tJng-n?tiklurx-1{#|dUX zT%Yew`-9H7mh&S9+rP+@#5k%tYwBqp4(DBSGHOCUGd^rU{+MB*d%2+bx{U+kTs5jt zW)FN^LvBKCM>E z;kUZpf-_-a79UCD6(-DXP&mwqMnw{2H##AqFsgNDfajHfKk7+vr5LS62k}LFm8jAS zQKHA4fX1wq8j|{?-ZXS*FDY{mM0AyKw!-on@WG$9`J++dX8&-*lr)TyH?;Z+M} z3Bzfkab@KuvhjKZ$QX6ayapsGSvkJp#^HUI@M8E|Y(rC(jK0Ho9VcO(sjZJ14<&kY zR?HjbW8u%H4@9kG?jRMNRPCEj%4fpHK-Qa|zmM2ZkCy@!pT?L<9cYbP6Wg=EqS)Au zo3%@-;MN~FMw_FjOBKvPcEj{a7oadQ&Fi$BF;!xCn7j)0dXl%i3qk^9k+X5`IBx;P z^QmRfZTXR9FsFpa(CTWKE$bl;3wteu%ihI3sLyv5-Ia3SaIwKK6MM+jTW6O;e=SZU;i-E-n?xK`US;5lC1A+I0!Ve`R{GCH=HdY3qYywI)_t~egz(1V|#~yI9Z`F;hZa+qR^sQFHEh*@FQOrW3|=VIO6Oz88wiw;`fA zI#&UF9U6{neK5Tkxo0suyBOf;$z+9kq6>vk_ua?SOD#_a1^sdSk*20VrSBjqKDeVi zN#rE?Dt7;LlP%4kRid|2MqbV4_-LqpENIw15WmK-$I;+2edX|>2U8<}z6xth^jkW1 zi^l|*=*7Lp%@@-Ld(-*_vu%$h=1NoG?K-j*Aq$Xi!(kgFq}p1`EZC)Clq#nFy1I+jw4{E6Vru=WMKCgsW13h$x>iA*=T56!>8Yg! zl-E<0sMM|oU$$&7A=_&Bqw}Kcu~=C%l5Z*80d^M8rPC=$A50~j(6mDP!V)Z3SsRGC zsn=eo+bT4eO4ybn=&!NcaPiYHH0b+Oqu&keoIKvQE#DjKi-*E>a9IKoYS09msl|cT?2w!U&%?hJcD0As>f7w(5 zO`G9Dh^-0qW6gw$oEd3wLO&rEp+Iu`^N~suU>e;}K}I=tG52O4=87=QxZxrIf1Zqk zGG!##sWWHtN*6jSrGL_GM{BLl;JMA8v#SK>YcaekM|w%8sDD{A0HVnP>(Eu zA#*B^Y6sY2@Go6*VKot?{HpqhiTG*ASBj5Xz4Rm&QjWq4SSVssph85k#WSV80fSwG zasWGI0K-&yLg6# z!(7E$Y~p7~e>`@y^s9(-sHj9xKrT{s8N9Lq^LqXf`LO2Bq2D209TiJnVEFL4tNJLA z4xFis{I(3^Lqq17%%Xvb{6v?5pFkzA!>`4UXDpi$(Sbk4< z&#N^`sZ`}t` z@3*hG&!^iUFsEHrk+2i$ifhoc9Q9lG;Y)yjXYkEs`Ff_zYHV5B$zeYd532kuFWX&l z;$bf3kdK*p-o$!YUX-L=N#1PfX{)zM-_r2fNIFzcqS4CQuEXCGPq%jty-vQ6nnu+*8QY&0FPJr7Q;4b5e30abd3#@^MEI`b(GNwsT>OiQl7|S*UPx<^p{sYX9lC$mo+%#4_ z2l-htbt$(B@@oo1w$Q^V;<5aOQ8Q>TD9(p(DP`c1k=`dBSSNkxS6|(D_^;kmHwPXs!;jzsSlt4%W#SlO~QV&~>5jZ=<*`%r|V9EJT}3(s(i6%~rqAw}!N z-as=>D>NAIgAB+#^6?Q}L~@H*E;1jx#Ww?TlRg%mKyLQ?hk4t5U)cN>>x*I2uZR3$ zpK)z_^-toyC>Q_8aL8@eR-T}SVo?tWC8Wj}o^dE{i%KCu;(ezPphE*S3I^QS4#umz zZG3KpreyL;5GkEH?A2J=-SzKA+e15vSm^+bprFDPbSLxJcTa${+j!{nf(xqCp-du$eTY!u~ zh9}rie!s)N_949_;JVgZnj$w9;4T0;QXF4w@M*38Zb7#s7kx5un?>ZEd|y9&y1hCn z73B!xt$ya&q*D5v-<8p zq@(A{y+GrYcRd~}0aBblRC(HjN2P_et_80#OJlXsc2V*=x_ri}{@@68qsK^sFHpGS zS}}RW$QX3u;?yn&qgpKO1~u8l!P#l>fQ&3^hVCE@0iKM z8d|K?isQ`Bv7OmhT@;b?JncVD-R7MVQ?;KyVY#g>oSz4`Q11G07OdL)V=QRW6R_Wj zMfdq2v&+CHQAl9&H6*YSg@`lUBaNWLSxCU<-(TT4hjKhkxIL)9M@@CNMS`_fyz&Xr z3{v-xdSMaLatt8HEK)eBAKw8#%M(nuVF}6gn+igwtZPJ{fa*o4z&ZRe8w}#jj2Aho zMj00ND(e8u1Ez#r+^YV;IPFXF1J| zo5$Sdeudf^ls?ac!TB&Kj8y$iuF!7K=ZHW^Gr|mi-6fr>YY%8iA<6W@z>G92-|@S; z+&2#CwEN<33f?+>=ko2^tcsaMiW#cRwzH0o<;}@1Irp9|yW84^F2hoDF#++K{*r`} zrgw=b>8s7x@!QG{iK6@9q-?(7Sb7sN-T?;~Vg|#_T*<3O13~x(o?0jv-q|J$I4Bi1 zlqJhs2gS?Biy2KNZl-xmn8@TBKBI^~LOZ5UiaL*tm_hZ(eVCvA8`kD004aNa1wVZ+($gTPx!-tjDo%T7Y&x#bPUxljG>*!c{YlAl zZ20#MWVA^Y5~tWzFkj}ot{lamAS?IPfg#Q6*X-rL0aVi&0jA4w*Xn9120dsF+W~?O zEX$b3>KV7zE>=RjvQ?|DjznAC=icu^)1zF`>UDKBt25n>Tl<^CPJb@1a!;`c%dK*i zG))#=(&Gu(G--<<2^P2e%y>4#=jzJ0%^>cryKFJ#!fPmX)K(tb|!S}*&x z(QFzEuYu2~O9+^gKKjIAmaU^OKl-5gx&A@@$@C)mnU&Je^-{WeFMX- zI;a~S`;h0mJBu#_4FP-bm%lQQ9h&m(upslD`QUx!`EN|T-;+(PWOVuV-Rk|<1>?wP_;FyyM|>{U!$mgxbmHlw;WgFy>5FF~5+RC*oO#u$gX65Y zE6fKTWdK$44psYkd%#yoFGu-qm;xg5tpy{&2m@QE!WPz(Ft&1V29-TJ(egq4O*6X< zv+rTkO@JR~*>C;pyvIA#A(5XICys>l=*LIsGAs+SoAP%fR*PtDaAV*p!h3{7$!+LuG$EW<;D;h%gy2J%;e)5F zhYDe!9bjFVtw^4t6RI%HQ1VVXLZ!CcL`A`lp>COHe8TioTB{-KN9c!Ls^%HjL3&Fa<}C&V9t^i$a8_^8Pa zdj%R_qQBUbjTrF!V7x@{j!{{|D{JPE*TUK#==d(oXUVq)cMZA_nI;09<+Nle`HVh; znqjvEi`(ufU;J_BHq-c4q+MG5BbLcgDOY=p=pbM~U@|}SZ^J45&qZHo@04O@7`8yn zBE)id%X5(rl;uC1{tGacbkdC+r5@k~^yTQ*XS0ha?~2`^oM%5e{L{{ZnxW?|W5$#h zK=^8*Tq|bF=J{uxAF^Dlygp$w&DRKK2hdP-XR+k`j zrOirQDzzOX59iYoliBg*inyU0qo>8aywSW*k8t zoXc~ZM=5M0Dr=LbSDe2*B%4F(>bqiz_uNDKAjP>y$+l0EG5xTF%Z=S=?;Ifzn1NT+ zey#EqQp{r=MrjP}MWn3+wzUkN2HyG<3hVAc?6xLVrPa1XoxipGzFDIqp~KR-*eOb! zC=Z_*IaID<-OHWVnU{ljV=#ZLA^6HoxYAU%_}U(PwZ|~TY)y)53$)jzJYHul)XLoZ z3+?}$XLE^ypr(Rj1h?&m?JC`;>vR_xMH=!VT8B(OQcZL@X3cymfAwj=RH;y|*%HZZXK3k7S85CV1#Pt9mTt+^WgpoQ-5P{MquqF4 z^ptMRq-O7zPdd*}ny6(;X+^G95;k(c%$z?^*7_BSna+R8kTPASyMq&0*4n1XpMM!l zF4=Z%Vn=CHkMWU*Ed0`pnOq8=5xaWB)Rp;Npv70NE4zQ9lpv%Pa*KIjo#c)>NI=j= zlQ&#HRB}jRcO+p})eTJWR_2P=&K14ZYC-ae z5=S#JIB?hu3_o+3!%G6SlXHu~QWe($2;g$IB7|nT>oY}Sw3c}_!H#)w3z@cPN$Ft- zP#77M(hKZ!37fURx{;Bn$0oe041ZGg5+;Xe+X}(VK5iWSNhrebpw5T~(Nx32cnL8w z=+y-Ja5M$g$%Ax5jnH(gfC_;*SIII9`q9vW5yee2+6)c_(0+}U*_K(7+#pyDo|1d! z0=y(VwO(!|MeU!#@1z(9VX8a1Cx?#AJJ@hPR1?zc0*#8h#&q+8ObYs#6t}Udn;?FJ znZUF6>&f&Tku-`HnUZKRdsYr@CzZ5r-%6WZ@3i9UpLVfep6W%M_nf?6xv`yZ(mhjqW%L*}__3M%&h;kI`5~~p zUL_o!Muf}Rd_D6Kt{k4nUy_*Lw@6hz2Nj_p$P0Vx6d@4&j6Fb9TQ41-cO!)i&hOtx z#|P#Fu?JgF%3zer5W)#>j5Z)1fe2fsimU{cQ{F6n$P|uVFv{A$oN?X)8|OUxA82hoWw+$@-dB*`+S^ zqb#In!zT1|+RzJ|C?r*iD`!qAT+JJ1?=Ok}v{6F$`A7m`C^k)_^eCyWZW2i$!+%{Pf~veTV$X27=woPGmzD&Q}lkodjiXUKO#!eMU~u*0Y#2<&CP z6Kd{AeZlKOr;_FKN(vT>qjsz|tY;_t9I9r~GWw(lMRF(KJX6*|$ZyB(oZ`0>V1z-l z$}KOJpLby?1R@>PyBW2-%mRyD%|>5X-3hs`a34KRK27W5%`-pQeyBVzwWOqdUeiJ; ziTb@l@6-hl4|0?Z9BoadVB4ORWg=va7G%a8rDU2<{GrToX8rpUUy&On62Z3kqg9Mj zq9gFt>LT1}RpkC-#u3l2kmi#A^Wm>aIW%%$9EuU6?6{UtNq}=&kOJO8z9)4};uq8` zQ?ls)3-t5<h~@E?&2Y(d4##nsGC%HG`JA1bG8W?=<{Exo9PfGd*y z1Bw-#P0gHvP(QVlDUboTa`gh{skk{h+M4}??uh~a=pRN=4Ptg?E;vRFV%Gl=4gfR% z7drf3PyYud{ePMQ)C#zm{A-DWo$Wuh!+$6A%m5BxKS0COffN$3K*Y#V(#*=j@_*G6 za<|a1G6f>gYykHEaZdOjMorJo#Ys%h%)}172&npD02DJCz zDy$YRKyeH+6DtsIQgQVHYA1l6D;n85xR~(*H~|0vGr$;_0-1dP8}O4Ac+SfIpQHb5 z|Hm}{f5sU2FqknD|3^~+!Sa9MH3KsE|06E=zk|Z|kN5r;lz-3|3$q>*lMV|f3s7jm zqQ}Grq`-iO?7%kwh?8;Yu&}ZIhmZjPdMrSy3jn0I06J{!z;)sNfnUG|I9Pyq7>6DZ z`QqSUf@5dr_{W&BaRM1KHunD*Ghis#*npM(T?0(nfWjaaAcOwjF&if_Gc%AuB6a{AEAZB`0Cgz;ivCsQ0CL%^TtG4nc+ASms>j5^ zrNatf0?H4VfL3$qumL!Ls2UJIV*~aMY>M??$_c!N9jGq?uZJ?b%O%NL!2hcxY zXo0vK2QZ5NBE`x9Z12C&{L@JRc|MMRcZ22sM(No8$tM2y6aT+aIu_tMzre@%|L41c zi4$nq|H;yI!TO@BwQ%}6*y*WtQFWE_NO_bcPt8ej>y$MmGBkpMLYgHK(}R9fClO*7 zLN`Z)CKxo^$Y+k*kBOTK4aCkU_hR7XP(^nen#DOJ;pQ)n3wb9X&;4p|trQmAdYk_8 z+y493{r5{>qw;K_MyK_P?|8#yn(OZdTptVx*;<&UD!ZR~^WNr9uW6An;)~p9j%xUK z&+E%{vQQRO<3Wlp8I7*|=S$MiHr>$B3K_=j=7X*9BU57Xzoe`l>y1}G^a4z2DM^}7 zWIVgQamY1~adTkk83NBY%&M03UY;3+8T%huLkIT8eJ~kyZHq@f z-WhFKW42*Shz$<)J)-l5c)||FWmESze{Z&Lw`aK@Pz0p6ueZZW#ur$A7 zIWC5@6lc%ySuVH7!|{0X{9V+N0zYALrO%50gvszn%4@@%EXnA^iPIp%@k-3UUby2A zCKUW4dT~l;iU)78H$iHUn;@Pa7RQMr7~lZ!$u;I$fYpIs2*BMjg3W?^_JsL><2<*1 zy+Q2N`Zy8aLB7)gU*9~zdjLN|9qA#gvA8xH-`yh43dG#4s)tcnF7kWkcSWBJslocd zBp93qd)k$`1ag7!)A$bFSyeNEXlfks5CZ)3qFOeeqkrF>i{RACJ$uoZ9mhJLPuKa0ue5Y~e~20y|SCJ@3u`o)kf zcC#Y=G-VdVj|5i+O;iG*oet?93d60P?$*b&0H%=3L6B5>)V>}f(%sF<+4{6f=9f%7 zTrxTAdl9ETeFxIM(O}|J;IJ&_GQ5qLWFjFE4o%xx%mx7|Y*a&+RX~$xDb=t>i3qBf zOE$&aJv9I+XR)#jX;c%tu1t(JgC`?qk79{lgDg}Lmv6s?C)^T})( zJeYEu)0w1Z1jq1V4{jaqVA{qqecskg)$h&qgJns6-%C|wfjhhce4vpLunt>-TLI!g)mc^K)|>6=hKGYz@f3T7Il? zdt6w;L$0~IUTJayI_#9F4dE|W_}tFowwB~Q&tuwe7J5&D&i(xCI9wlAxzl&Kd--+) zTcVFCeT96&{kcSXM0=HcHO1HPd%@o2X5|CTSZ(Y9AQ4%7t^LrRSu#6XAXOH9lD%zg zf+mP|5E7egCMQ-)y@m;n$B*D}+7MWN>kL^{`p`3^1_H`Os09*K?JOnRPE(W~EvFts zgx3gvbe>3y$~oT#ZdURakE9*+)R)8czW!X=Kl&SZ-|H?7yOyKuo4}Gw6qIp1w3C5g ziMoTJ>LWj{lKQ=GkQj(a^qND%3YK%Qus#-fxq#R347CZ$&1y?OgJ|+KJFX(t(4qbm z2sm5Fv)2r3j85x>266DGH?v%OTjFUO{!#`Y&wbysA9;N zs)dcn|G7!xyD9b?!*_GS%jIv#*ta(Or44@|VwWpN@Y`i~YvThn6g>+_UMER1fE|Fz zQSfe0N3ixd^aRhad#4w}K6u}Ph_lKq%mcJ}d&{lZ`C@}u<>gc?abHpcvjkzx1i{L! z9G!Vd0QjUn4>ND!CxxBgEs@vmcLGV4E9`h*({fNjR9|}EJkjp!9l?1W{Bq22RQtrk zm71Bg^}|+C#(8!9UD_Q*tns#>+$?cF5)8%yA~y>D^3_3@MS6Hsl)2=wuG5%maAm&J zN5m$_8$Qf;iIb@y_d@$jn#1|FESr8)4K#(=;Ttq!1#4zCj(sMJ99e>~ zh~3!;nsrE_O`=$CpN*yCp|aW2&CTowT^Affe<41oJ(rzTDu&g@ z)@u4>&%s>Jrt`2y`b1`a4$HAa>$X>sdPUXOVdHqfl4d^V%bJbB{jOMr0D&j(v}^4mM*~UwRxYT>@&j~6^$M_#QaCwvXB7#-pRiC4_50| zLC>cA*hpebbUdqU>bu6E!On|51hQ6ZArpCt|Zjje4pTheXpj`Jas&Z4Br6BaMBi$ zHCT$UZzAMwZJiTkoP{*r;N(L=vL&f7`bCP$r0^tkXY4wYQJJV((4CaO;!AGzD2bOP ze@Hhc!lhVBvI;gRT0_S{i1aR!_)y@}wJ#&bP4IdqcwG?RhbQ`e-i&~-WLD0cU=zP^ z^)jB~-J{dhbC69yiMl(Zwj@(wr%kkQ?%DBzYXq!S{ko?XLj}3!lgC4#Ch+b9!=cC? zASWy78Xaj!H0c#hoef=6niFY|e%Cz`S#{{Sg9~_G$7+ABILYRYjYq*PR!DilnZxHb zForL|tIw9$Mj7#HN$R!H|d0iLn#e3 zjg;}hE}j@WkI^(`9l*uWg33XQ3sNs&zlNf)9*%kM8q$dcZ zV&i;0VG}H74J58i34y=`wxT8F{mO{);`0mc%{^d8LbPW!9--KPM(yCRJ&aZ)+9=Yf zCWr$DiirTl3dAjMKQXA^Fs!jUBbro@9jk`6`B7!CtObL|v&wcKOjmeo@D|^U)86ob zZp}8%F>e;`@(3(+#POrWm=f7J=lkP`ju|D_(A?-)Via`(Pvo~qR*q(DZ7ybr-<+XI zs4<1HGr|*mIY~%AOa9)l`_czIKd_KhW!S7_lJQMUhJG%Wc5tT2kYc@7B&8Ap*7oqZ zBx81%Z=WcIgo|wZV)|uH#Av{JeVSXr9F?g`Y1wJytQ^Gn2%8SrgppD`#WPJq&#@Mc zu*I~(_N`l``xBvZ(?Ww`6Z~lr$!3y`Nz~qzlG=4sF&3RpR4kizN#<{POoU7!2*eUPpQ`k=Xtrp11cU`IdqDAx*Piidm>K#N6Ks^S$3B z(d7m*r3b%Rhi2>IS?>KqkZ1HyG%4#To!Sh^wd+QB_|16+cm->-LgP?bHWR9GRJJrz zVibTjwq7dP*nv8Up>yI!v1b-HK8J)D^)YpJY}fKzZP;QM6`nY*p=#*RP~r4l$+kQp z{zbsu=z;*>3wiqcX45pk%qdhX52Pzgfwou}{}3evAsQ)x3NI}v`P8^d^*ys$-y-Rs zTL6C8=uJ^~sAL7Pk2Qb-p;4p0hi4N`OnEiHt1_C4nlk}SPRSSw@&JVrIz%pMKLa0b z1*7omSPvI3S^N2C*wpnzi4UpEL8~%igx0j z+h9n~=6arW_&j{+j%Vm#n+3Q@oAhsDm2~L$*X?=h{@C`G%e#A6s#5L=xLK|+S+9Lf zSJ!TJFJBL7w!i;d?3zHQ$dnc_4g&&4SHh!Z;f)=>>gc_~1*_n!PornWUTQRzlD5+# zyvSKh1bD0@wbA=V!XcSpdT;?}f@cCVSXs5dXw^UqHMIV&SxOh{b+h9j(6O`CF+_8T zM!CUR{w^mHBUT^ zg}rHg!T&@C`(Ia>DI_sw|> zj$>M4h(ld7a)mxzKEa}hz0PoN!E^e5aTa1HseY=qlL)p+1 z;lpO->Te@h8x#nzF@|Kw@a%UbJEZZjuCgeW4rn7Il6UYBXy!h;5pgscsY(PzSr{Ou zzV%Fn_~{WxEDt;xfq{85d(c^4OlP3E-^7Qg4fwdjr~bVuJQ z7L#9@x}glh9R|-;mUFh2{U5G=$I0ii`@Q~{=WjpU;xN|-xNsk?7S9+!Ker~r>Xjbc zzCj2X$6kOWfb4Gf*%61DqD%G1fZ`jrHXr~VjysgIlw%GsZRVO6(K?d6g&P#<7e(1r zmy8LM=>gmb64rErD)O~Lk%?iV6uS|`j-hXbQEiy#C0zbZq8!yIsY0SAprc z%n=}s2O)%w@&t+*Gt>HGi}o2YWsKzHjhcE(J+kO|)ylGu?U2+f!o-@g>@FSPdYG|R zeuK5v7HY;UKvGS0?P?kk{!LciBM;pb+(SH^tKZ$`utcVF=OTQ+-=KyehUFa-d(G(o z(bTumY37#hw=ve~@!Tkp-1BxbHZ=aey2>BkHQ_&C@b#ITzLA#ER^v4416MlTH;cTg zP&f&PtLm+nR`qIuXMZ?DTPVm%N12Yj)vUpX91ry1pB6KPiR;={lCX z0B=KS4fnhGY4!Ww9tM_P^LL*0kC~Q9HY~4BBwCdSWPY&M)R>7ATnczxA}jWo!yH_Tzd$tXHJAP8Adpz9A@5(*)FX0SV5YYYI4-|ju z1^P!~KG}c8?f2bg8Gx>RLXB|4MM2K~amHZD>%Zy4*JmyG26`@xCqY*5tavI*qa?M! z=lcNv+yFQA`gH7yVl1uz*3*f|$pJF#TaZ=!M)DH6r}4-j?UI>ZCD=zg;Y|8XOI{eN zcR8P0nPSwmMEP!nWIa(t^f)*VjexKb@sAG~YX005S>~jO*i8m`^t-#_2?22&h1QCF zFVv3qM99<0fIejmyb-m-aE&a_Z$dkBIbpR-1hT5{qZwwqgQ6(h6h1j-a_Y(3DF8dR zP1C&^%9tW4>n0SN(zFMC=7o@>JbYYSWrYFNQ<$JnLgSF{DU|!kR5zpj>k^-4Joo$i zM7QAzVz;%bR4hNdtV>`sDy$yJu9QAmR3ksNDloW0^uFIa>Zcb|?~Tz{x~O6PTBVXW zX1Z^aIgq(%Sv=L5&wOxoNR2%GeoAj7E#0RkDbH9cn@SLVPbZE0N8QS50=IuDL!gCt z2n;z-*^dzPSR52y)f0AMokZ~~T+ojY?9mHmVO?nMG@|;IHWp( zxwrm(bqcA7oLgk~K4!|%K!pO`}7XD$xSL-s>RPRpZx(#Of~@|O!1ZxQ+0 zTI!I29{X|H+A~7yulJ;(NWN|{c&xpgKifvjbG3&Izkdw?w*;U#VQh0;Yu@L%_72c~ zTPO+)D0*Y@NbEnIyFB2wrgBi^HI6w(#px+q)T=4+Ax6so3TG1bGBE$z(%_E z633lRH?#YqyIiCj}_WLD-(Rrq2@5Xj-2_)lh!xbHk8`h(Ca_iy5~r|5JG$g=Q9{!#t; zReTyuQo28S$7~@nBm3?#{MZ(4E)F)UW6!uUv#72{&?>WBhW?-hA@MRp1Pr9B`27UT zw#m5POw{IHFto%&4o1+*gQ*o^SE>q;44eY`C(fYUoZE~|+rR)wxRStmCTMD6;!tBD zDj3qE&&0Ak? zQs?Fo-Jz8+fV(LSyO}>rGb!8GTc$n0>b=RGuGO(a5x0_9FF!8B{9#v0DiW3bHtIj+ zX-`G_1#i%)`P5`>vio!3T}?lq?U6U+7CDc{n1j!1lxB=klOy)BTVdboQ@y+g)`Iqm zi@Daw`q{cY{B)P+ux3KNTO*BHsr)NQwLML+sg&!Em+ZGtUnhcb;_h6VFvHL<$ELQ_ zoFI5b0VoTA^mq1%BwJIju!0?yL3tGRngbY0yAGjiwSAmtyi_j)-cb`nDK4l@8`Bf!b9oJ8$Q-9$r1i~?YZ!6W#oVM$ zFrhrUUW7i9Jvmro`~wHRj5TL7>vMnGKj$~fIa5t#*M{l&xVy;t{%N^@BMt)650v3l z_}B%fjz58NQ29a6vD%0%2;-SQ!sI4!Lu>~nZ%lrp45jTI=&uxgrD5)?T}=Z;daMlZ+(ZD)lm z>F)P5!G^vq2=v?;Q8|EdkUM?=<09DKz#ifXd#$EEBz`0wsnIyF*pmu9lcM$f;+UPAXWyr8P?NTOxAKr6$bS32JithN1y$DV?YZ|H4%fx(k z2W8$_&sWG@peSf_Wbj~4IQyDT9w(I?a!mvg`BOSswuZARB?0?WIV7ZgAq>otYV8N7 zUKrY<-24i^#sqqyqHcV%8m`McMSLtinXz+d6#5*Wkn~G9~}Yvd7tUiiJN! z%Oj|z0;abC(+kb?LZQ1gOMtMI4XNE+!lw^Alq|Eqx6H)^?UAbHU5Ls5vHct%BI*H2+jMr>P134Se|EANCu>@{Fg{{$I#>HeHHbCx}?@o=(md zLV4GQ#;+w*A2sw?pS%7~7G+r9KcwVDOiV;B;~1U_=i#qj<{@k7EAyYm7f|gVPw;1$ zF5hm*5G2{dA|{yxU|L_-uU(c);(Un|wV}P=2(P5;2di#tI*_cuP)iE`sw_f@tb8wf zqe8JD!ln$Q#v6j^XXMMFK0Js#T{P~%uX-xB9y24C_!Vi`lC{GWZdlsUL1{k%K=n17 z(ZN|zOBDK56caT2mm1mJbYWmR#uE486(zGuazz!AI1>UkRoy1sAB=glwN8B2b5CUr zdXhWPwE+9|lu!do>9Bi)Z1fhUdCw`DFOKS2%7RIax^7L*3RO+ zU)fjIVYo1wBnhteaG9E_x?hY4wJNyLl?W5(8pyk_z-x%yTA;(&%dXZ--+s%IHqObj zOlaI~#h;SVadKxyz=L)L?@X7}q}?hiQ;Aj6hETi?=j@U`NcA+yrq0)7cL8e!5t&vHiRlLo1uWwUv#%ee*-wAz98D_-~|F ztM(m`t1O__xdNVf~E6UAu6v99cD+iWyG-C5Tf}c~)O{@8r zOeG>cemMt{9&u9iRBpJvk1{DrIr0H5z}ZrXu&^N;q5i-L2(GmBN1U=s1izx6{beaH z7{a)6B|Fk8zszXunSW^?nKF#(IwW+fcnE*E(ysfOORKTC{mOL1TbDf|KcRNe-w(G| zsZ;A%mO2bcM!J|!NT2dJ)>F!_@ECGQ$|)X4-K3nArzP;p zOf;tda>u)T9b^KsxDNC75~N`A3m5A2*5 zUon?G_`ZNWg$B+HqaAyd@ZAh8R0x;#<;Z4z3P!j!1NDG@z91~AXT_-4?@Ync-!nP| zBw1tn)(T3;ilWH_f%izCvP4C>M=^4xPt8m!m1dWUHs{T!a&psknP2DT#bioJJw@+n zn?2nwpCVCAF6#2D%`sBRRA09(I@@{7X5MO3*-yr;WN#noNZkT8yK*jhrVYXZ70@_7 z2(MC5aQk4fa7YdeWF4DG$Yb`-j@Yjm)GA$}Q=P=ESnDUEQ3aEb`7_7CY*tZDLjE(t z_nP?R@jv)23kMoc<`#@;M!w=$e{4BNOjWA1tAOgNOHr$-7P!N^3m4FqG4S%uBsI~@ z@0f`a)f|d;r78Yuun+2rh~gr*;r=+5X29s;gA~gD@ohwyMUy({dx~J%map194!;aU zrSeSeV`Q8mwR>%IaY~~`U1BiTukf0qQ<&;w9B<}w0>-9od6P1|RA1DQ#$x0H1jnlNH zoz=u@pHy~kObPu?K_w$K{)Bnn;$0GS*4)2?>T{b51X~wY1M)hGGpV)cTjknLd#YZMu0vhf^hgq+<0qZZr~57tyJqTb4C#E;HrIUExeN*EVRXlhcs-#5#^=1?-RqGYkvrsjM6eK9PAc7gFoT`6!F&WFh z+N>+#5wbLi8LJd8l(q%1J^zHQ*+ZXSd_HrQsu)mWK4zzY&#wzxfOG`q7(+{H+nDk(&V%6zy(i+ zU>SM0APLAC;~L^&J3LEsWygNpo7f7i54~m+cD{zu{Efkvb%Z+%?J$aXDp&i=Xj?sTVcgHvDaB1A~yy|6ftB4lMAGM{U|K8c@rlbO9Vf^yy@l(FL z9-!;aP3898ZSLF~>Gb}X%E;GwH&9%!X_k_`)CDs}j3x8wD~#5JT>2E8Px=&(kJ8H4 zl>4VNUksmpwp2iv?4u1_j@HaUNo8;5B;j8iNcl1zoXiMoRUM&|oGEU-DMa}t^QB4? z%uJ>|CVf#FkxMsWeCyisCYWG$LchwD)8~c#hSRQb*$x*duNhekR+dQ{^KxSj%urOT znF?uwdC>&aE)wj<{7l*z4A%5yJ}yhQ4#vENr+KN6kS4;;2Q26edaU>If^tXe{?jr+ z5*}8!AtL=!?t68?XInNXa0ymh8IxVC0bN8D1<(~+)&mJbtR)IX3FNZyS>Mv%kyDDt z0th{x!lhH3cwZA#cVa_Ju-W(?>EP5TEA?T(tJoo3iC=P8FM^|Di{pwU7}iEh%+ z_e#%zRqJzeB*fj&Dv=|1$!WSt!gJiw;6P4X-x5)%_xa}xK2K73GH-=)NR~i}_!tVZ zSIWb4GkFM0DZb6FSes&pL9#`K#$1-p8k4KaR`IZiT&3V>UHu5tp2W@+OC!yByff|l zo=cojW_d^>{IW`GddRiy>F)& zS*)ioyU)uf-_st~=X;R&17AuRi~uZB$$gR{_`WX9lKDKkg1h3B8&1}pp4=Z_h7mJ3 z$>u2j2K23-_~sIHE!P?2$FqWEiA}{y4V{IXB+??-0@BHsr?4ZJXzdEJWbqMYd@Vk0 z+lzD7@I~B5R9Fe-=b@kAz9~X}3*#^qbu-oc23oGwh3=2G0%#8vpGq&%IW#RsiY@ch z_d{dl%%kDYq!uGu=tw`Jtv{f+I0VSncdW<4!|q4U&}68kqy?|6_nscOl3VhVGMNK` zd(d@ZDlHP$l&C<$HdH?_(NUmVk>fXY6>%fIsZM51y_KaHC(TOuedo{mBSCMyO)z5J z1Xayb$5)CMgiSp^dd!WG;c7wTO+CGL!X8LoplSJy~iwYyp;=)vbnhu+9GJ zX}Hcphy{jo)N)!nMK$$;w49yUy;|T_reVh$c`&*0ZA{^dMlG}j}*g$n~lBJe3hy2h?+wRJa1p#)|`_{9@+?c%$s5H$28`{%!}HPyS^o zt=O*+@dRrzLavqX%6WZ8cIbWy=lHzAd_?@&(dOLSZkOXQV~?&Wk^CdsGrv!|Xq2Du zI^P%i+C<1PU~bPcQDFUq{Y2LuWw=eZM3>=Amm%t_)+XT<=fvUhbm8h*vkWbb;j;na4J#h%+LmZ(ySYhwpvR8Wq~pd^QJ&NVTLMad0O zd8%$kmo)L#P1P@?lE*RTetzY4O1PHy@sWYf_>Fe)fMVywu@9s*p-U!h%F2Bgqisixfbf$ z*yaF}ZnW(oMdDQ2xRq&(uVn5wp%EkIh_&zQG~5R`ova(P^4hesY0|DJX$y&24DON@ z3rVtTv=QkGgUD6$!<2pEq@pg7%3=jZy*C}Y>NWV&#n!*d7Iu@ipDeT13X6ePAJVozQR9= z!gO)s5IhQtXY{ViJ>ZdAeH=;_P$N@n<8_JJyHjFQItcK`)^G;)v&Wz$x$#gs-3_=F z7xZqO-F%HCSril#_2pxXaP_t2EglLMA^90|X&N~9w^);gWh_`bS%E+)tZ_SaVE_1OBYPH+^qt%^Q3bK5D*CRIbqBsr&8f0j+1B%l`Y1Hv8pcdQ^Y&gck|OwT)bHFN9x`IO$gD@ zP*5a_ikh_`rbv*1i_wKr_~SZki>^U;vX?HAAvd)`hmE2q|7*w;D{P55&Ijr=EV%aEmc{{>eiD%6=S(ZOK1oU zE*AqiSP@NipShI2M(lln`(!+QKI4pvRMP)MfirZgj7Y5Jh`t^HuZg#AVv#l+cz(Fk+BW0WElQnt`0B zG!udM17~KU+~(*FJ9(C}Ts{d9guO-W3?@PSLrgw}LL2~q$;igQ35P6!1lOu?HJw=a zxP5;MG`~cS9j;TzB#DCRo6Z>ktWe`6PDGV_h~wa38>$B#L&zr7rzhj%Q-MYy_&{EO z1W8p16P;SSv6SCj?swI;ncu>=ONK@r#*UxphCoeOI(5R2fkH z$fDX6aC8a+eai@~ zQPn|u(HvNX#9q%10Be!EYe?O?By;OFIg#X%dJe>}gyE}@)bJpku{%NgmYHGLO!qDY zwCB%ZzJFe6Rr**^ewyHKie}+aP5a(zA@sk@)!^mh5wV)A^|8EK6yCaeK2%QM6jLaS zOooXxOgtnVBMu_)Vh~`_ihLnITA772^Q(D4cJ;HdXCozS%M1bHd)7IeBMe+0%q(69 zZtQjkPUcgOw(og13Pj`$Ya$mC6FLhH=7RTx7QCjYIFiMDNi?-#-=$jG1eLw$Q^_jy zB`M&IJ@Zfj+7o~cW3dbpSGa1y^pv2NCBY>tD@(HRF-Hn`p8KU=6%GPKl+d0|t_J`# zHM`x^3yBN%8p5W(WN|S>2Q`GAr8`PkrNQ08l@dwV)0&8yA?n~AKP6id*~E@(z!GR6 zcz)AAUjF)WW#ocEQU4iVc$wr6;FocdYF~nz`nyZBB$=i~6%x4Ii zqMHK^4)ZZ)>r^Mq^h(ehhgSH|img&RUK0TQJs%W&QYP&`u-wc3Jl2F8JimI{y;OZF zM3ZYV_Z$<6iS(B3N%P@Or3THa2I=H|a_FJ%La4^PI2c+en=g48Fe?(P5H5xBAyI+y z&BtYE+CKR)$MlgY7p_JwVUV}b#loA`ddWu5Zf{7-e2PJ7UwmaWLY z@AH3wGd25BPg0y=c08Z1w<(z~FSGMLBeG8hc_l2XCf!K5Hj069bltcrVvbQ2d_C)J zGut?AYjfwo4NhsdGftg3B@Lj1Xcl;$v{?w$ZOk(q^G5MKP>s}$8-{Awli9B7vw%Z- zVa~4Euzpzev}z-pl|{@w-OQ0JGq}~uh4q(1k6;a;+KSo69!)@OBa75iW~A&zegP(# zH5pf@Xixrxe!)=#rv^Xd;@2-g+R_}z4Jx)baq2_Yh^; zOPkyHLFXN61^LA2<;i(AIpq%9(#ngHR*#=g+PgZTQ7%ww(vT&cH?7JhBFGEBHX_$x zaTZ{a)A}XFp;xpfR;w%?n!0j&KRK5f8Iv5xBBw;;;;lL_DF;a(x6?t>uAZmiYp#DP zj15Gq*XC~W@i6V?wY+#tv&*gU^pw*JC@O2|&Zw>3_Gg(IwKbEXcT_vi?qVh@tD{d$ zZ>M!$q}BiNyDUw&g*ki<$qc8hibcv+<}MtxHX`~qe{uQ9mP~LOHySmR9WS`uT>SmP zkD_i0=bnk$=r|XaOBjAmXe^uye%<3Qw zzd#ztt#|bG46dC+$HS=tvm%N0dx&S}{CVQSwvF}>zJXbE^o^qR zv|sB+W3(eI&&<>N((P3p>;1^CKY5VbuIO8H5QOlNJLEp#dKJ1qge2b;1xtjKmA9p%%J5J%%S~-ZnDqz zG@PwWB#&Haz7khY@SOJ^KC&G$ z1&0m1zz(g-z3*^1e)PT@s8tl1^ipYY@YTFM_D&b5^>`tpt(d`0!1#0|?PQzK>R;Jq zS&h);nALaSZdzIjmFx0qNp?MK{X9j+<@(0OuWMx!L3!Ho8HNgQL4(C5h$NG`5F8*W z;etPrwhp^t%}hd6{Gt$iuUu#*unc@b*ZAi`U-FR+2Ch)cI_eXnAq#zx3iGAa2n*HaGfXsZsL&wquC7umBl%- z#27SUAF6U$TvP7F*wUC0RwuTAgZF!z@E6qu$Eug{J<9iX$z-X7a3Z0(re_{Uy=?8$Z?QTC|HlPnM`{V)sme zJCr6iP}3(aXc>?VwFOd*@HGG8anu92e{gr157)-e*y#6Ao|Eo$#OT{>rv}*^RbLlj z&v=WOn}EFxLAn$BeZb8LSOh;zXU-(5K9ahJNS8|@NbtMeF18!AB|I`_S{k&Wu(iyr zhD@L!-e4eOJpwzcJo524n3t_Y-r?Cl+H(`2RG`qhh5B4RyK6uX6DnVsjTHqA3lb z7>{TwuUv2k9*K3YvL|>@!hl&SV{@w>!(b)zKNq5?AU+kB_M?BAPmc4lJASEWamq;o zfXjZ&Im^=(iH*t zwY2~mR0*1}Vb-8^TE&wFk>f|jcvc4Qi^;2CzB;`As#v^!RL97~KZI8^Dt6M4X19It zXk!aewcwm3UWTB3q%a~Li#~b#`?e7Ckum*el&$Ou zMw){{1}r_|crrloyZyJ7(tg8usIcMbEpg)f$gNGYX*V=xb{{w$^z{vD2^3v?RHZ1_YunOVE_79Hny}(UYb>lm zZi&y3HVm%8)+?uoa`P1<$wZW-@I+5jpU@_9!5ea1Lb2x79<+mprHY6B*cdp8%xm5^ zJOxc0j1=HgS)G>02h;Y*^}X+G?;f;wxRjft?fpl6V$Vg46Ks-#2BG~3wFLNf>dfoU zYMUzjWceX+O0FUlWZG7WH!!|k0wZmy(vHYEv0F^3lL{P{luRzyPu?L0n)p>+Kog4% z^JG(aGq3JkuHCPBRWgzG&u+HED@wM}8N&{o*Ti#DcQ`lVnkD#2dk^U7%2m@(r z%2=xU(69+BX&~czFN!VmBz|gKP{M#CkF9+Bd0C+hT0ueE>6Ypq8~aS0YFf}Y*`qMo zPGOsiWD`KFn8OQ%(?@r6EJQJxpXk^3KMVr8lr`fCnT%6>PRd_EMFe1>TUV@KJ0#3$ z($)_L2H{h353OWxqn`!u=*;1Q)5{kXx1=ZW>OG;7%uHRU*yN%ZNUX059X>kaC8E6!>)NFL7 zejoSha0COJeR8sT;F_4tgH!PAtJqg5hyJ$=n|8prgjaoy6skLd_nfHUd7G15FJQ)c zI7k`HIRqjAWJ(N#Gq2>9sfb%c9jDKC+}+m#Vxo0uhMr*uG8o4<3alPpg(okc>|q-D zZ;E7SCb3c)D8eI|k+maE8Ggy8mWHSgKgK~r#~sipqDl1KswlaZNo0uV;!P3Q7*$Es zFXd4*P;sT~N$Shp)_s+XlNXS$D4W4~s4n#F@N@deor+&cD0;WINS+oix!?`@@t0m+Xt_udXXpk?HW*PfuBh~ zv}v}y4$}9-G%eF3j~<9n2sd4V+ctG|O%@k+t3v09*;IM0b9W&T8KGU4^4gLQR? zD49sr-fFJPloB<$MsKPc?b*%P$b-ZGr0J02?#F;hN!T8QvC(g4GoJYt9q* ztsNglHdd5h@cHy^?p!@^STiMVl8`wC>wNOc2SVR@zY;8lOSHlK&Psh@rJJ%9>ke-|~pY@Kv4bgWQbAmd#_i3vV%h;#S4M;FII#(=tOhAN8#OH!kGJyOae9m+2w`9rB>9Gs z$0J?tDI$txQ`i<#J3IKqwd%F+`*gR9c0Q@5V%VA>xbRrt-LGO=Ih5$>d^+lz89x|I z{?efm`_bNOsMM@=%f`ZKF!8P2osXFuf2unAu{4}V5sXJ;%4`7QFtSvP=@~%V0uAFO zDp2^|g=#(bq*J(PKPOpj-}=I(nvtBGetA_@5vcc^JIo(r%Va59c)VU_qWuFL=99~j z+!Zp$s0x9QV;#J;X7&{Dh)YmcEmdTGMtVNZsfFO}G$Q;m)3~7BgeK4^ch)Q2xT>3c zRE2+fhKJDqzPr5)?O6%EbO@E69M&a(M!j6O@T^H8Nuu&!@ zv-v2}_r7JSs4ph4!tz$J-xQ#|Y&Q^}v&|rtM0Tlj1J_&VO*gxrPAtp8kFv4xnhb89 z8%@I>F&;GCt=|mvlke~r(=qfh2SjW0d)KBGy53=Ja!aj8e(RQM733KenP;v$%wGI} zBX}ip35+Xb1rM@^Zw;`Ads@exy>AN$?#&AS{>YA^-?*9EJXLnX+nI+r%eXJwfp{kQ zS@LKT8-11>l2}Dx`Ud3HMQ^uW*H@$vBFV;Bmn|_j4f8uR&45hi?1`MZ`27gi_~$2k zJtgtAmzl{w#po9 z>&!XrG#WJulHb1<4hceyWlw~g-aEe<$;*{-PsmzrUj~`i;l|$IzPTsH-T{BRACCvW!dAA8u(GlWVB@#PNSfF5BuerrregA%i>ejJ4eZuSnyRyP@@Dt}+OSwDnhVy1-tX^*1 zsvyt$6BcP-a`s%}zIjRULI$62kjrj$mH^a^=FI|Gk9I_8T)qvFr7>AhpjL{)p2H^z zoG*=LI$7_|^fXCZoNbv)ZaWuwV52QC%h!>2Xl!6$ zsp?u?;$4Yet1T}+CG4Tt*g!R`^w5YgXM^kDtclJANvvU0e19H%m_xbvCS}GF@mpjU zSj9*1W5@D)+=Xtz;Ka-9O_X(&O`K}%`PO;fYYr#=Iuv4D_qZE@Jk+rsKPLZ1El^@D zK_%O#wD-0iQg^6Orc}EUdOC17!c$1Pi}w2;=O%w}r!8+-W=Ua8`*YDMg_<4BxsK15Xmw~NWt||FdGI;jw-xAj2kGI#R z7Kon>Akzf3;XfAW;d$25X@#C3IEJ;ZVD6o7w zilpz6u?>Yo3i$4(`KJ3#7)sf==^Du3J9vQDgnx9XR(mt-+r|`vJj}%_ASziXXjAZ_ zS?~Vl0=y8W!?6!qW8ep?riH<9*&}*DfY6k{Ys@VbMjp>=Md$C3nJUeEy^yelc>!0Nu zSVAQT!hqP^I!;LYFNXYrdrOg>7tgg~aeH?AXCkyx=0BxemwdTOdYOC)4y*Dghogi3 z1X~R!w;sLbb_m+WDjzPAolV@FK$-0<{pxLnHvh&4UKpm{i<9=N{$oMrO$OCB-!xQJrj#y)hbuCgULsj8J|~|V`REX2ao=x)c*o?- ze~o#ZMyVUd+T-_}9sjg3DZvCyt|*1?_NhMFx@rwzcp7owi2ZY58??+&R;gDG_G?wf zBEPndoK4{<4jhqt36A&|SnO%740Iw09P#*Z{{Ay5W4d~pbx`i!jCc$gWb6{&l+nG9 z@hnBHq(kA85_l7gp2v1qcD~&1Ehsh9tn+PY2x;R~kkhs@;At^)L-QqEIp#IvMt=S- zB5RDt9?<*QNMz2o0Gwklhes-q$tm-x4G4Eob zf^WiaxrKthQCW)5o5HDw+(X|axUvoN088L`7=0?3&aK~Z-B8@H@7_$q>}GE2;iS`f z3L6QHKB{}fC=o=egQ|{ngYL}fnP_I#cv|yJAr__rFQzRe@b&$gZ4>lJY=)7o4P)cB zp95DiS4$a^{osd<*jB|Xacq$pg$kln@-yzmo30I%b}}m%)BBoR?Hm3 zGqomhLdLyWD&6t91yslz`677!74=e_;)(Wo$LEU_PPAiQe0l5BFBj#ubE~Xz8%Kk- z(<*4@C^cv+zR#)0jYO(Ms2NuTPPo$+xid$XK5_h34R5-`)%u=4yJ!XrbLf>R{^W37 z)Q%J^=ks5Y`5i6s7=dAd3ai=XG*mc|@(uwu3 z*F}mkrkvk)H{-&U7QqUyz4W?xP5ntwWqI9J#Nm1E9@Pc&=hskt$}g`hFe_exMz72( zuX!oKD?lDa7i>neE!81~t!C%o`oYC>HUg)2joaONo)w~2{XEwKDK?T<<{I@RM&wrS zQTO0BZ1O&C_?j$NXsj$c)W++^*si%;Q{?Tz&m``b*}oj@C+!AFr?VZ0y{ug`m?Ul+ zcb=YdkA=+w92VXb3{x>B(Qo9~N7X#BE#xoA8b7@C4Yz@@x-+k&tU(0_i$zmmkZxeA zmuLoj+gCHy$7{m#6UHaNoN{U#%z)Jn!}{h9qe)w1BKJbF65#22J>Rqq)nJUOfj!4P z95mSyWn`NS<;uK!(SFaq3-Z$vdIO}=)p>ODW~z3Wl<$imTY-z)IBF?1^ zgkb<<*bH+~vIX_FVZ+jcNY_Yon;~0w4(oX>sWaEx=u;oI;(*mqY?~lpMJW5Q@rVPnTa7|8)C;% z4C~t)RA+pQTYVJ+Qv;8aD;@}#0EOJsXEr27X}Dje_<;AE8t$!ZmaCUmK?oO3 z-a0O2J>Zd`N)Bed6(mOHaBx}K0e`PH{~hu*^62h;+edQ^P@2xxr{Trf<|2>^NC&)g zM||2Za;I}1H7C`-@{I(9R5f@sq?a?K&61S&zOHBSguMy8wJ7klI`$29 zTX9Qjfbu0hUHxrL86Bw5JhquGb`R9J?VZnD&;#b5{=#JgxC+=#ZLE6Dp_X7VO|MpP z;X-|*qZ1;iDxyI?FB$(9ncYVB^b=+t7jk?C0?^yz(yB80&Aco2o7gsk##dF0)0J5^ zPcCNnKi8Yl2h}a*rD{|!^quZ@G;)0};+g}&e6hk74>D%JQ}3Wda>WKzy@H9`Z-^#r z*@>nmz`QpK?Y-wOjRWr9>r12xe$PF_<{r5NFaq%^G%B@Tu zVmm+Yf8$fc?oowcCDtq^mpSAcou7<8qKqi9;VDea#v42coiPyUu z=J2h~j1s%!8$}%+zmtKSc?B^;bDhaf8Nusbx$2 zD3@p-;19qh)amdOaDv!?04O>f5EQ*?{v>N*dYRBDi9sA zAA)LsNqn(C^z?5Ps6XBROM>nHt$PN9D4_k5>={IG>nGVW7M6d>o{ylk;kHPoqyL9R00wW_&jH8MaY}t9>FTXK&G15EfuVq$9fYPIFalhmxRCb zRmN|w^qy<_K>a=lw)ItVhdbU8V{6;3(LQsp`-#fa&tR5Ml8=KQ6X{7xQ$Az$zC<|U z4ZFP9+I2V7Zju#f((4m4Iu*v(nB|)2IT!1Pxr2!w@vW)~TE`VwjO3YmnZn=bZA2$# zn>NNaAhB&pK5t4sPoZdM0+a1vsHecZ_-N>vVBUb-_IAA%Uix7SJ>-q5T8BVq0jI*G zr;x7_Wfa1zTANy%fRfK`lM;An>nrqebTDq&2bS~S=^UedX|wU68{ONVH;r7KWGncV zl&}ls%~e~rLL2!she+=?w}jP8l#v*&QguSN*Ib1ran%kkE>#%sWP$wI2#$F6N&wiFAb6KI1SF8bt0M9ac8=o*1m9q-yw4oS|@Q+=?P zWiFpJ`4FQC!J@wqWkD16LD%aL5SFD0d`UJFkA!fcE>p+}+vO8LdGFtS2pLNUMx({q6Atf>86GJBgO4tKS`j6i_hZV6t~m?I zRc~jIchaz_no5y(8FA)Ic+M#?OW-)ckCQ^wWa@Jnih!i3Yo=uJz$d9a6UV4j1(l?c zrK(+A30)Ze+n45)QF-YY9En-ZcBX8KzNz8kA=0k?ACErcd#tsim+QeVLS!y{I056Q z80Gm1W}lCwrkL6$Ryi&MY|@8ox0n0|w=0PB&y=2POp}_&vDu|czUQWWEDXz4^IB z5|_{P4FmzNCbHp-;5fc(=6uCp?zOm_{yJk}^_?t-o+jXl6v`-%kw3Y#<}!93TY^#` zod6G0zNliX6wHB^bVI+~%aF<3jxFgaQrIY)W487$$!#6I(yvvpN0rB79T~siZb9Ff zm$)JtXp|=iOIoQp!hOAhvEK14S-H#dcWY$DM<6AOuQ=R@dsi#r4@+i?_15$wOau%r z{YJygK=f$0|FI+{#&qMSJS*W;|1c^iDbI^hRH5Ne#f@S|?RvmcEA87?wp-t8>8qJ} z8oU@{x0foGzk1W$MVF|8xa5;GlY$RV3D$-Daj=jQ0VwrVndCB3XQyt|)vq z6~ZW}5Zh;!dQW1B-EHqmwQ>PR;PClfl?MgY+@7616?;}hUY6bhG23B-UAL^E$_sgF ztB70!B*z93mzoY40yuh6Ekr6-wO${i3>C#V+6IcMAn=ot)`G=bhoxf#*Vvd*R_^Jt zy-4ev;#hd~*S+k%;-4wAbJ7RiQn6_>St8FU)@ywbn}Q80W79^RC-O2nqnF(x=&H=O7yN+7hw zN#ENKvJA2q$Fe@<416na;Oa~-;_W`Ws&xf7vkU#a+Rn*RxQ>OEoJWQ-v>+gF+s&RR zxJHlXP0`ByqzK1Gp(&900~c1i#U-3iW9k|RPD?N=6o(g@89j&p5iP^|2(g?qs$5%_ zDO16H1grH$Cu!t#gJ0Dk<+o_3$rpN$<@PUsDLrupTeKGh{t?m?RrhLy%}W-TK)9h*!$ zkSOVNA>~8nd%4YGXic6OA8_Zly7tU;Dq94jr^dFJ=4qKZv4Vf7SZKCnpJ~=hcAoWP z>NvfRgCA$QuY=dE-b#rp(ajK*ai~$1ec`=xWI#6Qsl&@b=s6Qy$(yp=x8w`VaVhO& z{vrfBLEUU6Y}4LPrYW*O*L>RQ*gNfUMK9)u=wnH^JFG@HTECs{b(6MUCT06<-mb|| z$6vsLd%^XE8>pAx((mh{X4$}6N2pvonqxbY{ayXL?~_eB9WkEc?FfSD*|slN%ZJ;c z_#Ty_gbm;^j4XB@J&n9{rxyC=oA)@r*sm%fg zzw|~%TdOf&0UG~F;aWU%z2Df274tUZr=xQuk8`5Htfr&2ueevpjN?mm5Ml{NtiFLy zLs)k@Qn9tP2RoQ_DmRcTtg3XKKTuhJ(6I#Nkk49x!;Bxr(z=-naBdME^zX*Q)qQT^DC$iE|iZ3cfxZ4cE7fy8H zW-a3@NJZN)j@!V>97y)=)9!88;6gApPuFs_*N~>qr?(nl97ViXCFz7NEe_XeA6^+F| z4#$^rh2ZLr_Pk97WBNJC3y*Po=ds$gPW%tP_kD?=m-W7yhw_7NctHhg3pZs&Gl*|! zcJUGl?;1@)HCPRMkcz358@eao3+I)yo2@44GZAy?U|s>B8!^9vYBjOOizNdD{%ixM z5P@Bz8o?3g!~PZVBRj!VU~!^c2%m2`WbvM5Oa!ckDeLPKEj54|rMEDL*4iyjP69;>QdQNCNfilIUD0^X@-0Owofl$hoT^xm* z5W=$cOjvWxr?;~b?`OwMkdrJ%43r;3s~4914dZ6vN*%CribV7q)(E{8VXAaV`~H*rUcJOp?DC$hsGu}3X`!& z#&&`9ysB`X91P?s{D%{ca};zO+}_dD)TW z$8E>LJk((N9%g>(F%;h@Ka;1-$x?VGPJ$9zg@b2a3CsLOJmdYb$QpZLBq1ks;YcEl z-`x#OLCJ+YFVP9|LtlRz+t^E>qf$rbi&StNfWo^#x+Rx50k^)yvK>lWCWIR2wY^+j z_B+Ng@frV+Ya(X9A;)zWr_ohNP^`6mx%iwp`@oQ{0W4j54Suz!Ub%gbHN3>|$W`~bbWBUf6_@{M7RG((AOK`wVP=nw}_-IyL{=Sgj5 zYnweG1IyA>DShbHi~P@pV6Ct6-c|`~O(5~oiRQBp*jE?Zq2+j^PP#*kt!4$eWE@;| z(Tc@7`(D^BqPLeM-$?FJlw+T-z6n4YHE(Y})V2%>DxD6Ttal>ucW|m=mr6V_N8)DS zYmrR)On~)aI@Jb7HdAAE_xd?{t#}N&N@QZ(i*|*9f|j6^&M$W?A(T}#p)199-=p1> z74diBENEN?uaJ4t!6krJXaWByzM78npwLws`WpWNC3_wX<&RodW$IRjNm())DtUzD z{BIUFTO|+jO4dwK&tNo88gru*WhJGk*padguRD9e0z~;(q+^>GodNSm7I-j zq69T-yAvmoHN1QJD$#H3*v@Ls@@e<#v$tV|W+sLy>}Rg-6d232?~H{lKFFIYTkLxD z#&lUAm^D1RC!N{4Pi0hxdBG)VzO-EC2#h)!wE#LPU#YV(~y}48pD_BPw$JeiJJSYy|8?ZVOPi5SaD9`s* zR#O{qMfZkIE315=Q!%*_=Y*C1O{8wB0R7p58_QuKj?H$Oy2*fxXZEHFLq5`=$U*dR zyZs`qyUyI)^iezZTKY0Kjd&8_D64*qM@Q~kM{}zDlSADM3g_BmMlhV50a5cG3(50*|gw6)X{<7Qiw%jEAGI4#BHv#UNozq&~6w~T3N8)TNfYaTNv@}Q{0n%UYR zW{ zf|Fr|gY(WeiZ8)qGIA+~-g*U(d&FR-dcHDt^%J(fS_|734Bsu0mhR6S9T{OC))%YH zug$B=;P!tOl%hESygA)gqRC^eFVma@U7b>Y z`lMkJ#-Q4~XdINklCOl8XLp2WO$h=XD;j)>e{T0yOxnu*g_xSa2WAh4Rcs~m1mPpo z*}IqE@SBA4Xc_#^ycCA7rKFzikvJC<47MVPm~*wNYR`sYkPzsU6O{ni;iFe^`;199 zW{Y$w^NqKmKa`UheCuL}qsTVIbf^mSGgRe~MPO)@6Hv*pX=Ke2C0crFzu$ddZ4L5x ztE;MEsNE<@G$j{ECs{>0&z%+C^^t>*sn^jN0z3tnRNID|EFZ+!$8k|!u zC3M`lgHOUf+^X9dC>3eqqG2~2e!c+abLXN5?mND=;Rp{DUK!f(}Q=xyPr?P7Nrwp zJ438;_*N=F+h<-B<2TpkK1-O=D%2nRngQ=MMF+>}tqBD*=VrWf=UZoZa?}BJ&H7$( z__;h)_smL-bden6M|gVO?o)aL#-$~}Z+3lHE37D4Q5ReJYT9iGspYAIEB)S~!(O_2 z=+a84QgR?mt2{vnJq%bBm{}ba^kU=DrrdRbtq-2&dzX`|)hbHPK$3|$M}aJjJpXFH z#pF`W(8b|4(5JnPei)Y$Dw2Zf24gE&$tHM2xs722dGOFx;@x13^z%DQH$tv%Bfb`u z_4N+INP<4v5#>=C)QUCkGhxPrjR|k+)ttu$QUwVLl5(;}?o95u@Rxh=`#d8dh3dN0 z?|f4>YD$nxqw4x|Lh~Qlpv!r63bhg3liCNnhw*$)HA~+!DZ2Mv)Erv#ayyl}q*>z; z{Xm=QO}*0TW)o@d;z2|e3>+5nX!etYaYom{Ix6gz#Yy!I(u}soPIu1yj{BRd&HE6eZm|)bPA?#szl`$>;-cSjji-NcN7+9-*OPEmi?{fm8fH-f@f_GYRr`z}Od5ri|NF3z6{;#g{E zKFF}?;c>Q6eethW<>GH01p>^DaXOZQ30{dhhq7^M)sEmw>!A#cpe{5y-YN6ptKr6}Jh3GGtOG`8^{5p*H;SCRqc(rNQw)wSe#!AXtbWCkXQ5 z;R*tT_~VO*FaO&g#DA65;$Z)^x)uinWdUiJ6GDA>II;a!;DP_}S65nGKbm-GQH$$` zms-FdRev}O{Hc8{;16%LxPP+#lhazPEPporpUOZeB7cN}mGwtC$Tft90da$s^^a~? z0e{?s)$xfskeXVAw!_B_xb&kUS0-8!IH2!^-(jxtt$_oL}fQ5O~dxgM)?p z-ya{&ztC&`F~SdCJY;nqFeraS-?v> z>|DR!8#|Db?bo^xEEELS^mAPnE&zn7^s^1JdVjCW^&esiWP4yl{p1S^5D5J3-hkYI z-|h{_4xthKT9=#a*D-=@2*1Y<$N?cb{Zf~k1wu&r)yB#KS#=M`pL2r+$jSO!T(}`j ztzXM=vqC6QztrUhJgnkh+T&vV&1WDG@VkG2-^LO$C)s{$kDCqhRQ{z8ZuW;n{m=b! zb8!CN2PcFS^>aB$1b$l!tdLOsT9*}K{B=AaPhtpr>*w|WtXvQp*3ULp0D$Y)_^|@m zxqcn<2Xxu5eXv7pzpgC+faSL}4FGVk{aTJ4@+-n`?EzVSiva{P_K@CxIR2am>|E@> z%@>Hr+<%M9TYEjoGsPb92Vl;|<}C>%7LXmB`2jjd0%`Bzv!a=c(Zl`(*|Qwp>e;{j Tv173VxwttIDJjL|#1a1o*aDYG literal 0 HcmV?d00001 diff --git a/doc/Unit Testing/unit-testing.tex b/doc/Unit Testing/unit-testing.tex new file mode 100644 index 00000000..063ca1e8 --- /dev/null +++ b/doc/Unit Testing/unit-testing.tex @@ -0,0 +1,131 @@ +%% ============================= +%% GENERAL SETTINGS +%% ============================= +\documentclass[12pt]{article} + +\usepackage [margin=2.5cm]{geometry} +\usepackage {graphics} +\usepackage {xltxtra} +\usepackage {xgreek} +\usepackage {color} +\usepackage {hyperref} +\hypersetup {colorlinks} + + +\setmainfont[Mapping=tex-text]{Tahoma} +\setlength{\parindent}{0cm} %% No paragraph indent + +\definecolor{darkred}{rgb}{0.5,0,0} +\definecolor{darkgreen}{rgb}{0,0.5,0} +\definecolor{darkblue}{rgb}{0,0,0.5} + +\hypersetup{ colorlinks, +linkcolor=darkblue, +filecolor=darkgreen, +urlcolor=darkblue, +citecolor=darkred } + +%% ============================= +%% DOCUMENT PROPERTIES +%% ============================= +\title{{\Huge {\bf Easy!Appointments}} \\[0.3cm] Unit Testing} +\author{Αλέξανδρος Τσελεγγίδης} +\date{Μάιος 2013} + +%% ============================= +%% DOCUMENT CONTENT +%% ============================= +\begin{document} +\maketitle +\thispagestyle{empty} %% Απομάκρυνση page number από την πρώτη σελίδα + +%% ΕΙΣΑΓΩΓΗ ΣΤΟ UNIT TESTING +\section {Εισαγωγή} +Σε κάθε τομέα παραγωγής προϊόντων είναι πολύ σημαντικό να παράγονται προϊόντα τα οποία να τηρούν πάντα τις προδιαγραφές τους και να μπορούν να αντεπεξέλθουν στις απαιτήσεις του καταναλωτικού κοινού. Η φήμη και η εμπιστοσύνη που προσδίδει μια εταιρεία είναι κομβικά χαρακτηριστικά για την βιωσιμότητας της. Κάθε επαγγελματίας είναι απαραίτητο να είναι σε θέση να εγγυηθεί για την ποιότητα του προϊόντος ή της υπηρεσίας που παρέχει ως αντάλλαγμα της αμοιβής του. +\\[0.3cm] +Ο τρόπος διασφάλισης της ποιότητας διαφέρει ανάλογα με την φύση του προϊόντος ή της υπηρεσίας και μπορεί να εκτελεστεί με διάφορους μεθόδους. Για παράδειγμα αν το προϊόν ήταν κάποιο τρόφιμο, η εταιρία θα έπρεπε να είναι σίγουρη ότι είναι σε άριστη κατάσταση πριν φτάσει στο τραπέζι του καταναλωτή, διότι αν δεν το έκανε αυτό θα υπήρχαν επιπλοκές στην υγεία των καταναλωτών. Αντίστοιχα μια εταιρία που παρέχει μια υπηρεσία πρέπει να διασφαλίσει και να ελέγξει την ποιότητα παροχής της υπηρεσίας με διάφορους τρόπους. Ένας από αυτούς θα ήταν να λαμβάνει τις παρατηρήσεις των καταναλωτών αφότου λάβουν την υπηρεσία ή να περνάει από εσωτερικές εξετάσεις και εκπαίδευση τους υπαλλήλους της έτσι ώστε να είναι σίγουρη ότι αυτοί θα μπορούν να παρέχουν σωστά και αξιόπιστα την υπηρεσία στους πελάτες. +\\[0.3cm] +Στην διαδικασία ανάπτυξης λογισμικού υπάρχουν αντίστοιχα διάφοροι τρόποι ελέγχου ότι το λογισμικό που αναπτύσσεται τηρεί τις προδιαγραφές του. Κάποιοι από αυτούς τους τρόπους είναι τα unit testing, fuzz testing, δημοσίευση δοκιμαστική έκδοσης (beta version) κ.α. Το πιο κοντινό εργαλείο ελέγχου στον προγραμματιστή είναι η διαδικασία unit testing, η οποία εφαρμόζεται αποκλειστικά σε αντικειμενοστραφή κώδικα. Παρακάτω θα γίνει μια ανάλυση αυτής της τεχνικής ελέγχου και θα αναφερθούν οι μέθοδοι και η διαδικασία ελέγχου πάνω στο Easy!Appointments. + +\section {Unit Testing} +Για την υλοποίηση unit tests πάνω στον κώδικα είναι απαραίτητο να τηρούνται δύο πράγματα: (1) η αντικειμενοστραφείς δομή και (2) η χρήση κάποιας βιβλιοθήκης ή εργαλείου το οποίο μπορεί να βοηθήσει στην οργάνωση και καλύτερη υλοποίηση των tests. +\\[0.3cm] +Με τον όρο unit testing εννοείται η δοκιμή μίας “λειτουργικής μονάδας” του λογισμικού που αναπτύσσεται. Η κάθε λειτουργική μονάδα απομονώνεται από τις υπόλοιπες και δοκιμάζεται ξεχωριστά σε διάφορες καταστάσης. Για αυτόν τον λόγο είναι απαραίτητο ο κώδικας να έχει αντικειμενοστραφής δομή. Η διαδικασία χωρίζεται στην συγγραφή πολλαπλών unit test, συναρτήσεων δηλαδή που δοκιμάζουν μια διαδικασία για συγκεκριμένες τιμές εισόδου. Σε κάθε περίπτωση στόχος είναι να υπάρχει ελεγχόμενη έξοδος έτσι ώστε να μπορέσει ο προγραμματιστής να είναι σίγουρος ότι το σύστημα θα λειτουργήσει σωστά σε οποιαδήποτε κατάσταση και αν βρίσκεται. Κατά την διαδικασία αυτήν μπορούν να βρεθούν πολύ εύκολα πολλά προβλήματα και ασυνέπειες στον κώδικα ενός συστήματος, τα οποία χρειάζεται να αντιμετωπιστούν. +\\[0.3cm] +Για να μπορέσουν να υλοποιηθούν αυτά τα test είναι απαραίτητο να χρησιμοποιηθεί κάποια βιβλιοθήκη ή εργαλείο, το οποίο θα κατέχει τις βασικές συναρτήσεις ελέγχου αποτελεσμάτων και επιπρόσθετα λειτουργίες για την παραγωγή αναφορών, οι οποίες περιέχουν τα αποτελέσματα των δοκιμών. Υπάρχουν πάρα πολλά εργαλεία που κάνουν αυτήν την δουλειά, το καθένα για μια συγκεκριμένη γλώσσα προγραμματισμού. Τα πιο διαδεδομένα εργαλεία είναι αυτά που ανήκουν στην οικογένεια xUnit (Junit, CppUnit, NUnit κ.α). +\\[0.3cm] +Τα εργαλεία αυτά μπορούν συνήθως κάλλιστα να συνεργαστούν μαζί με άλλα εργαλεία ανάπτυξης έτσι ώστε να είναι πολύ εύκολο για τον προγραμματιστή να συμπεριλάβει την διαδικασία unit testing στην υλοποίηση του κάθε συστήματος. + +\section {Easy!Appointments Testing} +Η συγγραφή των unit tests για το Easy!Appointmnets έγινε με την χρήση της ενσωματωμένης βιβλιοθήκης που παρέχει το CodeIginter. Η βιβλιοθήκη παρέχει τις βασικές λειτουργίες ελέγχου και παραγωγής αναφορών για τα tests του κώδικα. Προτιμήθηκε έναντι του phpunit λόγω της καλύτερης απόδοσης σε σχέση με το CodeIgniter Framework. +\\[0.3cm] +Η διαδικασία της δοκιμής του συστήματος ξεκίνησε από τα Models, τις λειτουργικές μονάδες που διαχειρίζονται την κίνηση προς και από την βάση δεδομένων. Είναι απαραίτητο για το σύστημα να κατέχει ακέραια δεδομένα μιας και όλη η εφαρμογή βασίζεται σε αυτά. Η κάθε μέθοδος του κάθε model δοκιμάστηκε ξεχωριστά από τις υπόλοιπες για 3-5 διαφορετικές περιπτώσεις κάθε φορά. Όσο αναπτύσσεται το σύστημα τόσο αυξάνονται και unit tests. +\\[0.3cm] +Για την σωστή δοκιμή του συστήματος απομονώθηκε το κάθε model και δοκιμάστηκε ανεξάρτητα από τα υπόλοιπα. Κάθε μέθοδος έχει κατά μέσω όρο 3-5 unit test, κάτι που μελλοντικά μπορεί να αυξηθεί όσο επεκτείνεται η εφαρμογή. + +\section {Παραδειγματα} +Στον παρακάτω κώδικα δοκιμάζεται η βασική ροή της περίπτωσης χρήσης “προσθήκη ραντεβού”. Σε αυτό το test case η είσοδος της μεθόδου add() είναι σωστή και έτσι περιμένουμε ότι και το αποτέλεσμα της διαδικασίας θα είναι επιτυχία. Στο τέλος αφαιρούμε την εγγραφή που προστέθηκε για να μην μείνουν κατάλοιπα στην βάση. Σε κάθε unit test χρησιμοποιείται μόνο μια μέθοδος του model. Έτσι το κάθε test δεν επιρρεάζεται από τυχόν προβλήματα σε άλλες μεθόδους του model. + +\begingroup +\fontsize{10pt}{12pt} +\begin{verbatim} +/** + * Test the appointment add method - insert new record. + */ +private function test_add_appointment_insert() { + // Add - insert new appointment record to the database. + $appointment_data = array( + 'start_datetime' => '2013-05-01 12:30:00', + 'end_datetime' => '2013-05-01 13:00:00', + 'notes' => 'Some notes right here...', + 'id_users_provider' => $this->provider_id, + 'id_users_customer' => $this->customer_id, + 'id_services' => $this->service_id + ); + $appointment_data['id'] = $this->CI->Appointments_Model + ->add($appointment_data); + $this->CI->unit->run($appointment_data['id'], 'is_int', + 'Test if add() appointment (insert operation) returned the db row id.'); + + // Check if the record is the one that was inserted. + $db_data = $this->CI->db->get_where('ea_appointments', + array('id' => $appointment_data['id']))->row_array(); + $this->CI->unit->run($appointment_data, $db_data, + 'Test if add() appointment (insert operation) has successfully inserted a record.'); + + // Delete inserted record. + $this->CI->db->delete('ea_appointments', + array('id' => $appointment_data['id'])); +} +\end{verbatim} +\endgroup +Στο παρακάτω unit test δοκιμάζεται η μέθοδος get\_value() η οποία επιστρέφει την τιμή ενός πεδίου από την βάση. Στο συγκεκριμένο test case δίνεται ως παράμετρος ένα id εγγραφής, το οποίο δεν υπάρχει στην βάση. Η αναμενόμενη συμπεριφορά από το model είναι να εμφανιστεί ένα exception το οποίο να ειδοποιεί ότι η εγγραφή με το συγκεκριμένο id δεν βρέθηκε στην βάση. + +\begingroup +\fontsize{10pt}{12pt} +\begin{verbatim} +/** + * Test the get field value method with a record id that + * doesn't exist in the db. + * + * A database exception is expected. + */ +private function test_get_value_record_does_not_exist() { + $random_record_id = 843521368768; + + $has_thrown_exception = FALSE; + + try { + $this->CI->Appointments_Model->get_value('start_datetime', $random_record_id); + } catch (InvalidArgumentException $db_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_value() with record id that does not exist.'); +} +\end{verbatim} +\endgroup + +Κάποια unit test δοκιμάζουν τις μεθόδους για σωστές τιμές και αναμένουν την επιτυχή ολοκλήρωση των διαδικασιών τους. Τα περισσότερα όμως tests σκοπό έχουν να δουν την συμπεριφορά του συστήματος για τιμές οι οποίες δεν είναι φυσιολογικές. Με αυτόν τον τρόπο μπορούν να προβλεφθούν πολλά bug και άλλα προβλήματα στον κώδικα και η εφαρμογή να είναι περισσότερο αξιόπιστη και δυνατή απέναντι σε σφάλματα. + +\end{document} \ No newline at end of file diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php index 8a77abdc..84aa6aff 100644 --- a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php +++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php @@ -161,7 +161,7 @@ class Unit_tests_appointments_model extends CI_Driver { } private function test_exists_record_does_not_exist() { - // Create random appointmnet data that doesn't exist in the database. + // Create random appointment data that doesn't exist in the database. $appointment_data = array( 'start_datetime' => '2013-05-01 08:33:45', 'end_datetime' => '2013-05-02 13:13:13', @@ -175,7 +175,7 @@ class Unit_tests_appointments_model extends CI_Driver { } private function test_exists_with_wrong_data() { - // Create random appointmnet data that doesn't exist in the database. + // Create random appointment data that doesn't exist in the database. $appointment_data = array( 'start_datetime' => '2WRONG013-05-01 0WRONG8:33:45', 'end_datetime' => '2013-0WRONG5-02 13:13:WRONG13', @@ -190,10 +190,10 @@ class Unit_tests_appointments_model extends CI_Driver { /** * Test the find record id method with a record that already - * exists in the databse. + * exists in the database. */ private function test_find_record_id() { - // Create a new appointmnet record. + // Create a new appointment record. $appointment_data = array( 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', @@ -262,7 +262,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'id_services' => 'WRONG' ); - // Try to find the appointmet's record id. A database + // Try to find the appointment's record id. A database // exception should be raised. $has_thrown_exception = FALSE; @@ -280,7 +280,7 @@ class Unit_tests_appointments_model extends CI_Driver { * Test the normal flow of deleting an appointment record. */ private function test_delete() { - // Create a new appointmnet record. + // Create a new appointment record. $appointment_data = array( 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', @@ -395,7 +395,7 @@ class Unit_tests_appointments_model extends CI_Driver { } $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_batch() with wrong where clause.', - 'A database excpetion is expected to be thrown.'); + 'A database exception is expected to be thrown.'); } /** diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_providers_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_providers_model.php new file mode 100644 index 00000000..4b7095de --- /dev/null +++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_providers_model.php @@ -0,0 +1,199 @@ +CI =& get_instance(); + $this->CI->load->library('Unit_test'); + $this->CI->load->model('Providers_Model'); + + $this->provider_role_id = $this->CI->db->get_where('ea_roles', array('slug' => DB_SLUG_PROVIDER))->row()->id; + } + + /** + * Run all the available tests + */ + public function run_all() { + // All the methods whose names start with "test" are going to be + // executed. If you want a method to not be executed remove the + // "test" keyword from the beginning. + $class_methods = get_class_methods('Unit_tests_providers_model'); + foreach ($class_methods as $method_name) { + if (substr($method_name, 0, 5) === 'test_') { + call_user_func(array($this, $method_name)); + } + } + } + + ///////////////////////////////////////////////////////////////////////// + // UNIT TESTS + ///////////////////////////////////////////////////////////////////////// + + // TEST GET ROW METHOD --------------------------------------------- + private function test_get_row() { + // Insert a new customer record. + $provider_data = array( + 'last_name' => 'Doe', + 'first_name' => 'John', + 'email' => 'alextselegidis@gmail.com', + 'phone_number' => '0123456789', + 'address' => 'Abbey Road 18', + 'city' => 'London', + 'zip_code' => '12345', + 'id_roles' => $this->provider_role_id + ); + $this->CI->db->insert('ea_users', $provider_data); + $provider_data['id'] = intval($this->CI->db->insert_id()); + + // Get the new customer record from db. + $no_model_data = $this->CI->db->get_where('ea_users', array('id' => $provider_data['id']))->row_array(); + $model_data = $this->CI->Providers_Model->get_row($provider_data['id']); + + // Check that the row is the correct one. + $this->CI->unit->run($no_model_data, $model_data, 'Test get_row() method'); + + // Delete inserted customer record. + $this->CI->db->delete('ea_users', array('id' => $provider_data['id'])); + } + + private function test_get_row_that_does_not_exist() { + $random_record_id = 486868412; + $row_data = $this->CI->Providers_Model->get_row($random_record_id); + $this->CI->unit->run($row_data, NULL, 'Test get_row() with record id that does ' + . 'not exist in the database.'); + } + + private function test_get_row_with_invalid_argument() { + $invalid_id = 'THIS IS NOT AN INTEGER'; + + $has_thrown_exception = FALSE; + try { + $this->CI->Providers_Model->get_row($invalid_id); + } catch (InvalidArgumentException $ia_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_row() with wrong argument.'); + } + + // TEST GET VALUE METHOD --------------------------------------------- + private function test_get_value() { + // Insert new customer record. + $provider_data = array( + 'last_name' => 'Doe', + 'first_name' => 'John', + 'email' => 'alextselegidis@gmail.com', + 'phone_number' => '0123456789', + 'address' => 'Abbey Road 18', + 'city' => 'London', + 'zip_code' => '12345', + 'id_roles' => $this->provider_role_id + ); + $this->CI->db->insert('ea_users', $provider_data); + $provider_data['id'] = intval($this->CI->db->insert_id()); + + // Get a specific value from the database. + $model_value = $this->CI->Providers_Model->get_value('email', $provider_data['id']); + + // Check if the value was correctly fetched from the database. + $this->CI->unit->run($model_value, $provider_data['email'], 'Test get_value() method.'); + + // Delete inserted appointment record. + $this->CI->db->delete('ea_users', array('id' => $provider_data['id'])); + } + + private function test_get_value_record_does_not_exist() { + $random_record_id = 843521368768; + + $has_thrown_exception = FALSE; + + try { + $this->CI->Providers_Model->get_value('email', $random_record_id); + } catch (InvalidArgumentException $db_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_value() with record id that does not exist.'); + } + + private function test_get_value_field_does_not_exist() { + // Insert new customer record. + $provider_data = array( + 'last_name' => 'Doe', + 'first_name' => 'John', + 'email' => 'alextselegidis@gmail.com', + 'phone_number' => '0123456789', + 'address' => 'Abbey Road 18', + 'city' => 'London', + 'zip_code' => '12345', + 'id_roles' => $this->provider_role_id + ); + $this->CI->db->insert('ea_users', $provider_data); + $provider_data['id'] = intval($this->CI->db->insert_id()); + + // Try to get record value with wrong field name. + $wrong_field_name = 'THIS IS WRONG'; + $has_thrown_exception = FALSE; + + try { + $this->CI->Providers_Model->get_value($wrong_field_name, $provider_data['id']); + } catch (InvalidArgumentException $db_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_value() with record id that does not exist.'); + + // Delete inserted appointment record. + $this->CI->db->delete('ea_users', array('id' => $provider_data['id'])); + } + + // TEST GET BATCH METHOD --------------------------------------------- + private function test_get_batch() { + // Get all the customer rows without using the model. + $db_data = $this->CI->db->get_where('ea_users', array('id_roles' => $this->provider_role_id))->result_array(); + // Get all the customer rows by using the model. + $model_data = $this->CI->Providers_Model->get_batch(); + // Check that the two arrays are the same. + $this->CI->unit->run($db_data, $model_data, 'Test get_batch() method.'); + } + + private function test_get_batch_with_where_clause() { + // Insert new customer record. + $provider_data = array( + 'last_name' => 'Doe', + 'first_name' => 'John', + 'email' => 'alextselegidis@gmail.com', + 'phone_number' => '0123456789', + 'address' => 'Abbey Road 18', + 'city' => 'London', + 'zip_code' => '12345', + 'id_roles' => $this->provider_role_id + ); + $this->CI->db->insert('ea_users', $provider_data); + $provider_data['id'] = intval($this->CI->db->insert_id()); + + // Get data without using the model. + $no_model_data = $this->CI->db->get_where('ea_users', array('id' => $provider_data['id']))->result_array(); + + // Get data by using the model. + $model_data = $this->CI->Providers_Model->get_batch(array('id' => $provider_data['id'])); + + // Check that the data arrays are the same. + $this->CI->unit->run($no_model_data, $model_data, 'Test get_batch() with where clause.'); + + // Delete inserted record from database. + $this->CI->db->delete('ea_users', array('id' => $provider_data['id'])); + } + + private function unabled_test_get_batch_with_invalid_where_clause() { + // CodeIgniter auto raises an exception if the where section is invalid. + } +} + +/* End of file Unit_tests_providers_model.php */ +/* Location: ./application/libraries/Unit_tests/drivers/Unit_tests_providers_model.php */ diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_services_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_services_model.php new file mode 100644 index 00000000..73db43e6 --- /dev/null +++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_services_model.php @@ -0,0 +1,186 @@ +CI =& get_instance(); + $this->CI->load->library('Unit_test'); + $this->CI->load->model('Services_Model'); + } + + /** + * Run all the available tests + */ + public function run_all() { + // All the methods whose names start with "test" are going to be + // executed. If you want a method to not be executed remove the + // "test" keyword from the beginning. + $class_methods = get_class_methods('Unit_tests_services_model'); + foreach ($class_methods as $method_name) { + if (substr($method_name, 0, 5) === 'test_') { + call_user_func(array($this, $method_name)); + } + } + } + + ///////////////////////////////////////////////////////////////////////// + // UNIT TESTS + ///////////////////////////////////////////////////////////////////////// + + // TEST GET BATCH METHOD ----------------------------------------- + private function test_get_batch() { + // Get all the service rows without using the model. + $db_data = $this->CI->db->get('ea_services')->result_array(); + // Get all the service rows by using the model. + $model_data = $this->CI->Services_Model->get_batch(); + // Check that the two arrays are the same. + $this->CI->unit->run($db_data, $model_data, 'Test get_batch() method.'); + } + + private function test_get_batch_with_where_clause() { + // Insert new service record. + $service_data = array( + 'name' => 'General Examination', + 'duration' => 30, + 'price' => 50.00, + 'currency' => 'euro', + 'description' => 'This is some service description.' + ); + + $this->CI->db->insert('ea_services', $service_data); + $service_data['id'] = intval($this->CI->db->insert_id()); + + // Get data without using the model. + $no_model_data = $this->CI->db->get_where('ea_services', array('id' => $service_data['id']))->result_array(); + + // Get data by using the model. + $model_data = $this->CI->Services_Model->get_batch(array('id' => $service_data['id'])); + + // Check that the data arrays are the same. + $this->CI->unit->run($no_model_data, $model_data, 'Test get_batch() with where clause.'); + + // Delete inserted record from database. + $this->CI->db->delete('ea_services', array('id' => $service_data['id'])); + } + + private function unabled_test_get_batch_with_invalid_where_clause() { + // CodeIgniter auto raises an exception if the where section is invalid. + } + + // TEST GET ROW METHOD ----------------------------------------- + private function test_get_row() { + // Insert a new service record. + $service_data = array( + 'name' => 'General Examination', + 'duration' => 30, + 'price' => 50.00, + 'currency' => 'euro', + 'description' => 'This is some service description.' + ); + + $this->CI->db->insert('ea_services', $service_data); + $service_data['id'] = intval($this->CI->db->insert_id()); + + // Get the new service record from db. + $no_model_data = $this->CI->db->get_where('ea_services', array('id' => $service_data['id']))->row_array(); + $model_data = $this->CI->Services_Model->get_row($service_data['id']); + + // Check that the row is the correct one. + $this->CI->unit->run($no_model_data, $model_data, 'Test get_row() method'); + + // Delete inserted service record. + $this->CI->db->delete('ea_services', array('id' => $service_data['id'])); + } + + private function test_get_row_that_does_not_exist() { + $random_record_id = 486868412; + $row_data = $this->CI->Services_Model->get_row($random_record_id); + $this->CI->unit->run($row_data, NULL, 'Test get_row() with record id that does ' + . 'not exist in the database.'); + } + + private function test_get_row_with_invalid_argument() { + $invalid_id = 'THIS IS NOT AN INTEGER'; + + $has_thrown_exception = FALSE; + try { + $this->CI->Services_Model->get_row($invalid_id); + } catch (InvalidArgumentException $ia_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_row() with wrong argument.'); + } + + // TEST GET VALUE METHOD ----------------------------------------- + private function test_get_value() { + // Insert new service record. + $service_data = array( + 'name' => 'General Examination', + 'duration' => 30, + 'price' => 50.00, + 'currency' => 'euro', + 'description' => 'This is some service description.' + ); + $this->CI->db->insert('ea_services', $service_data); + $service_data['id'] = intval($this->CI->db->insert_id()); + + // Get a specific value from the database. + $model_value = $this->CI->Services_Model->get_value('name', $service_data['id']); + + // Check if the value was correctly fetched from the database. + $this->CI->unit->run($model_value, $service_data['name'], 'Test get_value() method.'); + + // Delete inserted appointment record. + $this->CI->db->delete('ea_services', array('id' => $service_data['id'])); + } + + private function test_get_value_record_does_not_exist() { + $random_record_id = 843521368768; + + $has_thrown_exception = FALSE; + + try { + $this->CI->Services_Model->get_value('name', $random_record_id); + } catch (InvalidArgumentException $db_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_value() with record id that does not exist.'); + } + + private function test_get_value_field_does_not_exist() { + // Insert new service record. + $service_data = array( + 'name' => 'General Examination', + 'duration' => 30, + 'price' => 50.00, + 'currency' => 'euro', + 'description' => 'This is some service description.' + ); + $this->CI->db->insert('ea_services', $service_data); + $service_data['id'] = intval($this->CI->db->insert_id()); + + // Try to get record value with wrong field name. + $wrong_field_name = 'THIS IS WRONG'; + $has_thrown_exception = FALSE; + + try { + $this->CI->Services_Model->get_value($wrong_field_name, $service_data['id']); + } catch (InvalidArgumentException $db_exc) { + $has_thrown_exception = TRUE; + } + + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_value() with record id that does not exist.'); + + // Delete inserted service record. + $this->CI->db->delete('ea_services', array('id' => $service_data['id'])); + } +} + +/* End of file Unit_tests_services_model.php */ +/* Location: ./application/libraries/Unit_tests/drivers/Unit_tests_services_model.php */ diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_settings_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_settings_model.php new file mode 100644 index 00000000..acde4d98 --- /dev/null +++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_settings_model.php @@ -0,0 +1,167 @@ +CI =& get_instance(); + $this->CI->load->library('Unit_test'); + $this->CI->load->model('Settings_Model'); + } + + /** + * Run all the available tests + */ + public function run_all() { + // All the methods whose names start with "test" are going to be + // executed. If you want a method to not be executed remove the + // "test" keyword from the beginning. + $class_methods = get_class_methods('Unit_tests_settings_model'); + foreach ($class_methods as $method_name) { + if (substr($method_name, 0, 5) === 'test_') { + call_user_func(array($this, $method_name)); + } + } + } + + ///////////////////////////////////////////////////////////////////////// + // UNIT TESTS + ///////////////////////////////////////////////////////////////////////// + + // TEST GET SETTING METHOD + private function test_get_setting() { + // Insert new setting to database. + $setting_data = array( + 'name' => 'test_name', + 'value' => 'test_value' + ); + $this->CI->db->insert('ea_settings', $setting_data); + $setting_data['id'] = intval($this->CI->db->insert_id()); + + // Try to get the setting value by using the model. + $model_value = $this->CI->Settings_Model->get_setting('test_name'); + $this->CI->unit->run($model_value, $setting_data['value'], 'Test get_setting() method.'); + + // Delete inserted setting. + $this->CI->db->delete('ea_settings', array('id' => $setting_data['id'])); + } + + private function test_get_setting_invalid_argument() { + $invalid_argument = 564658765; + $has_thrown_exception = FALSE; + try { + $this->CI->Settings_Model->get_setting($invalid_argument); + } catch (InvalidArgumentException $ia_exc) { + $has_thrown_exception = TRUE; + } + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_setting() with invalid method argument.'); + } + + private function test_get_setting_that_does_not_exist() { + $setting_name = 'THIS NAME DOES NOT EXIST IN DB'; + $has_thrown_exception = FALSE; + try { + $this->CI->Settings_Model->get_setting($setting_name); + } catch (InvalidArgumentException $ia_exc) { + $has_thrown_exception = TRUE; + } + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test get_setting() with a name that does not exist in database.'); + } + + // TEST SET SETTING METHOD + private function test_set_setting_insert() { + $setting_data = array( + 'name' => 'test_setting', + 'value' => 'test_value' + ); + + // Insert setting by using the model. + $setting_data['id'] = $this->CI->Settings_Model->set_setting($setting_data['name'], $setting_data['value']); + $this->CI->unit->run($setting_data['id'], 'is_int', 'Test that set_setting() method (insert operation) has returned the setting database id.'); + + // Check that the setting has been successfully inserted. + $db_data = $this->CI->db->get_where('ea_settings', array('id' => $setting_data['id']))->row_array(); + $this->CI->unit->run($setting_data, $db_data, 'Test set_setting() method has successfully inserted the setting into the database.'); + + // Delete inserted setting. + $this->CI->db->delete('ea_settings', array('id' => $setting_data['id'])); + } + + private function test_set_setting_update() { + // Insert new setting into database. + $setting_data = array( + 'name' => 'test_name', + 'value' => 'test_value' + ); + $this->CI->db->insert('ea_settings', $setting_data); + $setting_data['id'] = intval($this->CI->db->insert_id()); + + // Update the inserted setting. + $new_setting_value = 'new_test_value'; + $set_setting_result = $this->CI->Settings_Model->set_setting($setting_data['name'], $new_setting_value); + $this->CI->unit->run($set_setting_result, 'is_int', 'Test that set_setting() method (update operation) has returned the setting database id.'); + + // Check if the update operation was completed successfully. + $db_setting_value = $this->CI->db->get_where('ea_settings', array('id' => $setting_data['id']))->row()->value; + $this->CI->unit->run($db_setting_value, $new_setting_value, 'Test set_setting() method has successfully updated a setting value.'); + + // Delete inserted record. + $this->CI->db->delete('ea_settings', array('id' => $setting_data['id'])); + } + + private function test_set_setting_invalid_setting_name() { + $invalid_setting_name = 1219087912; + $has_thrown_exception = FALSE; + try { + $this->CI->Settings_Model->set_setting($invalid_setting_name, 'test_value'); + } catch (InvalidArgumentException $id_exc) { + $has_thrown_exception = TRUE; + } + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test set_setting() method with invalid argument.'); + } + + // TEST REMOVE SETTING METHOD + private function test_remove_setting() { + // Insert new setting + $setting_data = array( + 'name' => 'test_name', + 'value' => 'test_value' + ); + $this->CI->db->insert('ea_settings', $setting_data); + $setting_data['id'] = intval($this->CI->db->insert_id()); + + // Remove setting + $remove_setting_result = $this->CI->Settings_Model->remove_setting($setting_data['name']); + $this->CI->unit->run($remove_setting_result, TRUE, 'Test remove_setting() return value.'); + + // Check if setting is removed + $num_rows = $this->CI->db->get_where('ea_settings', array('id' => $setting_data['id']))->num_rows(); + $this->CI->unit->run($num_rows, 0, 'Test if remove_setting() method has successfully removed the setting from the database.'); + if ($num_rows > 0) { + $this->CI->db->delete('ea_settings', array('id' => $setting_data['id'])); + } + } + + private function test_remove_setting_record_does_not_exist() { + $random_setting_name = 'THIS IS TOTALLY RANDOM'; + $remove_setting_result = $this->CI->Settings_Model->remove_setting($random_setting_name); + $this->CI->unit->run($remove_setting_result, FALSE, 'Test remove_setting() with record that does not exist.'); + } + + private function test_remove_setting_invalid_setting_name() { + $invalid_setting_name = 12092130968; + $has_thrown_exception = FALSE; + try { + $this->CI->Settings_Model->remove_setting($invalid_setting_name); + } catch (InvalidArgumentException $ia_exc) { + $has_thrown_exception = TRUE; + } + $this->CI->unit->run($has_thrown_exception, TRUE, 'Test remove_setting() with invalid setting name.'); + } +} + +/* End of file Unit_tests_settings_model.php */ +/* Location: ./application/libraries/Unit_tests/drivers/Unit_tests_settings_model.php */ \ No newline at end of file