From 390c33af629b31225957878f77b1a0c0026ef265 Mon Sep 17 00:00:00 2001 From: BasioMeusPuga Date: Tue, 14 Nov 2017 16:07:30 +0530 Subject: [PATCH] Improved epub cover image search, simplified function naming --- __main__.py | 21 ++++++-------- database.py | 12 ++++++-- library.py | 2 -- parsers/epub.py | 62 ++++++++++++++++++++--------------------- pie_chart.py | 12 ++++---- resources/NotFound.png | Bin 0 -> 17683 bytes widgets.py | 2 +- 7 files changed, 55 insertions(+), 56 deletions(-) create mode 100644 resources/NotFound.png diff --git a/__main__.py b/__main__.py index 0a39bf4..71459b5 100755 --- a/__main__.py +++ b/__main__.py @@ -144,7 +144,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.listView.verticalScrollBar().setSingleStep(7) self.listView.doubleClicked.connect(self.list_doubleclick) self.listView.setItemDelegate(LibraryDelegate(self.temp_dir.path())) - self.reload_listview() + self.lib_ref.generate_model('build') + self.lib_ref.create_proxymodel() # Keyboard shortcuts self.exit_all = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+Q'), self) @@ -208,13 +209,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): for i in selected_books: data = i.data(QtCore.Qt.UserRole + 3) selected_hashes.append(data['hash']) + database.DatabaseFunctions( self.database_path).delete_from_database(selected_hashes) - self.viewModel = None # TODO - # Delete the item from the model instead - # of reconstructing it - # The same goes for addition - self.reload_listview() + + self.lib_ref.generate_model('build') + self.lib_ref.create_proxymodel() selected_number = len(selected_books) msg_box = QtWidgets.QMessageBox() @@ -227,10 +227,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): msg_box.show() msg_box.exec_() - def reload_listview(self): - if not self.viewModel: - self.lib_ref.generate_model('build') - def tab_switch(self): if self.tabWidget.currentIndex() == 0: @@ -282,13 +278,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # We're also updating the underlying model to have real-time # updates on the read status - # Find index of the model item that corresponds to the tab - + # Set a baseline model index in case the item gets deleted # E.g It's open in a tab and deleted from the library model_index = None - start_index = self.viewModel.index(0, 0) + # Find index of the model item that corresponds to the tab matching_item = self.viewModel.match( start_index, QtCore.Qt.UserRole + 6, diff --git a/database.py b/database.py index 854eaae..af4b6d4 100644 --- a/database.py +++ b/database.py @@ -127,9 +127,15 @@ class DatabaseFunctions: # file_hashes is expected as a list that will be iterated upon # This should enable multiple deletion - for i in file_hashes: - self.database.execute( - f"DELETE FROM books WHERE Hash = '{i}'") + first = file_hashes[0] + sql_command = f"DELETE FROM books WHERE Hash = '{first}'" + + if len(file_hashes) > 1: + for i in file_hashes[1:]: + sql_command += f" OR Hash = '{i}'" + + self.database.execute(sql_command) + self.database.commit() self.close_database() diff --git a/library.py b/library.py index 2e86312..0ee7738 100644 --- a/library.py +++ b/library.py @@ -121,8 +121,6 @@ class Library: item.setIcon(QtGui.QIcon(img_pixmap)) self.parent_window.viewModel.appendRow(item) - self.create_proxymodel() - def create_proxymodel(self): self.proxy_model = QtCore.QSortFilterProxyModel() self.proxy_model.setSourceModel(self.parent_window.viewModel) diff --git a/parsers/epub.py b/parsers/epub.py index 1235a2f..3c66225 100644 --- a/parsers/epub.py +++ b/parsers/epub.py @@ -58,38 +58,38 @@ class ParseEPUB: cover_item = self.book.get_item_with_id(cover) if cover_item: return cover_item.get_content() - - # In case no cover_item is returned, - # we look for a cover in the guide - for j in self.book.guide: - try: - if (j['title'].lower in ['cover', 'cover-image', 'coverimage'] or - j['type'] == 'coverimagestandard'): - image_path = j['href'] - break - except KeyError: - pass - - # And if all else fails, we find - # the first image referenced in the book - # Fuck everything - if not image_path: - for j in self.book.items: - if j.media_type == 'application/xhtml+xml': - _regex = re.search(r"src=\"(.*)\"\/", j.content.decode('utf-8')) - if _regex: - image_path = _regex[1] - break - - for k in self.book.get_items_of_type(ebooklib.ITEM_IMAGE): - if os.path.basename(k.file_name) == os.path.basename(image_path): - image_content = k.get_content() - break - - return image_content - except KeyError: - return None + pass + + # In case no cover_item is returned, we look for a cover in the guide + for i in self.book.guide: + try: + if (i['title'].lower in ['cover', 'cover-image', 'coverimage'] or + i['type'] == 'coverimagestandard'): + image_path = i['href'] + break + except KeyError: + pass + + # If that fails, we find the first image referenced in the book + if not image_path: + for i in self.book.items: + if i.media_type == 'application/xhtml+xml': + _regex = re.search(r"src=\"(.*)\"\/", i.content.decode('utf-8')) + if _regex: + image_path = _regex[1] + break + + if image_path: + for i in self.book.get_items_of_type(ebooklib.ITEM_IMAGE): + if os.path.basename(i.file_name) == os.path.basename(image_path): + return i.get_content() + + # And if that too fails, we get the first image referenced in the file + for i in self.book.items: + if i.media_type == 'image/jpeg' or i.media_type == 'image/png': + return i.get_content() + def get_isbn(self): try: diff --git a/pie_chart.py b/pie_chart.py index fee52a4..1a3763a 100644 --- a/pie_chart.py +++ b/pie_chart.py @@ -59,7 +59,7 @@ class GeneratePie(): lPath = "%s %s %s" % (lLineOne, lArc, lLineTwo) lGradient = GRADIENTS[lIndex] - lSvgPath += "" % ( + lSvgPath += "" % ( lPath, lGradient) lIndex += 1 @@ -68,19 +68,19 @@ class GeneratePie(): xmlns:xlink="http://www.w3.org/1999/xlink"> - - + + - - + + %s - + """ % (lSvgPath, lOffsetX, lOffsetY) diff --git a/resources/NotFound.png b/resources/NotFound.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4f76a8bb6973769f9bb7d267389c2e072bee8e GIT binary patch literal 17683 zcmeIac|6to_dmJ~=?Il5k)pwLIyj<4Z8S(!h)yL@AsR#yb~|G;nJOjGrc^kUp%O)* zObwFkP?@5lO@_3iO!v8VeeUnx$Gv~t$9;VN`aM2>oJZT<@AvDqUhB19YdzO) zwltF(r!bDeU`Uy-TyDc)2O;hV*;G^gMn@x3eUycvv1`{_TSMpq>kgQ3hY zU%q4=E1|O?puVt;{ptSa8Sf|NE|zZI^K_2|{GIsb`#Zny zZ#1^(mewvfH9_gwla-doHr+lUd+~hr^3ZW-UsOi5vPMS+?i4Sb?p9el$baal9ozqL z;kVk0r{mo!`#T)gtILiPVlWK;;jkeQSFK2bVUjNH$YAVS(<97?-hD`p5xiLjqJOy1 zJcglME2_<4+*4%dwyU9W^)J2wt=^uE{^pw*%w3XO&Dz8|@Ww32PoRZ+pds`B|*qmW_9I zRIMlo{8@JHFS}b+nGW~%ZRZu7b-AFobJ9)woXGVzH<@1& zFHZ0FyUIy0B)?c%EQU4koBSRoZQ>atF~EhRdBUG?V|`FZ4z zjtq7bH1%XO1&t1G%-y?PqHh^=*gb zHfG$vZ)h~oe3;eW;8$A!2`6_y9Q1h3VIB-9Pv4aNCx2boN;^Bd&dkT-t1c}&-p)<4 z?Y!TSp7l&CuQ)xgOChi8{_Z=TO-4_jJvgqAYJ5n}qC9N!T&GXZ<9ffp^URp=Mrup< zgMP;w4tWdst1VVnUt5#BDpspQg!Oo8S6!B~o=4{wRg*7&IF_IK_uqfd>$+Ly4}X8B z<6Iif8vK$2B@T8MB(i6@pDT@+W|rg8;8e-%4}b9e?cDEgcW2ajS2CSr9ekx96{^bQ zTf1Ncr{;AA)ioEE_jfhaj_oOM z%C$|gOgCeUuIO1RHhJ99^ZK5StK%2!8Q%RkcXD>7m*%EBC!mTIiADjJ{kj{g^8C8% z_T)4y|2t-3k%Ty2Ahorg-QZ|9%I3e`;EWlwc2r&F_9@G4y6Y|vQ{1Ff2oXZH9PTfN z8hJZSM5OayGR7TU3Kb8^Y#YqTb-2HOC+~puKD>L|D0uMumuX{Xu(rY=Rr!AxXW}@0$>r2)r>~6c3VC$Tp#%Oei^xw>Bv|vi5isXH{YyYjI)-yoVkH^*E&q+Ea zs_lO0>C>kyET*+YZp+Z}`ulDYSCgH~BE5Qw#+FH&DM}xo?z{2Mo_7oQbGFKeI}XIq zzAf0ZDZl5DgpOBBG3)(NIVVh$weRCoZgp_D`rNrE8y_4LylSTCwOny;p%6zxQeKs% zvGeHCnT_m@n^whNu9%;3!ZA)u5i-5v(^2D9KJ)mE30rHGlbOa- zU%yUs6H;+0o9-80ba-r8s2XQ#z~J`?9E__RQ5CbE)6@R>=bzq6C{-q8(e9cX+}n(r zcfRlTv<|dJMbR6 ze0u%*^;3?|VDCuqsj>4K>Mb)nIYraP>d<8smbvT5&<`Eg=jR2F&tMw5zDS5;Pcl)> zc7HR^(&)#Bffs%cWnh;U%&}pS%~y+VY`!-SXDi2OubLG{G4O|wJ7>7R$yf{9Idz`P zO1(XA_4tbF;||5LZ5~wk zn*s+18v7s0$!*@d!uu!>2ePH*KXeTL?!FX-FMeG zB2WHs+_k(%ST+iduH-=5MY-55fd@L_71P>G4tw98&e-Si@#$Hw$5Q&sW3?Pw;+uwC zS0@@V`Ezzy1FBdo-@bjjF56|3M8)zpcH_GUzdU|e4 zSUZ+o53^fqXNL{rY9RJ4UtWB$`+;0rg103dZ&>VE_IR#5e(`^_JLAEW<4S?71plv3 zdSUQaIU?BrPQelP;lD2*?CG!C{f&!C!{yIltP}X* z;meMUf*HpZ_pKz`)H(RQlk2_p_8*Rnt!Jc^j27Sb>z<+Cu-|swh7Bnkk$7m25l;uw ztIAZaWZUuSQ@Zo}EO`b!N3j~~*>K#UGnnjRILRqE-F(G?)lgh&*40(9r3P&YGG&V1 ztDnc|s__-;)UgikTjwa|l!ITgH`E98H&`BgqDWtc9+i+Q{hX3GW7#puP|^=yaU(Rz z@Zo6({aDqR*Zq(j4cAtA7R@=K5gXb5F2}QefAyO5h0zIE6R!k@({lNwR#>&=BF%GJ z4j0(tRaRpN{;q+Z_wW^otREj^ZLKaX2N+Fie9&7uEeqCEGTpEK6nmV?N<1m$1WV|R z@bsUpb??LJ)PMiAU>=_|3mO*lfq4`+7aex~@%3X`iZu!GvUx?MqWswD`>mf}T%^yJ zwFrg~r&+xva;?E4<9l2e@GjC9ar1YtS7bGZ=BJ`{MnxD-2U=X zT1vDn)U`7G)~$IjnEmy+h;p`8Gmm42X!#ABy-bIO+KjE3hZCgp879|;+Rco^5{Evh ztyPz`v6`tyPZ@0365zAh=k-6AV-06Q*wsH@X9P}0jErl)T0;-m6%2pzY|U#L_7H67 zQfZ)XlWPr7ofkB+==0T5dg=f@#k>`+K);>(7SVTC;O4FBXv0LjC_C3`Q82>o=4eNWX7wt;ILC)G*bcn0l+;j)->u&wNXFFhd^cgQq ziI}Dz85S0{qdvFSEwJEy)O?Tng$S%zmzj@`4}f1lOGm-OjDP^XCOEj$TDNGCS9MPgHyUELPftj~_o~RV;W{eD|Hlpl8O- zyKn!b>G?W&SsB2AC%=AmA1V3=f%WX%4W+NHu5x~IT4mbs{k`uWZOq(SRPf+aWulUV zoYpsU+a7~s6O?9b8GKu7Q>mOR82ydnJI{iE{hf6>zHfJ?>}Yxz;GWaqn<$?ROlrZ@ zKlAVP>&`%bz3-1JmDb<$c7~VHaeI+qmH+9*#c8+QDkCxJHI9T-y6MR&6c_8@3}66< z1%v;HC1Gjcx}7N(Ys|hJ%Yaqu0G^&^yFNiamIWct8+ao|=(Ngg8RZXSmuxDJu&bb@ z^M7w!DnK5x^H#u<9?IDtbM~xhS>*I&AXR#xz2>I<{x5Im-{1dv()2~X;Zrm>UE!3w zVptu|#(V(|fX&i|X9QK8*O@(}7UHD8<7LuR9jE248TzNlk&Akc05JvzJolKI1Mm}i zZB6_&j+qE~#rz+i;(LMYN{mN;RGy!=-C{Lov4LwP=OSCd{Tx=gKr65F7FV}?V$rII zKN?$JXE~R(S0$JItetVNQ`#ZV*T?3W+XcdK7Y%&^7*=@MF=*H)rO0Ld8Pzo{05st^ zRl0oHc}r<^`h&n+eW96Os*=q*vuhoz?)!9btII=5PRf-X5tnJX*Ty;7+gesO(A(<# z{?XAiw<7jnSDt6MlCjaBfBp%LiwHD+JNd>9K}e``OAM$vzNeDIbSAWM>((u9pSm;v z2qDs`#)1Be`=0Aka3(~`vx~_c?H(N*ZAmd6-pP}92^Ofv@ZuuBNTYzC%KYnMNLqd^_7W3E#T|Zw0ia)&kp%ESqugdAAz(`YNq^bJ7b&esXnJt`u0!cj#4y zKo*dwDa<>0Lx!VKQpjnX$;qu{QBl&fR}>=(O}p({VZjVlRDfza3r0uM+@epA_}!m6 zUS3*8z;t+b6!Uw}*8Kx9JRXlUD2a&Fg?yqvFin?mHMVCkAO~I?VLdRb!LMmpCZvNc z?9PEd8YnX!ktVF%iuf3zvg+Iw&hk^!4EOIy2>AX6a|smQ-8OB3`$hs%X>KMSH#TO? z>VhP>{2KdKL?BD%@mbk`4y8C0m)FEENK1KHn9#9|xmQk(x4!RFMQg{FQFi$BMGG7e z!*YkdK4WGbsaW9IxVZA8=GsGY!IxhFqUL_Ou)77zJxNAQDFpY7`YioMmUI5sr*qTX zUKYZEn(hR`JYj2TprX3^{KQd+-csirovoB^C$VsefE1nj$x%+afQjeg0u&(*E@ zeJ}bhiTFRV3i@fAlCScp;0NP_Biuu4RM3#~>bQ9-VvqurZmv7 z+v@Xw&!h;>wcVi{G}Nl(jGzHICO{7VW1lMG%MwSvrQ~*`=Uv7zOKY>PY(&n^LW+ge z&VW`1gi{g&1(t;^ie-=EKaa2dY7Plh+pANoX!P%I+!DFAsb&Pmhx!snvFU~YfLmpJ z+F#D(1pfRybunu%PlKC0`Xh1FoEakcQ5W3BtS$hkTx4s2Kb(4`&_)L|-5{6yHGuS7 zAfeX5rqMvWc{onjtrULHrZQ3J}JU=}?L_g;%?wudxLrrXGSB4&w{sTM2s+lz#*W1u3~6{>t&nn?2w*dH1I3YmfdCE^XYK&m|%7$5gUo4Lv6@g zS22%nVnNSDFk#A7FcRLAQ)V;d=^$gHrqrZ6OhanChIx1sDIsd4&v;ZNYYz2Jct6v`v?8>fDGmjeu_K#8`ERninFLNA(PzCiW5sX|5e_4Hj5 zc@ubwM7CJ`X9|6ApZ{PFnTFnpF{Io<*+fcc z+$7xXQAtv5proO_8`~FBB17j=y;^ zIVVr6tMc}mo@gD z(}5)edGhX;ai-_;MR%D%3a6%#?>Q82Az6q_V+-7zau&#y;47;>_xMu_%R8B**HwnR6Mm7cCB3F1dr7x;DZ4g+ZEI&ls?GlYebO z1r0r$Qf979ooM=eStS~mFc5`#o62U1h3_?uuqpFC?A|iCi6`zKa-i?iRZgKRX<#N& z7aTfmW!A|kn&UGtA|)=E!vNSYku4lga+N9u4!loO>oG{^nIah98^p&IPGKA5HWU1g zY@rI|2v>JN@=(qIPD}ZDglP(@ww2Agz(BJh=Hc5xcUzS&x_~TRau-nX{*D?=zNFD(-s|hOR$Fyx znpDRJY^=r&l9*z+75uJ-MSM}C!_AsnwCJbI#?F1mb8-cbf>MOC-S$4R-;8~BSqBm; znf#htru3oTkKm-%ZI5-ZsoXky^Gouof?fuHm8O;{PoviXlE<@!r_E%YJW2A)|^1J+5bd ze{`ovE5NNqDFdSUEy(GVTNn4ux4*Dpj{@8nq?T7mnPK8(IMnl)aDAGa%X+NH){3#u zMZg1vNG)u@*2gN`102NH>hhMX+uOlc!g?z{ScouO5@Z_PDV&fp;X2C~s^!0V1^N`G zMEUT%bT4Q9iMNY!T;S%Sy{@mnX~D#(zYWTxBy2KPi-YV>3r=XRn6g13jO-}f{-OT? zEUOT2YRr`#ca%Gu5gc(HT)2SFDl!C@orMim*YoaQRbg(I)>`%E3H;4rbZ3o_xe;m@ULOEaNx?7kSB*eSN~d@`KJqMmn&hWO80 z53qPhX?ftlflhE&aF8OjwCCcUzQhn$fkXd&xM^1xZFW<4q~F~^4db;){Tq|DX~*Y- z^CsSxCx4^$NDhm%mHOn2S_&o?{pC&80=q*!KE&q$9b3Zp(GgpFNPp3I!}cU~?btOX zPb(D}E-}p&kr{#f>uGA%5;WBgBFBLexw;$M06cIQ$g@T-Y0Nw~-F{E@S%_djBIp5< zb7eNB1dWb#bx$!cK8Rcuivi%+q|IwYt5t;`qYnb=*jR=+Ysh%uC*-{h~0 zl;a$J%!e|GUx&t2)X0DIHw6Xp#BbaNr+=~`fyAEa*A(D~ue<@juOW(gT@_-?YXKJe zzC2j+0QofV#1t75=(rC3s0_k_eGtBsvf^jc8j`Hl|7eu-x;S$SiCNfJX{@B{`W$}2 zN7VBnZ92nq{j*5s_qy5Od^l_|AEf#wPS0YsqZW1N>+&UQ3_f2ugNOsllCiQAHNz6q*j zlTBTk=8-#-FIuw`5ti5|h))n{&R1Z>;__3oZ7a84thO>{f=rKNi}5J}%!1au1!^xc z6a)xyu8ij$25Kyj9yu`-HdkL&0JN!M-0&^4UT%AZ(1-5RxF$Iu>J_@QlN5xu#_AruWH0*sWMX%+y zjLWij61;X`bhtm+KF5ReJbEUjiBuy&^2}k6lPZT+xMu)`R(HBSM=<}>cU3eNmO6HV z@z7qygPp+CnIY4C>t;6k_f-%l!_(0F8+L152FEH(F&fdyHt=V4Vpzi@6_!v^q=Q$< zWFj%Rx^;yBpfeCdeR=Y8;Cz$DM}Vg$c!VGq(=_x+zY63%a>?T9m~l4z)k$I$!5Z3H zZ4=ot%-i6M)OYRr_2<~*LKnUt6KuHe^*sN^xSTpLs}I&{I#by$qhN%+p8}2SBya3ewzaw}QNP#fDw0lWKit?k zN!k)(t`F?bYbyDLbP{h7>H%Xt8JWE~^Qa{4?tDFDJ;ap(aZU58m|uIsp@znrpXvVQ zR{QJg8&Fx&a6`4tAb{0ip!>JnNV={atE!odi!uh8{A@QQO? z<~9nW#sftYh}A{fs*|qAD0HRdCfT#;A$fbQ?LZ8rdMNM+!fr~m4g}sR6MziN>yfzH ziH7j|AJ5qrYfZyeY?Zl@+h(K4_}vSDy4*2I`AJmt<7v1T>p$#`Scr0ZZa0o z&*P@)Y%L6)%4FXuiza#v36O#JCjr0lBEcXnB~zNprc~cD^zA&)9w*<12q>TrqU!i_C40ThxqynDgZQ zBbA1l$5!QdHgR>;=Rw^j%NPIna7Jfub0L89YUa_(mtc^o8ntfSI&he$@P16bhV8ZG zqa(x4fO<4DPSOk_Rj@H2RiyV_6$(E69lTf>m=i5u-7b8T%j2GhzAS1GN-l3H2o!Md zwgZZ43450gebf%>-MNdX`=mKYVGdRm1dR%KBMtvZ;H^?XaOT;NO^5=)r~8O0ymWlV zpT4^30HM8bD<>Rwp~b@5hP>iJRBv0bhbx&!wf{zPE8q(&l_^@O5Ww-`t~D!JrTrj= zJ#I_$t5w?ksv@quA4^DG9JujBa-`v(rIFL)@naH%?9C|{uITek(*?azPjEz1K~J5I zTMFlq+>Y9G2Lk=RsPTZ{K2@4F7Pb0Kw_MC66&L=^Iq)eCwkk}-E#jS3r%f9~xR;HJ z>B6t&vs-_(C0a3$uGtGyDhU}o114ucXzJb_kiZKGjJ>*I4to)|G#>tzi^CC+E6QKm zBOJX*DR5w$BIe|jTF;@pnDz5>N-~1eFFrkZa~!JUitP4FL@`|BJW?fQ)M;p-x9VZQ zAo1{6Yk9<*+irqVAGj-;gg7^1%8Ir4FUDFiq9u@>P-Qi=VOA9=w_GZ-xK(nHb_!z= zrrnfbW%z%bOSNmRZQXJTQ?S%df;k`VDF$t+0$qd2`-pxu9B!jG8J}Ey%BMgex*BC8HV) z`V$;@8Zju!n5^B1T}=Rkth4V!IhG}Z6x?zEk$I^=P5jy|N?=hp|Fz}eV!q;nYI5cSKWhsBOm2{eh_kV?laJlJ z&%7QX2y==Q43ucMJd)5}%@iU387+TOPCGIq;LGk_ATXR#sm%vIp6xxXG=!{nK;LMm zRPbt|!4-3^5CU|0R(HP>oOsQ0_(c_6_JYgaQ*J1TEpEpJ_s=+OkcT14=tM@XC zi9NV`;+qc}R2k+{(+%~;8+@3UWq&2ocw`_Kt0~cn`?NugVZLtt`qKV~{SOxQ*LypE zd7BNYM}&?y@h(-lSvjJo_5>ake%VZ6GbLePn10ISEChc8IHq(#{2uc#8XjQD0}$h+ zZ|Z9x27!*i)CKP6&p3n~gWuCT58zccGT5!{d$Nvvcy4RtqGBymWNy~opnj<|CIA+X zj!M{8UOM)Wf77!B9BVQdk+hUTa)8|HR3-6y*mp6v{qo7n|Ne_|>MnfJ3bXgPS5iGy z-C|_1a4`Eeokiflmb4U(1^5v`0d};1bYzRtpG&HM%ef~(6wDWsQjjIq5gL1h_6Yd` z__Go%(~87Z4C}TTple{eBE=r324Smz`92u~v%9DP@be|RwiLy5Cvu`~))GXtwBpcC z$@_2!%jAY4ik87mDA5jJ?4?LPhTcyIIstW}?3PMSY11IEz7wpe7ew7P&QeXPVN(r; ztcj;#oq}D>o=3*{z0SE6lrhf1rX3&`!xatY{p5GxaB>ac5mK|zhoL-vagRxs{fDMFwn}t@3$Aiwwx(jgRIm{LEQ;iwT=g`a)kM%XT=x`o}suRAPhkY zs_|ua?F8nEGMjN=&fB|t;8D4AizEtF?=P9Wp%~HZGVlancE{h) zN##0_0LIW%>$hsFC@3+7vrLm(i9GUqO9xPBm-+Z{q|a)n*KxVv2!L(71g}#0-j~ZJ zAarSFqqI&e+QOC-vmwqLC}7kTS)5nBSK05dkZ&1|Ilu!xxCi64D!h|8K6!M~$4pc6sgW~srbQj#Bf*JZ5n!n%k)%|z#eP}wG@=a<#T4o?$&yHfD~S=% zz?5qO8BM_PJ4dt-3+c>d)SS{%OkBksgD0UIs|Ba_B+@;d_34{I4QE@UPlMc&6BV5) zANE$Ru75v9UHa_VvtCbBVv9k}Ct%08C{E@gB9ep{ZY^}6vK2?b>#0UcF;ohqo6V(W z7gW(YL4V+xs=Zf*l-p3)c+g&(kyuv|vgEK>2?|vuTJEYv64B1+)Ci?U4)hFNz}w%F zm4?+rzw4S_g-VSnysXJH|nlx+F*4GN5~Ix15D|H$T@x5vUe7dhiZyfU zC)vAaneq&xcXy$IB#w+tY{Cphl#u}l72XC8erbjOjl%fXIKnDL;@T$C@<#qFpre(Y zQ?jT7N-r zlVMYiJDF&Hs>kycm60x#$51&Oyq6pdz!GYy+xJYf73p52bEJVPUv?`PBIVgQ9P%g~ z-l)^GX}Irm5{*pi0>WWjj|PsROYoV77GKnT%0N(P`_4qk+{B5B^{r9HMuHZYar5^g z;VS_>kSr+4F%pZ;iUl7b;VHBdR0rOOBLM#RnAAz2s4E;{f3VEaFcOisP6hbQ4WcmWz9f2T&Hosf)j4enz$Txbe`;7ls`-xVFJWZA-cEd9Mjju)vzx zSZ&ko#C8&5FNO&r_E;~267+(DE_Q}Bw0u3A;jiMzdOs#h!ql=yn0p)WZQyO~!zt*B z*+IAqC|H}Se$*Pu(=gFQerp|tfS(XF^5d8Pex$k!Eok*f|9ii91}(w>HkCp=^rxhn zZE*1UN02DFWrp5u@O_sHvMCwKX<7{VKcHEr*Zt44Y)LwN01=#R1|B0F+we+&I#C)WniRM3S=?&^Rb}RdsFcsNL!JRJ%|>{8d^rVIp%$ z8jlj0H!&o|PtVS^wR$f>wnVX7WbF9W%rU8MG!T_Zs}Mf~Ev*{QN9vB~@lh(>?QhSMV@L;&7*%SKCFu6;xTpL+2-O7^#=$@2)AP{y_n&b+oeT zhTUyCi%#XyGoiCk925m~LU^IbSp18@NlZq7AQ;`=kmrZ}SxT}NW&{m)Q0)&z1aDxJ z-;sv(mwe$TBq_&Rj>uU4Q4ru-i<-t{^p|d-q8(pW8QliR`#)ceZJE2}hlKza%Njj~ z#^q6>jQ|42j)cPwx5`w+>uW>*8?)lKQG*+N`sz;M#AYNJ@mm4r1=fef*;u{T-Rxa4 zpE`e1I6^Ma#DFkdjTSNlL4D3@+iV4W4@ESm*e4)&!y4tmAUrH2Y))UIIJ2Cp;N?Xo1?=OOSLZ3mwA`uaqTc$l*w175-D zfM(cF%`_0zv{+obFl96GPQ>qm2htAsyb7PEVt@r0V8P!`#U~cNKY{{hvn}|c$yP=` zT!_h~m%)AB(DnZGoQge8h>P$jArwz%pi)g>5u14mIlb~o{~4~O!AIGwc%3ajo^EJC zIF-#axd~G^k<0t{dW#^ZJuF4=OyDk1Y@t`-Zq*M&E&jTaoGJ=Ye-lMwu+Qc9RwFgH ze$|L-=fJDjhq4qK!4`TWsV*Ux4S)j?4c;5%n>^Ws;t4UQPWsCTps-*+g3y59TgGc! z-wOPCCb1ZN8VWBLQPClw>Nm z%#STbDf9&Oep3gFfTy+%PQdcjH3-ST8H6^3*TH`|Bgu7=OWkltPFZn zRRqJr0f3IwjZp}19%J#S8+;tKgz)5-7lW&nr@myKyooscO8En#*4f`Xc& zIkj%gY4ArMY6wbZJop}l>jx`@Ex7n#Y71xelg;NByv^Xoi4lSYbU`;de z_N06hEy@+q4RMjZ!h9~ABBceOEi+*6lK}Ih7g4i3ak*J&wBg!Xyp__gpGdj9Z6$*y zUxk)4#KkS0s^4#V2F%sE-+q~^%b4{DbLvh&szR;MX>KuhsA~vqy>bYUYnWrGQ8Ie= z^$i)3bcS#mPO&YXP~A_!)x6mr%e7_Hz$D8f!0tJAs`{dqb<#oE8#M8~9vp5#=e>Z} zin;N>ntd1}5i*X-a)H2>p$$hs0~SywafU5cxB|#AtntH>Gt~I=KIgXH-M720pI>}n z-+pQ{mko{pb10h)st&AQTHTuVg3-Z(60PW+Fq>Vp3W7rX@Y6n!tVE|wk%nv|YukkF z3==GYx|%J_d4N^u5PQ?-8}b}XtL)8R;@jYQ2eO2`x-nsgWE_*!E6|gPI?EJ6>Feqo zhrk~niJrZ}5y~{jL5rcLkX&^6*i@>U6Cxod53NZE4s~-Nk|8la2#=ZqXQp8QAk~XB zs!TvpjVo03K8|Mu39iR15E7x7HWJgOBGxS;ga1YLtGEFxhZ4>Ci(@Zz^@7A_w~YR* zb<#hD(odK}k~-4hx>#`ndqwPoHcm~!NXXvv_P)(E!Hhh0XuKWgsSl6lg z_l}p!SrQ-UzRfdlziM)!HdeSXgSZ)|2Y1{KT6jS7-)&MTrUC30T;&K^r@&0(_M-q6 z)*woa*Vj1U80fp(SZO;Xql+g3c8p1~gtq6oeJDTWq#trU{k}z~ z@IAP_=e@4k$7&Huh`X|Z&gBltmB&yEf&hH3c=7`s^sW2l$~B85T%J-J4_C&*66VqZ zN`DPgB&iYph3=Zj{ECn&Za@cu&O%)VF9PdoMdNF@pjBY)K0%9+41;$T-wPUm)nXDr z82>8oGsusYA3=&-2I3dHY5-{+cGr*l30YgdEyKyv>li;LFCdkEUvDFMQdq9YkM*h z0gU2p`NH4cw!Qb!Z(x! z>f>cIxmA=Q*5GT!{V$iKsWIk2hhlAp%O39rLEQ<@gJU50PK`CK5$;n-;Dtq$#fv2L zQmMHVnP4;t93-c{{gB)aYwF<=lr-1wt8W@(5r^4hIwW@@l~u#6Xs~{nB3+_2n`3^ zxhZ*{fzC#uDz*x%`ZHKi7)YG*uRs6KTYql9Mm*cR#u=W)k}0IDi@7sIJrxegH9KQm zeTqe>8KiL5o(3>4!>^u#%Qj1&fE-bRVLgbWHebQa{C$u9H3?csh5LRh&ZHu$1_KFbSQ&*VVbgbqoDODKKNzabQ@0@Zq>dRaaR3b_YX z5#F590r_6sL~kx%ltDK^d~lq9Jt?4HgcvnY_eI1dlXEbb7bsBNLbvAsdaCO`kE7wt zoca6l>vW~#_h)G8mk4NzU4@;bx|H24R*0e6Z%NQ6@_!rqf8g$N%|bpyIp`rST9l!u zFb@6z8d?QIvweyaIg|efgORF@xc5IW)&Ine>gG|;+VQ*Q91Bq&=X#|?+C=VF^0{i55JkY~nV6#l98-@}K?Xv+WfM*q9`-8