From 0ab96ec226a8a15aea169bd2e0aaf22ea34d3cb5 Mon Sep 17 00:00:00 2001 From: Peter Yefi Date: Wed, 22 Feb 2023 22:45:33 -0500 Subject: [PATCH] Completed changes for new models --- hub/exports/user_factory.py | 18 ++--- hub/hub_logger/__init__.py | 3 +- hub/imports/db_factory.py | 27 ++++++++ hub/imports/user_factory.py | 16 ++--- hub/persistence/models/simulation_results.py | 1 + hub/persistence/repositories/application.py | 1 + hub/persistence/repositories/city.py | 7 +- hub/unittests/test_db_factory.py | 66 ++++++++++--------- hub/unittests/tests_data/pickle_path.bz2 | Bin 0 -> 25178 bytes 9 files changed, 87 insertions(+), 52 deletions(-) create mode 100644 hub/unittests/tests_data/pickle_path.bz2 diff --git a/hub/exports/user_factory.py b/hub/exports/user_factory.py index 0c932418..cb3d5121 100644 --- a/hub/exports/user_factory.py +++ b/hub/exports/user_factory.py @@ -4,7 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project CoderPeter Yefi peteryefi@gmail.com """ -from hub.persistence import UserRepo +from hub.persistence import User class UserFactory: @@ -13,19 +13,21 @@ class UserFactory: """ def __init__(self, db_name, app_env, dotenv_path): - self._user_repo = UserRepo(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) + self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) - def login_user(self, email: str, password: str): + def login_user(self, name: str, password: str, application_id: int): """ Retrieve a single city from postgres - :param email: the email of the user + :param name: the email of the user :param password: the password of the user + :param application_id: the id of the application accessing hub """ - return self._user_repo.get_user_by_email_and_password(email, password) + return self._user_repo.get_by_name_application_and_password(name, password, application_id) - def get_user_by_email(self, email): + def get_by_name_and_application(self, name: str, application: int): """ Retrieve a single user - :param email: the email of the user to get + :param name: user name + :param application: application accessing hub """ - return self._user_repo.get_by_email(email) + return self._user_repo.get_by_name_and_application(name, application) diff --git a/hub/hub_logger/__init__.py b/hub/hub_logger/__init__.py index e4d66f89..4c3dd95c 100644 --- a/hub/hub_logger/__init__.py +++ b/hub/hub_logger/__init__.py @@ -6,7 +6,8 @@ log_dir = (Path(__file__).parent.parent / 'logs').resolve() log_file = (log_dir / 'hub.log').resolve() try: if not os.path.isfile(log_file): - os.mkdir(log_dir) + if not os.path.exists: + os.mkdir(log_dir) with open(log_file, 'x'): pass logger.basicConfig(filename=log_file, format="%(asctime)s:%(levelname)s:{%(pathname)s:%(funcName)s:%(lineno)d} " diff --git a/hub/imports/db_factory.py b/hub/imports/db_factory.py index 67891f30..e58ce073 100644 --- a/hub/imports/db_factory.py +++ b/hub/imports/db_factory.py @@ -7,6 +7,7 @@ Project CoderPeter Yefi peteryefi@gmail.com from hub.city_model_structure.city import City from hub.persistence import City as CityRepository from hub.persistence import SimulationResults +from hub.persistence import Application class DBFactory: @@ -17,6 +18,7 @@ class DBFactory: def __init__(self, db_name, dotenv_path, app_env): self._city_repository = CityRepository(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) + self._application = Application(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) def persist_city(self, city: City, pickle_path, application_id: int, user_id: int): """ @@ -36,6 +38,24 @@ class DBFactory: """ return self._city_repository.update(city_id, city) + def persist_application(self, name: str, description: str, application_uuid: str): + """ + Creates an application + :param name: name of application + :param description: the description of the application + :param application_uuid: the uuid of the application to be created + """ + return self._application.insert(name, description, application_uuid) + + def update_application(self, name: str, description: str, application_uuid: str): + """ + Update an application + :param name: name of application + :param description: the description of the application + :param application_uuid: the uuid of the application to be created + """ + return self._application.update(application_uuid, name, description) + def delete_city(self, city_id): """ Deletes a single city from postgres @@ -43,6 +63,13 @@ class DBFactory: """ self._city_repository.delete(city_id) + def delete_application(self, application_uuid): + """ + Deletes a single application from postgres + :param application_uuid: the id of the application to get + """ + self._application.delete(application_uuid) + def add_simulation_results(self, name, values, city_id=None, city_object_id=None): """ Add simulation results to the city or to the city_object diff --git a/hub/imports/user_factory.py b/hub/imports/user_factory.py index 3d900c1f..5053a6e6 100644 --- a/hub/imports/user_factory.py +++ b/hub/imports/user_factory.py @@ -4,7 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project CoderPeter Yefi peteryefi@gmail.com """ -from hub.persistence import UserRepo +from hub.persistence import User as UserRepository from hub.persistence import UserRoles @@ -14,19 +14,19 @@ class UserFactory: """ def __init__(self, db_name, app_env, dotenv_path): - self._user_repo = UserRepo(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) + self._user_repo = UserRepository(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) - def create_user(self, name: str, email: str, password: str, role: UserRoles): + def create_user(self, name: str, application_id: int, password: str, role: UserRoles): """ Creates a new user :param name: the name of the user - :param email: the email of the user + :param application_id: the application id of the user :param password: the password of the user :param role: the role of the user """ - return self._user_repo.insert(name, email, password, role) + return self._user_repo.insert(name, password, role, application_id) - def update_user(self, user_id: int, name: str, email: str, password: str, role: UserRoles): + def update_user(self, user_id: int, name: str, password: str, role: UserRoles): """ Creates a new user :param user_id: the id of the user @@ -35,11 +35,11 @@ class UserFactory: :param password: the password of the user :param role: the role of the user """ - return self._user_repo.update(user_id, name, email, password, role) + return self._user_repo.update(user_id, name, password, role) def delete_user(self, user_id): """ Retrieve a single user :param user_id: the id of the user to delete """ - return self._user_repo.delete_user(user_id) + return self._user_repo.delete(user_id) diff --git a/hub/persistence/models/simulation_results.py b/hub/persistence/models/simulation_results.py index defc3e7e..d4dea88d 100644 --- a/hub/persistence/models/simulation_results.py +++ b/hub/persistence/models/simulation_results.py @@ -12,6 +12,7 @@ from sqlalchemy import DateTime from sqlalchemy.dialects.postgresql import JSONB from hub.persistence.configuration import Models + class SimulationResults(Models): """ A model representation of an application diff --git a/hub/persistence/repositories/application.py b/hub/persistence/repositories/application.py index 91d720ec..2aaecd5c 100644 --- a/hub/persistence/repositories/application.py +++ b/hub/persistence/repositories/application.py @@ -42,6 +42,7 @@ class Application(Repository): if application is None: try: application = Model(name=name, description=description, application_uuid=application_uuid) + self.session.add(application) self.session.commit() return application diff --git a/hub/persistence/repositories/city.py b/hub/persistence/repositories/city.py index a1459c96..83a42e6d 100644 --- a/hub/persistence/repositories/city.py +++ b/hub/persistence/repositories/city.py @@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com """ import datetime -import pickle from typing import Union, Dict from sqlalchemy import select @@ -60,8 +59,9 @@ class City(Repository): for building in city.buildings: object_usage = '' for internal_zone in building.internal_zones: - for usage in internal_zone.usages: - object_usage = f'{object_usage}{usage.name}_{usage.percentage} ' + if internal_zone.usages is not None: + for usage in internal_zone.usages: + object_usage = f'{object_usage}{usage.name}_{usage.percentage} ' object_usage = object_usage.rstrip() db_city_object = CityObject(db_city.id, building.name, @@ -101,6 +101,7 @@ class City(Repository): :return: a city """ try: + self.session.query(CityObject).filter(CityObject.city_id == city_id).delete() self.session.query(Model).filter(Model.id == city_id).delete() self.session.commit() except SQLAlchemyError as err: diff --git a/hub/unittests/test_db_factory.py b/hub/unittests/test_db_factory.py index 2a8c0719..a4544a6b 100644 --- a/hub/unittests/test_db_factory.py +++ b/hub/unittests/test_db_factory.py @@ -9,12 +9,12 @@ from hub.imports.geometry_factory import GeometryFactory from hub.imports.db_factory import DBFactory from hub.imports.user_factory import UserFactory from hub.exports.db_factory import DBFactory as ExportDBFactory -from hub.persistence.base_repo import BaseRepo +from hub.persistence.repository import Repository from sqlalchemy import create_engine -from hub.persistence.models import City +from hub.persistence.models import City, Application, CityObject from hub.persistence.models import User, UserRoles -from pickle import loads from sqlalchemy.exc import ProgrammingError +import uuid class TestDBFactory(TestCase): @@ -29,8 +29,8 @@ class TestDBFactory(TestCase): :return: None """ # Create test database - repo = BaseRepo(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env') - eng = create_engine(f'postgresql://{repo.config.get_db_user()}@/{repo.config.get_db_user()}') + repo = Repository(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env') + eng = create_engine(f'postgresql://{repo.configuration.get_db_user()}@/{repo.configuration.get_db_user()}') try: # delete test database if it exists @@ -45,56 +45,58 @@ class TestDBFactory(TestCase): cnn.execute('commit') cnn.execute("CREATE DATABASE test_db") cnn.close() + + Application.__table__.create(bind=repo.engine, checkfirst=True) User.__table__.create(bind=repo.engine, checkfirst=True) City.__table__.create(bind=repo.engine, checkfirst=True) + CityObject.__table__.create(bind=repo.engine, checkfirst=True) city_file = "tests_data/C40_Final.gml" cls.city = GeometryFactory('citygml', city_file).city cls._db_factory = DBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') cls._export_db_factory = ExportDBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') user_factory = UserFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') - cls._user = user_factory.create_user("Admin", "admin@hub.com", "Admin@123", UserRoles.Admin) + cls.unique_id = str(uuid.uuid4()) + cls.application = cls._db_factory.persist_application("test", "test application", cls.unique_id) + cls._user = user_factory.create_user("Admin", cls.application.id, "Admin@123", UserRoles.Admin) + cls.pickle_path = 'tests_data/pickle_path.bz2' + + def test_save_application(self): + self.assertEqual(self.application.name, "test") + self.assertEqual(self.application.description, "test application") + self.assertEqual(str(self.application.application_uuid), self.unique_id) def test_save_city(self): - saved_city = self._db_factory.persist_city(self._user.id, self.city) + self.city.name = "Montréal" + saved_city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) self.assertEqual(saved_city.name, 'Montréal') - pickled_city = loads(saved_city.city) - self.assertEqual(len(pickled_city.buildings), 10) - self.assertEqual(pickled_city.buildings[0].floor_area, 1990.9913970530033) + self.assertEqual(saved_city.pickle_path, self.pickle_path) + self.assertEqual(saved_city.level_of_detail, self.city.level_of_detail.geometry) self._db_factory.delete_city(saved_city.id) - def test_save_same_city_with_same_hub_version(self): - first_city = self._db_factory.persist_city(self._user.id, self.city) - second_city = self._db_factory.persist_city(self._user.id, self.city) - self.assertEqual(second_city['message'], f'Same version of {self.city.name} exist') - self.assertEqual(first_city.name, 'Montréal') - self.assertEqual(first_city.country_code, 'ca') - self._db_factory.delete_city(first_city.id) - def test_get_city_by_name(self): - city = self._db_factory.persist_city(self._user.id, self.city) + city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) retrieved_city = self._export_db_factory.get_city_by_name(city.name) - self.assertEqual(retrieved_city[0].lower_corner[0], 610610.7547462888) - self._db_factory.delete_city(city.id) - - def test_get_city_by_user(self): - city = self._db_factory.persist_city(self._user.id, self.city) - retrieved_city = self._export_db_factory.get_city_by_user(self._user.id) + self.assertEqual(retrieved_city[0].application_id, 1) self.assertEqual(retrieved_city[0].user_id, self._user.id) self._db_factory.delete_city(city.id) + def test_get_city_by_user(self): + city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) + retrieved_city = self._export_db_factory.get_city_by_user(self._user.id) + self.assertEqual(retrieved_city[0].pickle_path, self.pickle_path) + self._db_factory.delete_city(city.id) + def test_get_city_by_id(self): - city = self._db_factory.persist_city(self._user.id, self.city) + city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) retrieved_city = self._export_db_factory.get_city(city.id) - self.assertEqual(retrieved_city.upper_corner[0], 610818.6731258357) + self.assertEqual(retrieved_city.level_of_detail, self.city.level_of_detail.geometry) self._db_factory.delete_city(city.id) def test_get_update_city(self): - city = self._db_factory.persist_city(self._user.id, self.city) - self.city.longitude = 1.43589 - self.city.latitude = -9.38928339 + city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) + self.city.name = "Ottawa" self._db_factory.update_city(city.id, self.city) updated_city = self._export_db_factory.get_city(city.id) - self.assertEqual(updated_city.longitude, 1.43589) - self.assertEqual(updated_city.latitude, -9.38928339) + self.assertEqual(updated_city.name, self.city.name) self._db_factory.delete_city(city.id) diff --git a/hub/unittests/tests_data/pickle_path.bz2 b/hub/unittests/tests_data/pickle_path.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..b49164896e9329d76cd82bb90f6f258774091a94 GIT binary patch literal 25178 zcmV)WK(4<+T4*^jL0KkKS!eLI(E)4NfB*mg|NsC0|NsC0|NsC0|NsC0|NsC0|NsC0 z|NsC0|Nr1~J>3AR08j-0kO~DTD4|qRKqyo1zWLGN_jL`f<^TW-rSXmQ#B+Rm-l@FN zE4ywxHrq})+jYCPsdcW~tuwS;cGLi^ZUY9XO*#isdiwU>THXKv0rWip0MHH{J|TPN zp1tJ&00C=(QHF{ z-~a#sF1pjUcehe|+S@Kq3YgcsU;qHf0PqldtJb$z@VukPy;KBmK>ObhzR|u~yuCW+ z)3r9tR>ZBhr^q^9o-XXyZt74{hbY+UplAW=0B8UJ zX`pG4A`lU#rhrC-Mn|AVQ1XKiGy_dB8ZsV`8hIl|hMH-K6Kxw9c z2AUcG02%-Q&;STZNf9Q4Nu~*sdPk;eX|x3%DDg< zQ%wQt8USd>14fOg8fXL5&;g(TJx8c|fCfMS&_tvp$uTe>O;h$pnNw5L{ZC4Mo~f9H zKT|am(dn`j@eH1-eyRFI_Nn?5{Zq*_^hWZHPg7z~QL|EH+GzBRPfbs0G@3I(ih60N z(>9dPNvYzD(^0Zy)NH1jHm9f?5Yz~ikV8NdO&T-+8VRElOqiK6p3+m(4NMc%VKmbO z(WV1MJdHg-Y3LNpK+{byskWGmBStBnh#3KrpQA%1Jkub_h+!C~riDIJ+5D%t;qZC3 z=ROGtspW-W1naH3$9H#J>}*V#DE}0+O*%o-p#@4ZjO5G`_C0_Nb*TenUu$s~DJ;j5Dy=@KY*Xn{ksc+hPUYRni?<5K%TQba9K>~y;} z59`AAy$~d>93z<{e=Za0JWxlwhb<$VtB9BkE&~bHS8c`#3jFZzKCM=x8+tq(HTAmk z15$gIj(84sLkvQWL2z8&O+cc#!_lD=E}%IsmSRwY21KuBo*SY~n<3={lxRb#!HMYa zMEH3-gRmXKpTu;YxY$c40TOtv=c-TE9TbOGKNG2a(70j;IPDydGKvZGD9}jGlPNMx zlp3Uzj&g{eCejS&!=Wx(UvvtLgeDfHxngpAy4{8t3^c>BUKA1saln;^g4{HzO6wj2zeEasW^y<8;=5FjiuZeRy>*Dt@X2c%_j>Tm zH&++XyhhPGPPeAGz&v3(HclzQcm#(PMQQ83s8fB3ppl*YPZ&fayH&QE+^dna;2wgz zJO%i1dNl5*x8lc+eZM8*q*q{b)GaHgX=qcn?(JP6X?fJbeFmG}LktEI_X#cg;lZYJU{RCpNP41{*yM7T zVUQqHB{AF}a`N;JJZGP62kQj{@QCXoUt!9`$p#_MYzX5| zl7Tc_tVLjgU_8Ur5sBqDR#68E0&w*%CSC-IMH=o+Qj+YUw-hzjjki9enR-HyR+waO*AAH zmX2n7k6jQhWMKA!T*NVOff(v_#od~k3^W)FI(0e@;jpkciu7&3>A_yrEW=~hmX83( zk~zrlpm>d-Ap>~gk|>XPqw_7#k_As<5DvY(1a0vBj&9ufh3NbnHN3Vg3XjmV8V%sxT9iX^twK`;Ue zP^ddIvIejZhLo<2u zTjvt2yb&EpdAaZ{w7JfX@d&+RTBp`-d$tTR8*dYi;yu@}eZs)-HV|TPPLlbQxb9l% zI>!C*H{7RjOL#)zF1NkQ>p3d@>IMX2`J9pwXF}rMNHMilT z>8Nbj)G~3V!_?p8_nmBT#)J{-kludsn?8DSwtdAh+mB=LOCleKmgu_U-0nH)+#j%U z9Lv`3o)?J*oY+t z$O#Bvf!{!V^P*^94oKW=H9OBDIBUalI_}3#o3!P_RpIO<&~?b30W9Ulqoo~TDS7a! z>00ZqMwgRewi*WV`yR#Pmh0!^&oX!KDVrpdx{syrH;UeQ=Z27XJDN>gJx);b$UY1A zs=%HF`^hR=TD=@oNmpCJNKbmAlM8Dr`hYw6wCZw3*<*Bl`T6f{>0yJ5#fUI4T~2CDH^S~*N?tbqWTTT$k%DO&-m z0EPwxckZ?aJ$t*=has%GJI?&c;h^!T5yehduedGzQQL>0BO!31Cs@k%KHYk>JYIcr zr_7zK9o3C7b|5vd5yLrf)(EYTyvCIxaNfc7F|RFlRU~@QbB6|&q^ZS4FtKb9 z*W)4umJT5cNjjWcEEsM4H^#j@-Tc-bNzO+X$UVMo`ohr5`3zwlw%1*B(>U3Nj2;x` zged^hPPt0NY)L1;Q%NMWD<^Dups~AQRFvG+%w)xS+BbXMPTH`@rXJ@_@;7Q|PTE0+ z9dd}g;u##f;6}8ZsHxgdHMc$dM;@v7n_Ict#!OrZju^wlCKAK5;r+*E!-(#-9?)~} zo6=5>{o13L55V)>u;r8;rtLkq#M8MJrWP}=daZDoh zz>IQ8)Gdbn1J>=Fbn<&h@1FM_Ut^J_YDr+2ci?I~-p_fa7tG%*i5;hq<&m^@f_DWQ z%HM*6)|uap!XSG(d&3M<1MWS}7HCJrYldZN)r~WX(v_8zF`~Sqrx;P3EquNGy`xQQ zXN)R+VT3dc=iI<}kZC3)cZ@dP-W9>6xY;)orbhBah6%U|8gxmKm@(Dqu9Et^MKua! zFsy;EX?%}!BJ=A#Wku=T7>Ik+loXV_v?Uo*mc!P}F*b>XPCBFzb+m^HSDjc>gI)Xf z9upp#k36kqf-Gq8QA-32QFqZIfUZ1P1$A`q;q7F{)Ek&l# zQ)FC3dBuB|MXxV{Hjxk)qr&6jJp*A$!@-7JS!i5tTeFuH2@7H!3raw+$+rpeCCT=kI(!}LB+Wv1)nZiGSMuMIWDVY8N>JAhmcTn_;^ekH;S z`OBvVFW-m69xH`Wx3M}YaTf-3xXu$z8XE|j;^DN5k9SIU;jj{k$WlYGCi)#(3D;ax z8U}(TG5%|Oc>^ayZ-Ylgx1KR12VAcuIVICA>1a7aK*`jRDjj9>qI;^H>8x9GcyvH_ z*e4`H>>j;zp;+@lw0)DT3y)qicY0x|uOqj3*S?yLE&aIm-qcXm`0($ZNI+Y+I&&gY zk&6^8aNe-}j=Esq|oz(^1@c_BNnG`#dke_7klc2CK z;$6j*1abiTxgyO@LIypF5 z5DZC}nQ-C(oxrCV8-$;Wa^|F}g(+=HrBzm{wM7cUupNl{;n@x_>_=WZ*zCuA9%ObS zmN|p59g=L_qwF24ws!9{^KUrr9m~$SujxCcd3x&ZuL|<7O7^{l?5pDTl={A_^1rMC zz^oF%tRljy1wvs8>mfmTHYlQgn#|3xZgX(kS?4%J#mrT;Q1Z+Z$8K|x?w@t|6Yzec z!}oLOd-(mQ1;}!q-pOZ<_I!6LSE{PqU7FW6)fX825(FfW!6YaYr3QmqGES`+oueC3 zQ3Sr*W06{owl^+G^NOzBVAq}F!Z_++s12iSj7M%!?@l46=UC+!uQld~z%FdNw;OIV3A?G|eU{A}#kcPU&gDW9uqmRk276RXT z81b%SYcH%hTz5{%O_EUuU?A|{*`_!~9#w*@D;Usz3yZqpcf*%H5b=&$128OHkD2*l z@BAM0cmeVG0RWaIg+QQ4Kr#rZh=4#nD29yjQAF8jUTRM)XnK-1f?Y^+4<}X4#pKB2 z@UTpAgGgRm-P@Lu$S5E$21H>H_pomcHQEeyfVSi-NS-0!@9UryMNGs6MwMOEwNzD` zz`29KaUfsZlaj*jk3Nr#E z41^4Xgdrg!o%arP<8{cO?KqN>BSDECgdqtI$OZxe5P&=@c6vwi9+Tx?J-XQskM5FU=eNRik z|B81>-QE54b@w<9XbDZV)st;A^sw%6=uOn=F?@1h}r`MiEMOTtl zc_dU->9JRwA|fIpB1e)y9b?XRgmeRtCKC%$Vg*PG_F1mImcz?09sQD_0B*&){;tdJw%!!e*=Gvtr-c~MC*NlEXCw^U z^Jd@8oC}8xFvAQlrk8H##vM!3n@lwCub`8zERXBoc0HW=!;&sEY}wKNT($I>0SM>L z#LG7gunJoGi13x;nVPfyW9sDvB)O!>0ziPs4`|3_utblD|Fo?7sQtqTLW?%(SY>gNPZkZ{FVS zwzuy+d-v1SyLz;@J+s?Aw$nux)1$8Iv)y&qQ%xMY>Z+^z6|b!H?c1xbXpaqZ)bs>^ zI|-O*(R}ylzU@hX3iDY!-QNo zbH4sEMTNX`tBu@K#_O(CmT}jYhX$&)nrPpnLw57$zkM{*zM62u3^ZST_wTC?P4_!U zPm0GmcRP*trG}OfTV7QmVTr>%jZ3RR#eT0`2wnTn(n$*E-X5SgjxiRHdnHUaaH^U$ZB zhdulFt^`02=sGv&n=i{L=EPpSh>RtdOQ)r#^LU*^5vU*Gdo1H@NELe;NGB|I$b)39 zrUF4a0w5riaN+T4UmuIxb(?a^mbhe33s~K4#^%oob@V^`$DcB!=S|~1sg#fejVNMR zjc6JbrAk(SI*lrvkptO0#SsM*pJMSN*rD$b{9-Sh{#8{~Rab#S?7sO`RaI5z(Ej2c zi4WNL&+T^Tx`7RTsSW8584ucrvO?htge!yOp|L~CL-`@)q2?jxp}(QSp}--0hWdx1 zhpL9r41yQF)7Cv=N)ROoQj$_hIQRXb5a!VLqq-slA^;Eoh=7n$h_1N~y$=Bnc@GH= z+3gu24_x-|z-z9$>#n-W%Fep$uD086(1+8pRa5LgT>r=>3A}%h^Z!(Pv_8A(e#*WF zdoU6vK|r8q0>3!Hnm(0-HiH514mE)A3rmFH=j&JseK9s7!5Ab*_80>qB!7W`Rty6H z{_Ma$nZuVB9>u552?(dpL*l4%2zN+vsCmeD=yGa@ii>Cy-6HrOABEK+PjSxgPF)mDP%u;&C`DC( z5JcN3cF?j%q?=$7HdLU9RAH#}eb9O(pKP#Fg@PS|6sQuTu0-Zfo&xtKU_QP$=8@%L zAtEN)Akrujltj29NT7!ADJw}zbqDe)Lu7}$L&(FIIxB#<2j1c6vs5K@p-^&r9np+!EFRD@0t!wf(_;DkL`iVOACd$BSQc;OR8;qx#(!7x_4A7qGoiOOXO92?iqTdaSsa zAt3{D%0ZRrfy;>$%SX92`Ef&m)PKbXdg0>zh0enG1mL_NP727Oa|?tP>{Mnk-YoGzj=D3SspGAM#^kdrb2#&Mrn4lp>eIH}%uR|%uHc&9ZHP)Y(v@`c)_#YV(>qx+Fl zj^LQ0Ax{vv-huhQYFY{d_Qa-;#5zKnLcEV4qMrntVA8D=CF(3_bkelwU9t})c>pCe zv{%@l7Jw&&R#b;Kd&?Xp4)5Q|XnZg*94|ka_fzesHr*Y+SFxMTYNHT>5Md;S1VMdD zV#vu2D~}Y%ipbD{S{9abw!KcJN8OYFvwBtuS18VG^btE02XJ?$p}UC4pdLhRw5B8* z)L{aELrE1-eiwZYrRa4#U%%h0z20~}wpw?cm$Ytl-Cahb=M_|K zo>#k_f7;p zc0ulcUrg|V!Bh(1T07f8oL;y4~DDD~xDeA;kGozlFSRzOOneqgH z1t5fhoyl|8U4QNBI(-jkm*{r;e5r^=*#X1EK>BS9IfFF*!mxaghiqE8Vn2;ko>7=? zPmpkkBM~o-HCK8XZ7|3Vo&_Lw=MeqUkQ z>#{hrQTP1*N20T|h46vsAxT03@c|M=G$kMu)d4USN;)(iLpMm3FYnM!eL&(OIJUt% zkzB<(Q2SuN;QBN^skv0ba>hysNC^lI1Q@`Q92gnzr44ESXDznKe^Zsxw$*ZX&4zOu zEkh~)izkt1k$M}dm!1?OUxKg~9rr+9X?+17MyTF`cSJq(=cDXnj3EOzn~NYT zAc#nBX4`Lf$m{q2U;g4t?k`)fdMm#rQSb6H(l&bF=WJ-Ax(E^pK`OW3wi$;6s7Z^N z%jLk5YUqH-g&=@P5;?7|mwMqj%6irBw{o{tx0y-%<~L>RVzQfzp5vD$kV(U~Pv#s> zFf%hIU}>|2SnbdDqq-V(O{u!|1)UIvi6Rv|$>P@d;il06ql+NONQ8hADY{BP5Xbeh z|Gw-$ck|Qv9-m{rZfB>m+ivl>4xKaaQ*mnCJ0w?t5QZ~NTO|NCsw|R2pcz4ckjnv7 zJh4a972M}CjEtCp>LMai9Y25KjLc8fb(A-rRx4){h4p5%e$|_~u987gF@YOV08#={ zVqOR&;1J_AD4w8+2t7QM3Q$dXbbFNDxGk6fSLFg#Bn#u5cM;;vf7<|RAQk`a25Cq&!@NZ( zoqRE~(kY#Ufk*|hl0aBFRam$gFR7tvsoh%b+T46Szk5ebV>X``njDBja%q|Y4vf0#1h^5BMnXao5D&(3LB%`2@!8|OKBJc7;~@wV zNJvPsJH{zUHD@M*ib`seNurn1c<71yU$T7hesYv{UrJJP#SLbh`mV9pTM&s*B6(wn zK>H<-8RA_yvj?n32mKWft-mO2IsynOwPu2mN)ghAguxnA)f6!RJ7a7*pjsAyGYg~# z5|NL|AILmo-;al=K^gZ*!h3IGM0|@?Q%5!3OP+*R*r^B_dUjt zen%Acs)(tg5bD!6>H;J<5Ulh^j(GOM3I}=9&!Gxj0UQTiYAwe5U-;~IKBo)w#?ET} z_?lSGrL%**?w6oaY?Cy%O`?JX2X0ECIqD&&78+^m_jCh0C_yXz8a>emZvwv#JP?_JN=eOm0U#!(_cs^eNr*;hg(i7>F6 zXqtG1Vd+!dx$7%z0bK04Lxnb{zX3ijC;}Z$PF*f@vy+!?=j{p9&E&e9y);kBtk3l^ zeNLpg8iX>fQMJ6-DKxJ`nUT^plLY?UV)ZP555Lx~eA_#{-U4=d^S#K#YiV-5EpJO( zQ%!>2-*>Oc*$hf4*#Q^#|MA|$cImYD z-UR=St)d{{b^*6a6lw*5P)zu0knT3jA}?$0+}FNium4%E&v;? zFH^7E!u9i9>3&is85(laR%i_L?lF z6W&~~x)T>S0=R?)CBnIZuIwDjzigd%Fh3QE*;y(^I^%B-F-D+>n5>nUq39>RVGARK ztE43tck6cbeLpLY|7_)8<0`v2j+PIp7b_YkC^?4csvv*chzvr%4%e5H3S)2qxdvni z_kb#4BtZ)bz#SQ$cP^$sv1g{TbNBL=7_7{$W-mFL=X_hQAWK$g07^dN>=hf)IrHG( z0c%O|N{4|&*p~nb^G8fw4zOZDW>UfsWoA3rYMy3`?cS4A-a3c3wv*kL69FP4;icP=fEOgA_=K5ePv8;{j6$nL$Zag%!}NkU~*} zVOCWk03jIxBr1R?lp&NMl?YfEC0HZ^Bq1m$U?PG+h++h&q+nu10#$%Ugh)m}NJNBW z5>y66kQowGgke#U7)F#JMnE4Ckw%yIb}hAtLJPb z&iw0gkPu!n5Fe)_k>vV(eEaVr@c*CwbQAgxHu9dHo}ZJnx^int0oy=~1pzLS?j1N0 z&=7#th3Co z^EF>bGnoa<=4x_V{%13p%(ZLgvmD7?xT`6{&OvD$luD2QCC43^9uOo+f<;ZnrlzMq zoz7i&+uzEk37n~fsh!Q#)YR1RDs~a-nTX+!B>FpW0Y@_wNd!p+kr0GI02b+`SOEw4 z$tv!%DKFz*O9T-uFp>=JbK=4LKMrf)&vSTOUSATTvCFJ;`C1BV@6+JqOMnse_GdkD zk=8yuY!ZZC%DID!w3Ts&{mLHg08`PZ0-TT@0Gd&dlo^B!Uise@0Dm|FNJ&=`L7&fO zv)SC{a*MSYS>$xMeQr-%mywh4f1WOc{r2PnRmY?urHi|e*CK%7=Wa;~WHh5;XVaS@ z0oE$Q5Fl>Bdu{)p&74UjiG2z{kO>!tis&Rk=xDSV{Qi0l@4>V9ydBRMW266aJKS#1 z8^8H`8C2xua%U5)p7UsU$;1f(xI=!q&(~$Z;+^{!J}c`SvM$ z)qxm+t&f%GPMmbN=s@gn(at0Y2>_=CH3u4W5(_Df=9aXQI-r)Bas(ns5MlCkG!z(I zW-kTy%6pi~f49H$%2@lFdPvlloXBP*hC__Z1HdpCw?6-01exbVAXy#!*+z7rQ;GjJ zO59#Gj;a>aA!fBS)_SA_-gFR+Bx^UgU9m<8LgiJ0zJVI(Tkak5$>%83$|M z#Y%&!?Yllvo{6*hDIj{Rrc6ruN%^OLwjiXhriUi!2|pcnT@SAD56XZUxA=ae+b{@= zbxlp#y5s0KJ&tvow=s~7nxXyuPRB!Sn8n2;l0PxHq+pMzM5BSA10*0IZy=8wtPer@ zh75E4WH-&j;}`@6qBAAV3c;GlM2-S#b*n^9KEL)#|cWJ{=7W{w89EhKCL7__lxd7L(_&At|OU zA|!OkoFBEK8gA4Sfu%(kX|g#tchc#NF}vbc)0MQ=E9v)or7PJA&?#bW`a5fXI05C4 z#CXDwm8TUuA6kvo%iYMG$*Z>JFH=JJrA8AHXc>@Au(F3No0b%?hF(bVe;9h?1)u;B zfwud_#0cw35?7jv{i`jiYckr~wP|TS7o=`MDgq>m-W8ZsAh}@}p7iC-b0B5{{=g0~ z${jmydd1dpPtFLep}atM*gg+pXM>64NS~~deBw?CUjg0!Z~Zav$L?ECh5keIIfw)h zT|fBXKq7`S_mH$?#{%}qPB%4B!YCXQ^ne;3>}irv;_#3x^^AgsK;M8k1VLhfI%7^2 zu5{L7-&?%uNwcBCTaGxb>2S0NR5oZkPYQ&Hl0f1#8-mq|ITNVf6S5VvY9t*|a+d59 zfrzq53?R4GG|YhDf*qYGng|QR1c-u(Bpec6DaGO} zVA(ZlpqP+Jc(;0|YJgl_go-lIUEHF<*#iu)EDUcO#^6WMuqDHU9I<|`93=q9{d*)B z1wskafAa%>^%*xk8t};K4vt8G&Dr1kt{~K=R>H1O8`cx(SYtX zsUjK~#Wva7LJ1*i=(Ie>uUb3i^R~?TjiPXI8v_)Z8kJRhJl5AbYp#nmSy8Scl&2Z1 zc-9_`oh}eLnVNZ`29>C*K9Z8xgQQyvJf>J4YtabP)g& zW}ByI3!tYY%7l<{FeMOBuDzopT}wJBLttQyNzOH`F^X8!Bb*zoYpI*gb$QF&=|~+# zoo?!|Yh7|CYAA;p)Ev;uAc05|qa7R(dL2n17otT3JN4}V?__~MkV~R6NGHt69~-+8 zP&7B1VkJU~i+MLM1UnG{0>oLI;v=rjt=?zUB)6g*=Zd zWKi;GIBGn;Wz3o^6$(Wp2!X3^1aCu`2|I%5xGn5n`(>_8q9xrXb)&+OY3|hL{+@1Pl@qNQ=-sByT1&M4p3yJ>(wrG-RMd zL574JAVCi3kc2`H0n@-tiUdGSfwt`(BwGZR$%NCWt-{i>3q{FN@JTTuq0CLnWwtI+ zx+Jm|6o-dp0TL`qCFEdO!(j+&wJ8Uni9)VGpn-!7@C9 z#{iDm63!eF3&=>Oe<408vPlY1aWWK#6E1|Q@Vp3QCEb&QV+4VLf#5_NVvkVnfe85g zXJER5i1J`;+5*~yhzOCyp#t#S*n$?DhTuVLwKW|=AW#HsKu!rI_5!a^o-}=)?gQ-` zrS!$#ky9AP=*H!{L@{?lHx1hd76>UFH#CEgfp*Iifdjxh&yIZGBIc}cxN+caM&NMc z&aXt`y%EQ_O&16v&g~KlP;}t|K@kXts(|xUrN}}VF@y|h6JSyXXju32ub0K$w|{R& z@OA0lueq>KOdSA>m-L3cJf2P@^=}@-$s(bhtWZ*w2jDgylHg9voG#|zJGTzW-YMVG z=jmynGrMaq6Np+|X#6-24>0_}51`m-v-1dVotbZg$VTy;;N8_j)5fjR2XQnbVKdpN zAdLq2r#=Da>7W&X-N<)x&mir*XNw+tZr#GSV!bY02CV%@P##BAIgUfYdki3LmYNE1 z?WCHble~k&xb47kZsE_mTGtKz4{%Ld4ChU9eA|gQ1^Ym&DpI1UhmpL=|4dUhbD@-f z(e)i3$6>v~QjWruF{@RLO4!>f_HnD@RH3_+(gMAZ0}Tbpoi$}Omn4CV?q?EaRwB-6uCD2kam85p@Hp51kr7Fb59 zks4WMPED&WaZpEgE?AF;3Q|QwWOr^RC(Oki8@=H0tq%+Y+Zfz-knR;z_PC*-(CZ}- zB(hEqUY>bszg3+!kk%MLkH8T!8xCp}Rr|n5=KAxn*X$0JN9*zdxUzF)-pmBO8>KZI z0YKOtq9+}jK53E7379i5WEmDf2@sKzL?k$~T&N*#s#hCrXW<3ko$BnxMDz}9SL-|@ zYG?;Md4eHkNEIAO>%avA>KRqTFd@}KJjlBIt?ln- zLkbD?Br=v*Y1j$+Q$TJXM!dkJi>WdoAHJH=1Hl>)RMi>U&k^$38t-X_vrojoHt~#o zd+_{C@{i$wX<&Cb%^CU}(I7XLZE7hd^?6s~lOtTg`l-`)Al`SDRz zG@(JSi>*}6mDD(*zOeEQ7;6gJgL*0Bn<5<9cq{G%@ILxz3TRTPDyU8#ScIq`nGbv` zlNAk845WUnArFDWF%-j0gfxo#&)e6WII49ag#78@6Qiain7-Zf2gQ#;)T9Iy(@Mb% zNkB_bNf47kO85G}$;6n5_Mfyj8Y-GfX#w|wd4qC@h?$6ps+fqVMrfLfX%LV-pY{x# z2qI>v7KiOgFho!jK?PJyF%?l&?o7Cen4*Ykf%iSww#D37MdXnj%7c zv4PP*9<~(F6ob*;ddTahVhB8d_C8qq&u+y-Q7scp(GN0W7$GRBy}Ti%q$w7J3I-%8 zp$H<9CM2k#n2M^2Dw>KIiYj1A2redh5 zsEKK&W?+IUhNVgtA!?!tmZGAliHIUnSz)A_3Ryy$CLoFks0g5vNJ@gDl13;(prA?! zYAC3JC8R1Kh`=Nu!IB^n5(I*NbO#G*uQq1w!kiw*?@?%~GiLJ>Zr-15MP@QGAObMo<8W55ur3euFdGGM@WnRavMfit0K#D>LnrHx&c&G#IxF~T0+7PA+ zf|7;yWTS~9WhjbTXoy)ULS~?%Y56dNN%m*S9wEd!fS{yk^#PpC5lMK00_+h*6^PIQ z=$VBEq8<@oC{&?TVo5IqzuWeFhbPP+fL%Hko@>iA=c9CY^-sV$`S3Q3>;Z`r_n!{# z>iH2567c{41b4`k9DphDSwMfZA+;hG))-Ufhnl}ADz>5uNv(bKzs2n#mjvBJ{V}$n zTp}X=G=i&0fG!rzxNwfDz!Y4L$7Rv#F>!Kt-dt2e!~uaB5CEh_LX}XtA|ps6ASL}M zD7b`G$d3MGj;PffAxs5=3Mi z1TcpHIZ_o~W_#O1VUk8Dn+W*5qcsWlodQpxj=jvH{<;Vbq}UaN0=px zd&)8;eig^4ekw@Mc;^8^C?pwTx;S+YkIq-52sh^~BlJz4e+d9n46t^lb~(oR^FfBQ zdiHgLXhOXTGLuceW$Gco<%)5CK9@iSv+n?i6d@r<6e2-W5_mR7ilm?^4tS~Jy$OH@ zW*ffA000e-v0|42Zoz{NS20F0=3p!dE5V4Mx;;I*GcaS?niVN%0 z_?LJ2AW=);EjF#NF6=PeIG}v)IFLDrcVWp_j)cfdr6A|`PkeFZxRcA#UTuRjYAo*X zAk;u95#iHFZPJ56y;kbk2c3kib>ELf><08O?Z6MS(bx_94b*sVxHFZ0?)A>P=<8F) z(A4Pon)%urkCsr~tnMJroFjOtS$@x3>t=z@*#J7@vU1;4Lw4h@|5tN#>N)%aS-0Oo8+Dkh~wpVPy#b;TcM->?4G-$1ITKdLj@=7?A?a9u8(DWQ>kt z&m#*NZkgv`2t-GlHYgv7igZTaVwH{R_V?p{DTg{+tqYL)r+!*jQ)g0rPaghPK3kZhDr z6eS&UJh|XbX2Rol8&WU-bXM+nh$=L^PL{F~l%+G3$EJ1>B0|`(^Yt;zuv;v7qJ>1< zM_Z;IY00aBf)6%-TAY^h#zb7$-VvkSE@uH=*F$#`3 zU3VZuQ4%-7H^W(uOl{?VW85;f(i$cDDL)nHZ)*aEBW|;l?QKX!Ry!F{je>&u7TU!P zF*ezf8m#*D&%biONmfrgumN_piODcWmBzp(0FrsRy`kF~F(zrEd4(IF@ep)F%bR^w zutk$fP~?ksLpr}^C_Y(O|6W9K-0;fx3=|UH-(6SmDV%tFS)LnL3@48+NaI-5*%ZE4 zqmtU<66by~C{s2xh#+9gSX8^Vt#5Z?>dlMHl6H1LN+Hu@6d&-aak*0GbejS6RZ{7- zs8Ri{P1Gr~(kKNhoI|~Rpl&-unBp8x4FFSP@VagS;WgyvB#9v*BVgAjurOQmcW1ch zQ=Id%T8xhf^b~kvoGxYby_9~5m^)C)4<0_3p+;H^I`NLO0ZO0(p0(W|3Tu!BFhtBi z6tO@Q`ap`Oy#!S_@I_N>fGKhD0ToW{5me~Z0ZkAhsdxygc4z{f8UUq)0ZTV%pl{4T zHfTiyk|Fe{42JXQ&>|-U0ZqgrnCc^c7Q|M(3U=gcwdbjHJ&$a0kU4=HqN8_awpWMm1b zqe&u2k`gi}VhjB>#uI{h=ePwa{^7SjA&?LKxYgfezP>lAd0%z(`~1k^E0Be$Ng~Ly zITH!n;Mp;6N~$;Na=yUL0xFw+M%&Tt1W0R86#)3^BbHBi02j^DxB__xyXKHUG|*HO zP0jEksp|Gho9>sMe52Ga-Uj);Ovb=msjNAV*c~zXawkw2Ad01);LiVp2H>)6_?+)x zE3L!$LB}QXsow?eGO=R;i(*T67OCN3QExO{LTsrbPlnEY>N%v(4Do5I-8h+-XRLJkF?LB197B*z=4j;rDjiUD!4ChmHr`+Vigfwgl4x`1T^<(zTH9jrk=nhIw= zw3DbE54`J;nc)FQdr%}5jCBYCnCK2t0HsO|ae#l77jlqb1DD?pxZEF?c2?#R zAtFdJG*OE1D8SpX;G3=2{YZp>5<7wG4j>c{^kEVHozkCVESQoXJs2o;3Ol|%I$Qkm zw!wlDNSwf2JfE(y=|Y#v?F22kw|9MJ(GE`|71XVut6J?&_zyrrKe~jeK^bAuhSpf$drYI@{>@eH$F!BG?ANdN`1(P%A zN$F$36-^$heT^35QiXH7`4rV#M@)Of2A~}RaXO7MKs1)WhCK z#XSRTIVKe(b6WekKaBokU~w4JLfyI7;cC(|GB$<>e(?Z>rc^Yihv|X`iVypF;$!G% zsU!sVf>xp@geBFU-tVVC0Umtw&&>3vmI3+DLzYv$7{*939vndHzOI$h3y~q%XN28; z{MDNGs5?VKfGOcy2)-ej#j21^vI7u+$O4i8q#z1*@$LwEB@T=Vcd{5lQG_@lD17(Q z0-g+g>_q8L5c%HDzHIQQdSJe0d*jM&t)ny~Nv1Y6XN>&dV9BHiB_bAwJRQcCPwaF= z)}OJ?IkC_6Wc@SfWE@RqON_*>Wb*G%ZLZPM-;9;4TUTVLk{RRyNj|5ylmomgJy`C8 zsXdfwJk`U|os5)1Rnz?Lujv(OOYF{S0U;t1NhE+t{5+4;f0EjNg=%8^R-g*1jU^<8 z>8LXt>l{`NSpt7*Hc2ublpKv_WQURhq~+zfkTPZ3WaVk!b=!WC{@rIwRYZ!$ar~ba@)nB=UU9 z?D)s~f4*J5J~QacXl2rm$v^BU4A;ArqfB4K(4bp#>4wL_B0hKMGw^+V`rgBq)67H1 zXV95^SWh36%kTtFPoLn7an}@by5mlj+@nu}%SgjLcV~|7dDYi{3Zw8hZky+8mZNHs zBNT0=)5MqasdF6O8OICqHSj8hkv#{n%{IgFyc6*P5qt!+D8~jBksGI12FXjJW++wnuesp>d zJV3#p&v~yLVtxycu&>Q?wkvJU_``-fH3shJ`0Byqzxi+?dQdjCHECxn^|;Iv!ZQRQ z6ws)=;Fp)jtW+&Fz`!drYD-n%Im%X7q{a>y9=CpfCyc`l&-4ChVCR5C=o~UML6RN# z)U%H+?NQ(x`9uxjgrtRvp!^JCW(2^#E%Y=TeLe+t>OX_?pEcm{kbXCpBjMq@knWm# zT)GncA~A=U9J4B(v5LDKaU{Dwu`_U9$o!Hx=c=%^gnxGX!C?vNf%hO@octwQj`LLP0|$=1p^%C6OJ-xYzEj2XC`rz8#vj< z&NgubK@d_WpjF;vl1-C)o}MATG~5ot3mQArR*xz0SrtmY$Hr)+C;h){*+13E3DDKP1@16_r*PUnCA>?-FD%-(VALl^v>G930SO}Z-31a`z-X$8 zc!VcBiY;hwEI<^vfGHpfLcV!b=spy1RRVKk#FdcLpIF1+ylq48nkRW7tSaSk!$1XP%=5fwp&m;j%h^lrI z*a828^q&*L*FY52-dm_Dm%s`Wf=DW$$e|cQ ziW*iSp=l_hfu@Q|X=);; zrdUo#X;M{Hn$=ba!W$g*jL^g7U*z`2GB7>3e}2q8))HwEYzNZ~Hu~>;+hTri6~rf1+@Y1#6r7I_Fy0h1 zrikllEf|R|qD5gwB3K11|qV>*1bZ0`nV_3*y4N`lL0!5d|H6K~YDvw0>H7ayci7yX;2njbQSp(-evUgu^Y$Xb4G&{9==lq@t+Q6(h; zYZXl0A-6Ljw8B!}6kVjJ%32BtwC>1m*9yL)Vu*q&4v^Zankh;qvKTf)FNcOZdsD)E zv@KEY2<&)C4+n!(crk30L=_X^Lt=>i!I0226!@we5z-m$%tWTNC0f)oaR@ci5sylQ z_{ebHg$x*jvKFj{G|+b;A=&;H$#~s&c%BqEb0L#tHt%?c(R7DX)6y7DSRV+S;~WPD z`v6lL$^*4V2WXE3ZUbVxF#9K@H&NSbh;21p=s_WQc*tC?*5VnE!`Tay*GO(;BULo~ zavltGry<;gbBLHW2Y1}7zBHR;J0ZKc;u;H~JCNa_ITwKrklc(RlVuNJhT}n8LL@ft z*rLd7F*ymvRzfk|aS1sFLhwgqJ9ao<2O4|`Y!Zl2qzObRLOJL6o8g`R!y**R?xC3a z2H-V+y-~B$K}~;L{}_qt1LYowkLMU|_#Nphn}Nmo+Owv_|BfWE1wN3G^~^RBy}JFB*E5rZ@be*?1us7g{4*~U>zWo2bf=dG(SRj?CCY79k87jk#5 zIPe+9o5{`YhB=2hg}c-lz&C3R`(sXP%WB+3o++@Lv`MSP<3pRgEv@2Vn3g({jqHT1 zLXf2;DC_K7Lg$40#Xj}BKP>IrFtI~0ec@{k@*sZsz3+ekZBhf*CRdMK;CB#Opv)!9@H% zNw?|o&hB^g-0=Q7rw^MC!fL*7d{Ubs)k;qX)@4;mlv8mF3tqZ%OC`eOh0Cv&C3cHa zB`h6+l#6nr$Vdcrwe3dit2wiM)~dGwadLZGypda90`MT%3&Clj9V+TzNU{Whsg!{x z#NI>EmRoy@LYEzfcoZ{-n}}-5{D`gP3fCvh_1K8(--2!PAw1AOxUthxITMNw}F56E!Qu|~0MKqV@TPAoGQaJuxz5TnLP2>9Jz3CN0@HpU6kAY11)zPw-{ddk2+#>1BR?KJKcc=D zDogos3)@hkN?vL278QV{0k?FNTZ{8}5fn9yS2~q8w=I#7~1y z^$pQyf8^jeNL=fY0})6ffDp(+ArPqwAySY^h*c^wYGN7O{e(9{35TrKDd>~F+|1MQR=M3 zUyhut*&WS!?iTjf=L9S$gu@rp)y!3uUaSJ7kD))aM)6yiHA6)WR69ki+a58%!xPB~ zO_RoLABqsTgh|i@s@{?jDJZJof=E##y-8I+P0FANAP18WE)+MD@CS2N$BraTyLlQ3 z*gZ|2VNq>hr6tt1l2t=ifPJjts^xCPZ_iMH&E9mq*^~qkqWQ=uVD;E|UwR|nS>k?e zQy9^*LP$#5U2%TK1V|6bT;dfhn{_T&!VF{(hu=Q8fq-3~pzDf!B?4f>i~Sv+Itm#i zxQ8(sMVF<^wc8$QdUA2!+z>K-6R>iTOeG9NMs@$PR%>@)53%b*vp_b7S&*Qp9_0wD z1R{h&3S%G&K|+eCKn{0C8uPl(qIIGsi%E%^Pux06eDl9Kz4eDa^a17_-@KeZ`6N-K zEJ*-o3K%OuC9WLWj6Z>~q1>G31C;IcSl<9KM1Cq8bu55LvLup7Nu++8#hvDT*~gE_ z_OTaU4|l`0-5tlhI^t8|_kl)-EE_+QX1QJl>1u<~kS!9qVu)2-p>PqdO8%`17H527 zy2)jBS}U?pz7yk-`5+VBAIsgl=n@oA65kOIW>f`K)NtfQ5hX-YK@ZwOXeok<3FzvU zsf;fODWEANBRr{+2}3|i9BL|xnJCCe90ndxO+{A#;ZYG$R~3;mnHd9`CL)qB1`Yrm z$yCf$Ckq>M7TnyfZLV{hbDM4)=MBc^F~giUDV>du91de};myMs#lkrAoD9*79A|86 zn~8AYxz1K1+&JmwtyQ_lmd51V+~(G~u^csXZO$CWIm#f%EyB^Fu5DH}qm`&97}XfK z<_*kngB-cYxy`u|u3Y8v=hl8luG8Fi9On)ZG(>5{^OZO`Iu3Ijxg9xZG=Hst3z)rWA_<2; z%esRIh=52d;`>ii7@^$7kq8AOK$&6*$VAhb zCBUT)Xenuu^D{3HnFK{KatySIhd-qh6AAMUK!PO2qg8x;_;VsZn_69* zUuXBsn`U){uM==@vSr-N4>ksN0;VWQC=gXu^^O{n z8$@02@C8u5psF9ZHrkJ-e9xU_7vGsK()RsSz73A24AaM-jzz_vZ0I~}O zGzuU+sR9caNKN>@;r z2>^r`q#+6*iY5wB(3y0i0DrfDo>%(*E9C9+?zet{uQY19QTnDn5-xN*(hGeQ>dMb@ zMCHfg7+sI|5CiaW=%&ii&AoajJP=@xIaeG(8&dmaeQ zJ{gbvdeKMQAO)LmL+NF?Br_02HRKRhhGd{Pf_>iywXe33_pjBYzl0aUC3}I#5N@Wa-;W--q7NhP(l%lR4EZAUJ?}s!(v*jVlkCT`OC{7eP zS#X>jJ3b6EI0P0*OZ_{jr0h;py`P>0AnW#jJ_CJ+)cC)mJH6+?Vunv*cNn)lAdh&7 z0E2ln^Ys+z-{V_12(mooTfRJ}|FNMtDk9D)tK!^Zd zQb05VSjU`Tf-UC%%XRk4SlaG?Max!r02b9h7E)x0T!W`M_faTQ>1I#e} zJPtG60L2a=$`93>$=men=3rI$VjCtBL}2NSpyLpz?RX3W@#$E!hZXzupxd zIXr~EP*F-%LSb402@;eUP?%K%#S#!MCl79E z!PJeJ389mWh1)qrMZ17p=N8`u_NK_HS1La0DRPBGVY z$4pK4Pgmae)>kXD&ZoS#S1sDQrI$o_IH0TjlxD6ooT_`0_a^K;Q!{o40Bu z@^eb25?GaYIg6ut&TBI`pyZ)A$a%u5l1a^^vz+4#8FKSkr+U0*mA-2?If#rK8`z&Anz00%yFQ~)oK1u{?t zA_JePMW8CCjJ1hrLkPnN$gGfoBPS>2+!QNCu|+N+#vxi1Lgi|YN+Bq+g9DlnO-?sj;#*5CGFNI=1%ky{^(%e)l*?4suf``DrlyE31PzjEqV?0R+TD-6X z_nLtU$VVF_BtPH=AAyabL9EP?Jv-T0(hxbS_jh8TAYgf+1W2%Un)`l|n3)y&;$g&y zqR2WubO{(L-|=5O$TDbP@=Rr3dWOEa$6gL_aKG)gfXENX7_@_I3f>`;eSOU$S~sc& z!2$yXh!BD2{j&txh~e+|g`d609&xVrLOrq8+`GWa@)d*V{~`z_S7BBhaWf-`e+KNk zZmW+k?5?L*L_0>1m3<$>ZFvRsjqjlO$7O_B$HGQH!eTU`LWFl1P6D7@gOMV2-65k? zEd`9xpTztKCkfJaI(?nZ+FbFc$?E=g*Sz73iAjeNNdqK6q-3}*<iD8TbpUyR+5y z_u#$CCr_3iw!R{zhV0N&N$|JHf^C2vED>BaAD{+8MJgzvB9ay&ifN)~AtIQisp5!xkX&Ar=X*14g^K7PNRl#W zSkg!mEf`WHizW+v8>cFtfK=Lu`U<^DC(84`@e{&C@ewPcWsx<2hj0jao-mIjst91% z8&X#8%Y0P0CEUZUJHvzE8B#CZX zRkL;@XhgU{KVl`qCAf{aA$Pnm0u@j~iH#_LMICln+62z==+2(udX^wo%>rB)iJ!0m z=%RdK1CmuZ@5rERgcGF~z6Cp=6Zi<3p%bc6eTpI00-X?!-=7t4#7y9g`mcbi?>FEH zpDyt=w+7s_h8SU^YV#nN0fB)DB!>am-_%R0R7Mx6X96-Zl)OJ8SXeuTM($n#_5ct- z!*J#AOeD!7LP(NH;u^r{#CXBkwQsK*c9KaX6RHT4TnxRAg``X|93>19 z0PxW>!hS9M^&f&`>@fe~6*?-e9U{}M)ESo!oMs^aeo;p706a6F@OHjn|Dy)__#9jE z=tl>V1-(E^U>Obi$}gu9$xXqJj0$3vvjSNt(6rWKuY=#O!9h?=^hWQe866zL~>6R0oW&O0G(it zC!6d}74D?Z23*XDQWgCR2B5AsBnnKlqzMK^;wzC3x@=lg&ZL)_VC~cP248S=G5qylZrg+c8gJ7BOB{N5RAEd>+V$& z03(ltse&E=GnycLlgvM|DXv7#9FnKLJ~3>jgaOl>3v?k^+Iht?IUHmDjt_Y+Oukas zM9-gbQRh$6U21fjv_e6VxCj&=ZlPZ&_+e|rJMdjd2gv|QIyYz$c(Z$?_yYG%mVyXG zP}}er$cfZ_T>OsP6{0NH8om~d(zV`7W6%(z$Uztc05s7_mQ%*W5_89hBcwN5aWpD` zG4zXq>_oc`$;tf9N8UDCUI2j+Df>9513>X44-r%*>|Aq{adZ+a3tWdao8>m5D*xYJ$wMxGBDcAEGzRvjxspz;f#=2?TxgAhNm=iHBke^R1Ikda7O z0tOZel3--RAEnc8f1HKV{FVB&$RQVdW&$j?`UcWETV&^=UgD{@zEhO0I&$Xb!|pWQ z9BaHi``-3^xHroLQ2^(6R~ z#vZ(O>yKqpWAH;>@IRz{=q+RF&m>uJxa-3PS+B|5ko%Y=txrxp4q{6dxB*tn6kN3m zz%ouS%!gBldK9~uv&SRNkBUOC{S{&8!dPvkx)RDrl7iR}-I3>^av}-758PKu=e}$Y zChBm_9C6nm!6Yb-;j|EgpEXuCO0;*$2?zv)C{@#s{iw5pr{JxG$?Mla_=%?wrOL4F z{~+#MtdO>{V0iR9NhvO-V9`(0G&Jwtnlq>#?mF zzKh5a436@t`IDCHgl|y!BBTs+9ZjG^+zx5+uV~t_zBh#I*6!jwLHhy`p%ASgIxz@I zTaJi!yix!P$6vZ6hx|tt1O?4wtVKUSTME@^N6sgu*ovP|sjMZ)4;TNU_-Z5(g24g- z1cMXjpKA~|3D8NFQjj&QX$cdR%(QGvMinm5X%4tXaKI+^b4sU_Y6bfb*%-nE3Ifp( zfHVM<8V=C7>mGONRm*OuCHU-1(R?@W6bSwIflLum5i}Ic)f7ck&{RQGQ883R5fLRY zK>XzWdG3Cs^ar7eN-Fd}8^fN52AZl7DFZu-YD<&nnoemG;1|u;*zvuQYsf*{o;l7w zkz8dq!J(=lazrLR%Xi9j*3z6L`emdZVOOH3#u9 z3$qdCVe!Z`qa=kXCLrK65xY~3Dpxm48e_O0Hdnt}Y#~rF5?FD?mS^zc9tD`LCjR@DhN3<&x;s6=? zfD+23(*6?g?@gDZy;%|MdB?WZ1=1bxqu@~gS4IJWsfb82DIz!E;rtO(Gs7;?!Fz)= zN4Egjc4xA$^$Y_-0i7ZUMK_}e{2~okwX_G2UphbphkWd__9#S|1x6AajR~ zep7H==PJT60`}}vHcJGtzUWXxR2EN`J8dKDpX>*%s1M+va%M4j42WY&7ciP?g0LdF z1qas78qPP$ENYDOB{2R#3Uu`J+`fXq0>1VL0eX+l)_3Jx5{rHa+k;c59%O`LA_z)I ziHf3O_=E78`R%f28H`b+(Y1mwXGCHn2CdQ1)+($@2R8gp0RQpvkB@(n#5S2ENrUC@ z*65}Vm%|muwNI`uh9}ma=JnH<%t02^#8{0SQ(}%0j59>*RkJyx{%UkD!~S#~J6K~d z5m@L9x~&_c2n76lGbN0#Hm%~ap`wHkve=b_jiDAc$>cWS4_hmP|QX)*mbh(gEm`L9rK(0ji5 zL$dL&4NqlyDbT1hwiV?N1J~i2QF7#R*WDbb4oGpBCHVf_?*Q}U!b zEVTtFB>-PLp$nxYNK}G}erRyY4f!8wtq`^ZT6?USx`Wl`!xFN7L=cfwZ|G~t3Ty&D zJV?SxB0@BTp+mdsreJvZ{%WVT$3BsuIWYrPckSw--S0b2cMA`Z-xVw``X4%gKuSmu1Oh=sK~qUh1vE57RMSmUMKo1GO-y|Sii#SjW*G_sBC%MgMH|<6 zU?IX`+$drTl|YFQgknIH22xDklT~RCt_w2V<^G13;rg&?62}Y3atHy~mCBBs3vXD` zVP^$$Uux(NOGGL|`}1ASPMGJxdE>@Z4$$?gFU{lckhrFR(ZnPf0FWd}G?k5_bOvc_ z%RM7$!Bl+R5RZ-`i{V-Ur<=$BvE;{OLjI6Of-zR}=2cz6!8;#AvNZcpgF+-35E3U3 z8^%z^0w4D@iU6UGxN6T|93c|~FpqV4m(&D#7XPmF|-%K%Akp1NM?oUV^7E1=@9m}3txU}HSWH`I;m z7{r9pSkYbNBa7JN1IN_yjQWC+fbjPZ>!I%}hvK_6!f{BQNjj{zL?exYU@4h}qrl|q z7jzH@j#XQv{6U@{wIpr|yAe{WY0rQ-;FAT2;w`8psbnkhrn)MUBb|cukW}oThn9ku zhl$Fdd56VJh0mY@aK`7&(*j}%6jmbvVvz(XsETN%qza^n1|(I43<)rZlo^48GFJIy zljWrH0(8uXK~PixIs)(DK|kgJXm2-#1PR0)mazR=pxcVx@J3M5k|`tfJx)#lf)QzA z>62jq5lBQq=@6&cOBj+LmFR_G5ZWN7to8g^`uZJp;Fp&OTNy36+g2gg9Qb@dGr8hy z?kcFIC=y`D)h@Vi5Z!j-K2VRwEqldp&$-ah_tp49ycf3M9~BHFI090gZ!n1Vz22+g Z95VS|uN9UT;Di6g+>uTcBpLiIbU-6#j_m*d literal 0 HcmV?d00001