From 155818dd1f34b2a77c2b874bd2467d8cfff5a81b Mon Sep 17 00:00:00 2001
From: mizhexiaoxiao <1157861072@qq.com>
Date: Wed, 31 Jul 2024 14:58:26 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=A1=E8=AE=A1=E6=97=A5=E5=BF=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 15 +-
app/api/v1/__init__.py | 2 +
app/api/v1/apis/apis.py | 2 -
app/api/v1/auditlog/__init__.py | 8 +
app/api/v1/auditlog/auditlog.py | 39 ++++
app/controllers/api.py | 7 +-
app/core/init_app.py | 21 ++-
app/core/middlewares.py | 62 +++++++
app/models/admin.py | 11 ++
deploy/sample-picture/1.jpg | Bin 0 -> 162646 bytes
deploy/sample-picture/2.jpg | Bin 0 -> 104510 bytes
deploy/sample-picture/3.jpg | Bin 0 -> 247552 bytes
web/src/api/index.js | 2 +
web/src/views/system/auditlog/index.vue | 231 ++++++++++++++++++++++++
14 files changed, 393 insertions(+), 7 deletions(-)
create mode 100644 app/api/v1/auditlog/__init__.py
create mode 100644 app/api/v1/auditlog/auditlog.py
create mode 100644 deploy/sample-picture/1.jpg
create mode 100644 deploy/sample-picture/2.jpg
create mode 100644 deploy/sample-picture/3.jpg
create mode 100644 web/src/views/system/auditlog/index.vue
diff --git a/README.md b/README.md
index 0ec6924..0e2c186 100644
--- a/README.md
+++ b/README.md
@@ -190,7 +190,20 @@ pnpm dev
你可以在群里提出任何疑问,我会尽快回复答疑。
-
+
+
+## 打赏
+如果项目有帮助到你,可以请作者喝杯咖啡~
+
+
+
+
### Visitors Count
diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py
index 76a14be..1813810 100644
--- a/app/api/v1/__init__.py
+++ b/app/api/v1/__init__.py
@@ -8,6 +8,7 @@ from .depts import depts_router
from .menus import menus_router
from .roles import roles_router
from .users import users_router
+from .auditlog import auditlog_router
v1_router = APIRouter()
@@ -17,3 +18,4 @@ v1_router.include_router(roles_router, prefix="/role", dependencies=[DependPermi
v1_router.include_router(menus_router, prefix="/menu", dependencies=[DependPermisson])
v1_router.include_router(apis_router, prefix="/api", dependencies=[DependPermisson])
v1_router.include_router(depts_router, prefix="/dept", dependencies=[DependPermisson])
+v1_router.include_router(auditlog_router, prefix="/auditlog", dependencies=[DependPermisson])
diff --git a/app/api/v1/apis/apis.py b/app/api/v1/apis/apis.py
index 67f9c88..9cdca28 100644
--- a/app/api/v1/apis/apis.py
+++ b/app/api/v1/apis/apis.py
@@ -1,9 +1,7 @@
from fastapi import APIRouter, Query
-
from tortoise.expressions import Q
from app.controllers.api import api_controller
-
from app.schemas import Success, SuccessExtra
from app.schemas.apis import *
diff --git a/app/api/v1/auditlog/__init__.py b/app/api/v1/auditlog/__init__.py
new file mode 100644
index 0000000..d90f07c
--- /dev/null
+++ b/app/api/v1/auditlog/__init__.py
@@ -0,0 +1,8 @@
+from fastapi import APIRouter
+
+from .auditlog import router
+
+auditlog_router = APIRouter()
+auditlog_router.include_router(router, tags=["审计日志模块"])
+
+__all__ = ["auditlog_router"]
diff --git a/app/api/v1/auditlog/auditlog.py b/app/api/v1/auditlog/auditlog.py
new file mode 100644
index 0000000..7a43b6f
--- /dev/null
+++ b/app/api/v1/auditlog/auditlog.py
@@ -0,0 +1,39 @@
+from fastapi import APIRouter, Query
+from tortoise.expressions import Q
+from app.models.admin import AuditLog
+
+from app.schemas import SuccessExtra
+from app.schemas.apis import *
+from app.core.dependency import DependPermisson
+
+router = APIRouter()
+
+@router.get('/list', summary="查看操作日志", dependencies=[DependPermisson])
+async def get_audit_log_list(
+ page: int = Query(1, description="页码"),
+ page_size: int = Query(10, description="每页数量"),
+ username: str = Query("", description="操作人名称"),
+ module: str = Query("", description="功能模块"),
+ summary: str = Query("", description="接口描述"),
+ start_time: str = Query("", description="开始时间"),
+ end_time: str = Query("", description="结束时间"),
+):
+
+ q = Q()
+ if username:
+ q &= Q(username__icontains=username)
+ if module:
+ q &= Q(module__icontains=module)
+ if summary:
+ q &= Q(summary__icontains=summary)
+ if start_time and end_time:
+ q &= Q(created_at__range=[start_time, end_time])
+ elif start_time:
+ q &= Q(created_at__gte=start_time)
+ elif end_time:
+ q &= Q(created_at__lte=end_time)
+
+ audit_log_objs = await AuditLog.filter(q).offset((page - 1) * page_size).limit(page_size).order_by("-created_at")
+ total = await AuditLog.filter(q).count()
+ data = [await audit_log.to_dict() for audit_log in audit_log_objs]
+ return SuccessExtra(data=data, total=total, page=page, page_size=page_size)
diff --git a/app/controllers/api.py b/app/controllers/api.py
index 86cce96..41469e9 100644
--- a/app/controllers/api.py
+++ b/app/controllers/api.py
@@ -1,15 +1,15 @@
+from fastapi.routing import APIRoute
+
from app.core.crud import CRUDBase
+from app.log import logger
from app.models.admin import Api
from app.schemas.apis import ApiCreate, ApiUpdate
-from fastapi.routing import APIRoute
-from app.log import logger
class ApiController(CRUDBase[Api, ApiCreate, ApiUpdate]):
def __init__(self):
super().__init__(model=Api)
-
async def refresh_api(self):
from app import app
@@ -40,4 +40,5 @@ class ApiController(CRUDBase[Api, ApiCreate, ApiUpdate]):
logger.debug(f"API Created {method} {path}")
await Api.create(**dict(method=method, path=path, summary=summary, tags=tags))
+
api_controller = ApiController()
diff --git a/app/core/init_app.py b/app/core/init_app.py
index 1c1e5f8..ef6342c 100644
--- a/app/core/init_app.py
+++ b/app/core/init_app.py
@@ -20,7 +20,7 @@ from app.models.admin import Menu
from app.schemas.menus import MenuType
from app.settings.config import settings
-from .middlewares import BackGroundTaskMiddleware
+from .middlewares import BackGroundTaskMiddleware, HttpAuditLogMiddleware
def make_middlewares():
@@ -33,6 +33,14 @@ def make_middlewares():
allow_headers=settings.CORS_ALLOW_HEADERS,
),
Middleware(BackGroundTaskMiddleware),
+ Middleware(
+ HttpAuditLogMiddleware,
+ methods=["GET", "POST", "PUT", "DELETE"],
+ exclude_paths=[
+ "/docs",
+ "/openapi.json",
+ ],
+ ),
]
return middleware
@@ -134,6 +142,17 @@ async def init_menus():
component="/system/dept",
keepalive=False,
),
+ Menu(
+ menu_type=MenuType.MENU,
+ name="审计日志",
+ path="auditlog",
+ order=6,
+ parent_id=parent_menu.id,
+ icon="ph:clipboard-text-bold",
+ is_hidden=False,
+ component="/system/auditlog",
+ keepalive=False,
+ )
]
await Menu.bulk_create(children_menu)
parent_menu = await Menu.create(
diff --git a/app/core/middlewares.py b/app/core/middlewares.py
index a6456b5..db7396e 100644
--- a/app/core/middlewares.py
+++ b/app/core/middlewares.py
@@ -1,6 +1,16 @@
+import re
+from datetime import datetime
+
+from fastapi import FastAPI
+from fastapi.responses import Response
+from fastapi.routing import APIRoute
+from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.types import ASGIApp, Receive, Scope, Send
+from app.core.dependency import AuthControl
+from app.models.admin import AuditLog, User
+
from .bgtask import BgTasks
@@ -32,3 +42,55 @@ class BackGroundTaskMiddleware(SimpleBaseMiddleware):
async def after_request(self, request):
await BgTasks.execute_tasks()
+
+
+class HttpAuditLogMiddleware(BaseHTTPMiddleware):
+ def __init__(self, app, methods: list, exclude_paths: list):
+ super().__init__(app)
+ self.methods = methods
+ self.exclude_paths = exclude_paths
+
+ async def get_request_log(self, request: Request, response: Response) -> dict:
+ """
+ 根据request和response对象获取对应的日志记录数据
+ """
+ data: dict = {"path": request.url.path, "status": response.status_code, "method": request.method}
+ # 路由信息
+ app: FastAPI = request.app
+ for route in app.routes:
+ if (
+ isinstance(route, APIRoute)
+ and route.path_regex.match(request.url.path)
+ and request.method in route.methods
+ ):
+ data["module"] = ",".join(route.tags)
+ data["summary"] = route.summary
+ # 获取用户信息
+ token = request.headers.get("token")
+ user_obj = None
+ if token:
+ user_obj: User = await AuthControl.is_authed(token)
+ data["user_id"] = user_obj.id if user_obj else 0
+ data["username"] = user_obj.username if user_obj else ""
+ return data
+
+ async def before_request(self, request: Request):
+ pass
+
+ async def after_request(self, request: Request, response: Response, process_time: int):
+ if request.method in self.methods: # 请求方法为配置的记录方法
+ for path in self.exclude_paths:
+ if re.search(path, request.url.path, re.I) is not None:
+ return
+ data: dict = await self.get_request_log(request=request, response=response)
+ data["response_time"] = process_time # 响应时间
+ await AuditLog.create(**data)
+
+ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
+ start_time: datetime = datetime.now()
+ await self.before_request(request)
+ response = await call_next(request)
+ end_time: datetime = datetime.now()
+ process_time = int((end_time.timestamp() - start_time.timestamp()) * 1000)
+ await self.after_request(request, response, process_time)
+ return response
diff --git a/app/models/admin.py b/app/models/admin.py
index 81b5843..9ba602f 100644
--- a/app/models/admin.py
+++ b/app/models/admin.py
@@ -79,3 +79,14 @@ class DeptClosure(BaseModel, TimestampMixin):
ancestor = fields.IntField(description="父代")
descendant = fields.IntField(description="子代")
level = fields.IntField(default=0, description="深度")
+
+
+class AuditLog(BaseModel, TimestampMixin):
+ user_id = fields.IntField(description="用户ID", index=True)
+ username = fields.CharField(max_length=64, default="", description="用户名称", index=True)
+ module = fields.CharField(max_length=64, default="", description="功能模块", index=True)
+ summary = fields.CharField(max_length=128, default="", description="请求描述", index=True)
+ method = fields.CharField(max_length=10, default="", description="请求方法", index=True)
+ path = fields.CharField(max_length=255, default="", description="请求路径", index=True)
+ status = fields.IntField(default=-1, description="状态码", index=True)
+ response_time = fields.IntField(default=0, description="响应时间(单位ms)")
diff --git a/deploy/sample-picture/1.jpg b/deploy/sample-picture/1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..d8e4f0820d8688fe03e1cb4ea3e169ff2838f43a
GIT binary patch
literal 162646
zcmd?Rc|4Tw`!{?c$r1_KO+|Le7G;?>gqRQ$Vk#kI-@{Cllr?)vCfSmh3XyHB5!thE
zGuCXGu?{nH&2#nne!supeZTJKxu56t{Cl5VGsm2BE$4BZ$9XL8^MEnJAOaow!LH5#
zU}_4;0|0OcILLGqV1Xzm=m%gD0a*W`0l=C`^uK5aCfR?V!wdk-eE)rZv@3An-{(X3
z{HI<0_ve4Tv%LTSHmK*o8C6x4B&L7SOw266+yC^doH_HB>A&yzmia$-Jn-o)%YSIL
zLwN`Pea=4*`TN3{VU{(zd>M7!0%2fuO&?%_#?LHt$J_hC-ywN;`ukcKUOIl$#`ZYd
z!oRrtd(|CBKktjzuV4F1|L6TL{oie8=&vLIU_j~by;kYD30hHx%^u}-h=*fB%Kv@d
z|1HIF&&kgb`i2G_XB@qK{UMnuLiF_@fA7C^7)0~?LW6>6NHhSy%fINnzx3UI(N+JP
zbHm~?bk0wRKJIw`&OL};g=odQ|3kma|3G^@2>AQlzwPhnxtu($ETMOP=nw<00Iq-^
z;0-(g?f@acao{?j_Wy9c`akDi2E3rNLjYgsIv3zR;16AM5$gGS2PcR&0z3gn;548J
z(W<~1=urJDXXsj}?F{}~ev1Fy_jnorbny%ZqvpT+jPU^QSrY&_zWsNftQG+9asoj6
zH}5;XcmBEEU)^MOhO}#=5C9I_003Vvbd8;#vzqEZ-!pMt0sz)224hniQmGUGpub=+
zsP7mIdM*I4OaMTGCu1CV0;$K}#ti+kFf%i=9AJUk0oDV58|y*Vf7-!+H@1J;p}&p&
zzuG@nGczB6e%TMQ9{jKV|IY+t64GWJMgzcmh@~F-VLAyg^D?pUGBH}1-a)g*`p*EF
z{$UDwfyN09jO`FR2j>xhnTdsk`2Y(mG+yYN@V^V_059v&6Dk)E@|oXZJL$u(8j+N9
zNb=I>MgfaHl9bwA-$-_jV}e4$BB!KfWaZ@5HO^{kojZT|ioSv2RU>1|8&=jxNbHXH
zoSa?mySn-L2LuKMhdg}zBK*xIu$k|zt`52N z54NSxKtDJk5B{lVwTFZt{89nC@jPBqWY63nexKLu+oqSU9%l<}?=9ave(6Yo4UbHm z7%$}(nmH^mcvyms%pDH@vJR&VV(c-8>(8{2c|Z=)Sstd5Zs{*1T=d(5C|QuDxUU2~ gE}0s}xiSmb+B z{4{~%%tCCUx|7swfO~0|XB-J-cuPtC(+4uQv68k{v9|xiRkYJ2rtu{H4rl3IQrL?W z`Xs_!iviS^nubkM5s C>!RWp18csE(tm_k#W?jc6BSf!KtJY%Z z-_19 HQxA0BE2^Iy zb6b{QF&!?vOao`>*m~VGt|>P1AwBHqU*5CeE({ l}pD$17%LylcAZQg(1IY$p9LVd)P%P1K?npolX}E7H!el z0Xu6TD`mBK9 7`dt~r?;-K&W-Zo cP3Wcy04nm7C~bI)CRJ>%)s zJSr`cZKY`zkT;MTWMt?xMj!exCI%iN74WJ3blDy$GRX4sjGshH*h#QM@;s1Ae?*Kp z@r@++D)siaL5&LtLgj7r1*;tBtJl!j!WXeX0A%<671r#_b{l(&?Q`sHqUewzbcST< zwn+(XnNsh6pu8q}uh-N!=J(VS ?=| z8bT!%ae}|YW*GqYKM%q5MlgUhdn5x$k%JQlFeF6%6u bzYf^+Zxo05jb#AyNGk>~*Fon=a7r^zNs~Oj zBT0Ji<)yT>!X6+x#Ljid!9a$|%D_mbmxF|j%eq?*4z^$j%UD{{ir&F?2Jo9Z17zaa zfa|o;j)sEl6ax$qMP>kZ{_>X$X(wr^ha$6xfz~vFlDUkhy>egx0aRynr?p>QNn4`- zjbv&0cF){)$w-y#!CUCj=`Y#a3?K$lz+DDloJez@x)nMD<#+b2<6yltG}OdXb6Iks zq4F@wVfcoQ6sA!c^0nAJ$Wy`pBgE8Pko_FQHUqG1dvwO|-w#F^Q!IB1>Dje@rQ;M- z jJo&qbu~1wmpWx`=2mCqlDo tf;`Mt0GM=;57Ui^b;iuB^DBPnsmh3wUW0D`qw&wtNwS+GOUbKn2?-1% V#sk(jr}03OK8Y?Tw(L=}A@Lk8WwJwDi| zMc2D$9kqat3OfodRXcPOc!r`&L>yb0I<&dIt#q<`c>%GeWE#||eyDrgVpVo-3*d3v zs+p!p;a35rdoL&~QgV`@bT#e{13;zi!kgCFO$f@-B5V#9VV+wMiS!ok$b1&$gg9=%kvF2aPBqe{eoD?)DD @N*bS zE9S! Wt)7 1ZHxk+4gt!5PLXnrtw~Bs$U0V)O z#GGsPK^#}O?Pqjyv!=gpcjq=m<3rd(3YCbB%Dg3g2NN9ceg5tkZ0e`c+RI-v_>0|s zAN5$+kg#WgQCi>NQqb5gFMhgtZpQsm)Two&ybJ<*5!0xJC%i&2=}Cj#$6$Q;`}o6O zaYgRQM!Ij^Yihit`|LX71IM~JXycl3k>24ds8A!wXrx12V$xvb3qFt)GyQ9&71rUb z!6sKpuqXc0tQ540KthU#z(;oa$jtRv3IT~l_rZ>-Pe))`!}Q6PH u855IMn;yM8WYb`CnngLXg}79I zmD=x}s9#){IOhU#pa`(hS)yRuBE^(??zels-S45^_={0pgN^Rbkv|(W)l44oW)z&! zM?b96@epFG2*MxHF`1cuo~=4qf70zmkDpJ8gK6N}r^@{{snZwQ+N-X!T?<7@Rdd&L zuZSjkO~NTis$qk$)wMF|=G&jQCyhVFSfu86$tZ-5)%k~pz?w$E(?VHJ*Yt&C8!J1K zznB!nw*MUYbtfaq7Bg~wokp3!Ocz3K(f$7UKhqR)6$8K{K!Ke^QG+2%)I*)9gD$eS zTbNL}kQXrN8D#)O%{4x%p(!CT?xEpMeQLG;E~lqVRb|WbM izPHO=qO!2(LLWHd=byk6 z`|;yKgBHlg3_y}%VE%O>J1WOLTk4mfQLmz>a_KFnI~7q2FA|+Dn8AAnex0OeF@XDW zDPaz*WOmpNSM@(B>21i dZG_{~LC``Qsdxjy|1TcW_OC?lIbf4C21NsDP_um7dt;w+Pe|+q94cUfi zy- !`ul)~x_ZF4u z6cSn=!x!;HV@bwdBVIR1($9-1DGhAYOEcfuswa!wo?R)>P)@VU2NvH0$mIx08P02L zy|+1Nv8}NDAU>a?xnhd*Pv-C>H#4!}b(dbM1TV>5u~SCVVG#Ev>+UN0{tBLCG(B=` zSih!fK!@4-Ueu_{2e-UGEO9JB`^b%f*`86%EIMPhE7YVnj1xzX90L~=E@XO)7rsbQ zZToszcyc46+Y}Sx;wW-g5z}ZB?6_4j<0(;OT_B(LIc&$ss_2~Tc*WGy2^3+-=GA+7 zl^Y?LWECWh7YeeQ&w{ Yv&B&d+0XJkM5t$r4cb#hZ|Jj7>{dp h(LugB#otEs zHuo)(_cb>O-1$OFd{4+S6**TgQhFkv^;GEEy*_&OZwA1L7Wyrc^$-_L(=HgD)W@E_ zuXHy-zrAs+<81=B)|x{kvhYD5glR?KNmiKA_1WQ8QyHSn6UA*=4e^`ppUTOyaYD1M zW>?{pmt*MKif=c(Sus&!_D!$8BOJ8CmgAO8zb_ftY)lPr^)i5qzFaFz@p;Shn-(zx zljB=|oK|cNQgE*QuIDGqk?Cc(b<~dgKR9Aw6~T6ZrN7>dGEdbFJ3p+)6 MB<$IETlQn$tJeNqui79Z^jlRXXI{B3&hR2Jd>kG{NN z_5R(vgV{KQg4nt4JL%_FPPcZ*=j*>)aS15MF2tidQ0yi39AG!dOYv|&@--Zei0!^O z7Gn5X6TA|L%Neb|C82h;iujP@2{G%SmR{P%w`@l{;b+ x(T7GANA`NxF;nCE zYWL SXk_*!CAqjzQ3_vY{6ASkSOC}iKTwo!`7$Eb9~#?ezIUH4%hn|TFI zt?cZ!)D4&>$MfZM(sW$%F>>{~&^X)CLlP2{$ECqRCW*$!#?ZMz?#EWb-XvXX|E68X z>dOO}ieh?cFQF|D3RTRe>1i12+hEj$$FqEq7g2gPQp5FMA7bNs+TxxbZl0 bA=6 zP2}b_neV$Tz6B+^Jbk2s5J|QA=3ex~(QVx?CBj-A1$j+AnrVT4F(tIQx-{2qk6oiA zZqs07&usW=N0vt$+Zew8v<4|dwwr=tN%1*_aN&^8{$Q-P<+?GdnO}kNp;a`~oMJmS z<*Oz>+jp;6g^S_7Y#DCfo+jBUkI*fDsxE77QPm*#94 {CVpS`YkzJ@$v@luJ7| zRG}U3) z3>H_!v}euybUd_X326$;zr@$(FwW!8!?tjc<8?)i#S#lwiwVUTaoY8esI=CmvBD(b zz^R5$1n!pt%kaY$7(UA5MiJ|>Srhev3X|f1^qJ9fN6LV}fCp*6y{~rpz+re7%u{%I z#v><+AK9XF?d$2w-&s`EeVL;C? s3TNGA2p&a#vnXAlA5}bsR{O9>7iW1xEquJ*gke~MK0RWrdtz~8j zqoyeqFwviG)1+wkLUH(vjUN =khod{D4=UF4+R+q-0(e~%`$O8ysyjoz zsK0~<6|5UmutIt lARL65^++irZ<$T}rrjp34@ z^DO&Jcm9wRAQ}FRV^}31q}v((jE#tG2{}D%dDAg#?@U?2=t0Bd50Q(F5F=IS-o>XB z-H$Np(@6~3YEOge7+gGyTeBmFgK|BD!(OJ-9s>B(YNy_rQw{Kd;!Wj4^>J#gixSPT zB8AcO55JdaPlhRkpbdYKv2iz+2*W!XlP24b$N#{?4Qa u;k z #=$Tgvu$dx8OssIKgV zSS6N*sZ$b(?kXQB#m)QKZys<@pZ(Jj%qR18S05IIxkn1>3w`9fI(+yg!N(%a*4H9t z=TneRkgaU#I4s7+L799|nced6v&ieouGA9GkKUcT;n7>T+Y}bk@O4TJ8P^Oyyn14@ zyYRhT>&eG)%~k5tsdtLoUhqGE5$Y&F)rJChbBdZw9Q^)sPyuzZy0iIM${ru)P)*vE zP;~vRyx}djxLYn_kDvN{d!12tNDoEUxDTEk&)M5$n@d#blD)leYy>yMN6<}!Wt#Re z5jw*8!OBJ#(yLALj`=W+p12UZ&(TULnH`5eMI8=Ue`cG+ek}fu?#eu1Y!!`8c4^ zyIC% Ow#Df!U1I-fpte7U9gyjk_O{OLXj8RkS#PA@) 1D1Xg0N8W;(a5MGL850&aFQG*ie*U z*ZURC2zaPMx1%V2^vIqgG>v)L+Vc(qPQp=|*nzASW8N ;^S$%;Cy|lm+*~EfVPyi2p%?Jv(^$54?w1Epqvy!D|dJ5aC2yEL`T(<^<$ia zSO&7<>xx&<=lx4kd% >D(=<_OkMl4Q&+wRdkkSstkb0B0z~9GMwSBG-OrvT*-vx7H;48nJr?2HJ&<&U zhh4x!gH%C*h5_X>=P~(EUu*9)$pAmnut|Rv9LN9$pe0AM$K2C9K{E6zx(m+-)kuY@ zJ|F|IDWqe@quWiO$4rrkv*3}=Zp;c~kYTtM+}P%9D{k!^nE^OZ`ke0VySWv+vDUtv z$kwmDz8Pt#t?Rz;2O}A*kF=gS_$v4U11S6}^lI=P<^e1U6rpHPuTsj0m5&A9^>4j? z_AWv^_C !9jKrARaIBuhi$fau=kfv*I`t4kff8FBX6 5SkyJA$=7C#h**T4(wDJJAXF;j2*nI#;prfg10Xh-gItu%OdE~ 5zNC-!v;6jOR-3!F5Rt^2@_UL>uh_b6N&IS;!aH~s80ly6|+)G);7eFGfDta)h% ze|g>v)2+~7&zTi=j});rtiF!e+e@gOmZVlJ!4BS3{tiDR<_OAU27zgYsAXk=ajG&O z$c|Z31Wh|DS}aYf;rMB8IT76yO_}yYD3lgxrQK14;&+{ERo~TX@bsgDqZEwfl``v< zx$kgt-z{uwzhRRXpHB)sBan^0_W(&-A LLF`NWOlMc@YVkjmR-fvj9 zzw6>34n~?h`XG9$?$MIHX56X6Gm{QVb;_RbEqA2i+q}?*Mh6OR|4heL(?Ioa{8lRF zRlsolU^Ny3HXAia7!NO|vY>BOvr8Tt7%vWk)xb4Wiy@8AV+1`0GFeQ7zniV3pYKAA zcdW4i$UVf?Zsq>qlS&$tFxw+2Y&$%C!=Szf<~sw}dWk&--BKSv_cJU6(kK=NU|@)9 zZi=*dw1fKt`(H5Uy-vq5y#GF+OlkX!-zH<04$(H3#37u)7zW`W Qz<&R!H%YrQ)9{*19Jl4!_4s2eUCZYs9d3ih@q>a!Se0Cd5*m?`)7N0 zdgvdNO45gT#FjrKy|*$y*8to`r|sg6Fu7lyzK_Idgw@x|QKbeJ>MPMkSWTkKbD;+j z);n8S-%8x=a_F-UqN(N3Q9(Nn867wDAjF@}^7JmP52`0BqC%Kl?`AP(dK2{%Mn0}% z2u*A`W&;CWp@)Lfa3)AYH>fk1pz#ynnnuh0niscYKq!kQR>0k$LPm)IFDmHfju|)E z!MQ&h2KnCDrK9$|#x+#;naCo@{WWD8v`dr3w{Lh`$*Z_?-Eh}M$fF9X#>=~{vY@i$ zfS|&cs}t%-YK>zH6>7KaCx55&Ar!(Flh%66V7?)^8#Ok1KUWJ6$kACbhh}6$<-fe! zKU)sZ`F&U5s=>Pz9N|n>7FeGq|1tc#AF8GBd(SxEPHum_S}@+YpXTx5qPuI!@{wkd zuBfZC-;&Icz4DJ#930>t%46l+VIjqQON%1lWCs`-0xEEt@Nqfu7YdcvQI|` cHH<#)WJL`)5LPkRUG6MbUAig$EY4Hx)fk3h*y& zoDxvlOEH~PQirRNFSlMhi-~$F5icWQgR%?!93Qz6@ x#a?d5O8|dSsOP@`4HYK z8rwCn)ipUQdbtBjI_2L}2tzC`HlVqO^o|EF8j|!LMXhVtRe9XCT6OaNwgZS^-N1E> zbI+6Ws0Ob{2~mo9LCdFj>8_*K>$ui7e@;>JoHZJ^h-B$=ffu=Ao|IS~8C>hn3Zl+C zZ8+C6d*g>TWfI=kjhP_G{?zosuaarns1l406ie9Q$4pkM9bQq@?^lXCtaRXiC22Ql zzMS;~De(;wgLD>mk-Ucn&x1awBjjyc`d`knzKJ|MZe=Kyb(;gK67pmR)hIo)_-CLp z&AfM8Tz>o}{{91pjj^k|IZr~o<^Nn)n>S!*#wXQ3t>umC()uFw^FeYvYnMR4y4jNc zsX-;D!VT;f%1VpCnQw-J@7K*om&zZ4XAQTYJnTRn R0 zIbqW*6(37|q}rXA#l@5|f}A#|F;m284%~g*@TDdwqk0Ba`i!`P9MM!Aum|&H{VAF^ zcu9k2&0fl$z4m30%(`d(D{stX0Imd1+@Aho hWWQk|`$P&@)EOE@nLFCOtl4ab<%*`)|oPB!;FXka!hg{(r9|HiOtuRWA z7flusf!UJ&{TQ71r-KHq_~>Eu5BO&14G2``$23C-*uA9oSm;xOABu+o9I;Hq{6?V| z08`Qyd}W%eu6Te3Z>OJvl0-I=Ru-m7{2hkCie~`4_OPdVf?=8@kG)^Dl0>@pwXDbj z6O)@c4 sE5xR$l6||1ADc6> zulfDDLe338nQ(pF)FY7CXdNZ198&c|s&@b8N&^|oGmI0_a_jXAwZl(@(~y##U4Gf8 zNEuIS?VRXuX6p3DExt;@q?e`ARL_f-9x9Ex$7F=d#=9cMkPWCW(CVD0#F7QOsqC*j z8X`2m1unIs15%wvvaIE^1z7~;lLKSyD!&-P>|WlL?aMu1^)&V95LGJdZ(yNMF(P4` zVV`>919}cq+)W17Z-d>Vx^;Of#;+?lowV(5@TRqEt@@zaNLRS|v=L&7Zs0S#C!QkD zwz^Ke0(MTFqMRdWtq)iY*0;^=^{&N*XnI38VEZB5iqU?}=NcDRDI@^4gdeVQ(d?+Y zTMs**f8I*mxj(sVey$*8IKPi(IBVt=@!h$6*=fU!gFUncmFe)NXT5OeHl$fx(*!oh zHk#y^mk<=f++3Lwx)XwH?TLUEk~M5(y#dQglOc)+*;bjXDHwh(tGejoeIfbK(X+s_ zXIheC$oF{KG1!hI`{qieJba~Nv6B7;w<&s-WEht%pO LLy-IW~ zjEO9_pk(Ne?3K;HX~zbdL4XobN)ZHkMT-+x?Jq4fbkyJE_Q+G2bH39!o}E3|2B!%I zH9z8_sIFqB@gN-4v) 3WHRv3$ zDXzDMDG<}%(jgQP?sp12O)lz`aOxhgaJ9E9DBLK$Gi!h@{$cs?n`PjxBeW5`^#V_F z;NezA9Grx9vEiUDwAsWnMH)s>E}w>CK&UQ|LdR$dd;Kv_2j@c-e&1=Z{?ZK1FAHYD zS6L)DW@x7q()PqXSpP*R=q3d}*d}>QgAkrI`wP=d(a~(9nP-GyhT#wkUG;X 0-K~Y2*z!|hU8AgMG(jShX;Z_>-bRub(&YpqUgVs}`RhK5nSwbwiY+ea@ z10r`dO7BLZOlQNyB?i#r2D xi-+ z&^BWxEuI1`WL8gle61R3xET~5)F@7^8Q2=P3rV$^$TAW${>b)+ 4T+=QKY#3 l;QL&OiNg&9PmhO4Z;#`vTy{1u3HJSAxBscrvUpC4K8 z_a89(KkqQc;Qt2HP8AdDH@|S0^BL_k;=8x5oSdP#aYHy{?GAiDgs+4}*c;NmLYY_p zxfQBg@Gg&{cepzRnz~3wXc|euf?$n3W=U+DUWpOMB%=_xJ%uy)Z6anVfM*q(i={nZ zW&p!&P=MY-f$F#XAA!wi?ZCP4%<;th2@P6e7(g#VtAal@cQbK~Z!RgRE?!UU$V65? z4V2q>^Y&+AMA|`oDT*RDOnZq>U;ry)&~i4;!ObB0@elU7=Yn |vH<{P&(wWD}NO@5E$m7s@JzSbB;# z?@nBh>UX@N+Xp=>mPZOJbulFaTDV6TLI!-}E}EGElxkx3?R3$P;0K2HI%lj8CWt;O zMyDPChZuk$18`3~M!Q1s zAqs1V3to3eludBHzS+DN=>2_KF}avaHL1V;ItfbO_;_|^zYIS({il{wK2MNWTXS5I za-EP80l$M9JG3~z^}d~p^P1Kw^j8ja;TLobt=FeJWu5pxZH35PQXiOqvnI4rOx+m` zoS=28kp$&i?-s@uS-5t6yFXhq>}j8%O!Q29Pr(o?qqoGZmE{*Y%_<^qwC_1+rQP&t zGasu)9T`W2=1kdLsL4xa=QaFxi^ fwMymLCFjS*Gfxo~xwyj}^V8b%8D1S_!8r6!`P(fmVxf;k$6sdmYO*Hlx}l z25`$3U$Nm;sL5{;rKsrrOYxf{si)xe=FY|*EOHi1{DxFA&K?RHq+>xtM;hI8?N9mm z@Rw@SM9$RP2!qR!2X%Fesglq@&0O~%U_S*tmQFr2w^zY!!$xBO-P)i4EtK-=8QHnu zi-gFAZ#ZI|r*Llu4~6Q`HWx*Kt$BJBvlH5jW_Je#2Q@_NWGDV)kGF5U?x{-liQg#p zmICGiaVxld{J{m9epA(hvk3iP?>{u%`n>i-8-HaCE=2NzH#&t>7>_J%v?bxN>X^ h|!q?{wD+_VNqcNR7J4w{NkDt;RYoh{$v}fV%>$2 zmo;E=_uicMx?y
FhcSsZ-&uGgS`h5d`n~(|2Flj{{Kj|^RLVcA z=h~(ht>r&m)Bc=oAougJR^t2zWM)*t8yCIYqH8bg8lD=R@)h9azxP?@5g?ZR2su;O z+Cx#oG~%MMM=GGA1D7lH!yf*l*I=HH3{Ir|0;rnRX9X%xd#fVjV)nV&Nz> G|DR<0#aQxuw=drN?f8g@zbM@_EAE_FHgY#?xH-A@ay5EeZ zm)X)Fs~=4cgRc>~AT>G;;Zl$ Hg)3Qj24 z55#~xw8ZJBedS>;t56;^T>*uiG>C}o9=SORw)=K*spN=6eLegBR@h2*+Gft*VN;Zy zF+^@W5Jdk0%iAUouQfvgx!C(RZG0R}ba+J^e{ (KFDT7aH4`PiyJn-*?W z8)WAs1?UNa?PI8Co@=!D%dLJU@4I=X#>AQh33uZU;buFo3EJFSgQ@_`>p7tnd>!v@ zdUU-B%e7Tv+pi=s<}Q9#>sZIc-HEG7j)6ZHUHniJ)5bXN&F)j}Co<+w%d2GQOuV)= zIQ_#`IS7Z4+@m&`ooaA8mpDW&T61z5#ExVetmVc*COsdG|E;EZdA@SvBaUMlQ5 R+h=nX>sT^%d_+lcHK@Y<&gcP={$6>kkxPB|lV4jbC6giu{aVXjK2gj`~F%}1H zB7Qt^$C8{~<4Qzv1`i`@e<^3_A2?)pdhxCBbp^sY1 }`$|grJ)A-2IXDyin)g|ji^-U+oMGCA!HQqYEL`Aoh;s zl4>YmTbeW7!8GFq*8q~p%DX<9G`g3XO#U-2Mk5B!%I_@f;ut ze|HtrEJI;~ZyhLy=7JSnL%oQfv&mop38;NzJ<;EjARSuh9JHQ-M6<)ee@du2VF$_F zC1=ZU6opImHB}z{U3yqAskm^PSM1VtVfBqkz5cj%Oe}_Ub3FpZ21>8MxVG`!D@f!+ zrN+witI$8%P)#IG9@nBN(3S}G+&zAASANgkD75fFx`}_K^( O}Hf zI**)pp}}2tApAGS5=AS4yw$uX#SY~3p~8dlts*W@KGIZ5o6i;nv1WO8GFiFK-Td`J zs+&oNiu@gnJy3fg-^@z>cCl ~XuUBDwpIMzY+E++4sam2{CU&avXMlYeFUXZyEJ9XXG7BsJd5#QTmn>xtBp1xAzv zqG~g^ihq4{yBV?FiWbX}IeoJv6qax7bA7? Zf;vC%*AUkqVOeVB zK3<;T;SAQ+AHXR+SGhHQ^CJJ}IS+07BWK#Be8%b?$^2y3RYRqqnD7J~h6N3$S@-Ml z2D>!xcfI-ZX i|Guj zeYF*e-?>h5`I~EBjaps*6f2Bk8uTD@6&Z{9@shF$*0O zY8q;t@U?bWX{0t{VQy9QgyI%;p0R&g{9>aU?Uk^@v zMXEfkI3SyBb#7MNYVgf^VLL^cGZ@AB+PRZ84w2rql-o<|f;_e)IE30d(g$CsG5~Tl zoY>9)Dl+H?)DcuDvxBCrM3D-nx=e53Hp@U6mjwLc8kFEd*;oN)hRGZ9o;^_YDzvkR zIfW<9Va7VaND&!21Hgcq2&fJ>gL>0YUfWINI8z2_hsV+Va4T><29U4uO{nWyeda{K zsm?W5XoWzPelIXBEW~Y`BA%p7Vauc)Cw FORyrdk0RcoNl&dNZ)$7Ygofw+13?fG*ZAJcK~;Ep-QXJQJ)lPf1Mig*DyAs$u1 z6IU-CoY-2e55_4~cvx?292xj@JEeO=_D}MB-CUlC zN2}*P?$MxMezbp&Tz@P|M??OTd885gk7l0@nr%*?mb3kXQMa)z0|*AMVTeCq71 _hRB;YvJMqKYIdy(<*B1{NC!|52`EYG^p>EKkkcN|) zv@qp(k|gCFy#NwP76TB*JHetq(=Jgm-Y+>1nM8e!QE;`-OEy@2`&Nvp=C=ZY?S_qA z5U`~|g`y q3s3yc^non}toAiht&fp+?jQQ+zUOWI=d2JpGx%d1v1#Kn#tgtz z=Ri-{#K2Qo=P-S#Wvkk>+|K*($dv)qD_UXO2p66`^G4Hr<&O72=*lp8U=9t PLTJ(xN(jlx@!LMnbHDqJ_ulc|KOC4aIAx!`*Isk3IoF9GY`@)mtJvAz z-`?awkC6 zfz8+~96wa++kFr?x3c0rxUKcbD~swlmfSC54G1bKN4x5 ?nxxbU5P_IP4|Mbue#4nPLF=>>gzrG+CQPyV ?87aQ|bBGD&V>K^Ae zS8Gj0+De}N%!teOZ|ZRQiSI5XN5Vt6A1ImudJnvhsapt}_Xb_4NUcyJ4e$Jd9JVqC zOSBa?w {nhDoBr+ zLPhh89(*=?AhzRu4l?=hc?8{7c+npJW2R}eH*zAnW+vh5Tz#U2kIAn9_4g+FNv7E< zu|d2~+vF|so ;0Km(+H6yNxJ?8u-QJ%dFyWXa;tP_658;=+sFe1$n4@H|5l z$0Rm`Ay@JP@^ho;k*nn#fSb~`K&|cxL{_j6=O{8nSB2_!slYoBgQyJE=R2JESe96n z8ViZ1JUGQRKO@7J1y!4FcYtXI!@x6x55w^>$!sjsWZbD?9=)h52qp*VUK#nY-mR!M zfAi(A7fn(kH2)?o%o>A4GlmA$e8g&=fZe+9JQ(E3WaKW1l8k86pn9_rgW9oE^nTdd zKFNu(tT*jdc9MMSLFB6u#ae@}#QE)rL^ OQvLDV&a`Lj?oW0gV4M>Kh`25|s}w2A^1>My7m1q?uD91zpzub@Wq7#C!?oB#6} zvjVYxQ8hR`04CA2$kVu%l}-_Bwie1;ttAN&xXk9=8%nu|0*ea*hZ9+5ddn?{ReHex zME~J>vH>=U7xv@tkYT8p99f#;&V82Nl;rL2f2f}4*ipr$X2D$5zGRkP^}^8!(Fq6G zcp~O-hNKmGFW=0S_Z(ALZu^79K=uyZW3t1?feS{5G~Ne&y50<%|NjTS4DJX!sDM1Z z2(TNNGl(IvdcDBQl(vNTrb@Y7D=#UDoJq!LUZ05dRYTq64I+V}-U$29q*L>-8G3Ed z2bN!17XGp PFL!Z6O$W2{!L;<)SeQ83wQ2jBP6{Gr#O8s1M3I=Nf88a4 zHfJ^{gFL;42KW6DTDcf718ZOxz{YSMc?Cv-#OjM+%yFJStEDsk{kebc4_86jA+Q6R zs(?MQ0CAFIV8JBns`xvQeDEGTW9JAM$Qk7Q0dSbZ11ksFXPA~3Xl9_?y3fz7-ikV6 z+FR;c8F=rBQnBU-$BXTIOu4v|k)8p@0_TN@1F~=1b7-;r?|g|~9#*sMflcTeO#zHB zt a~odg<%0(d-aL~Tj6&L3`(|vfMRgQK>*ZOSU+w>v_pyI7arBoPPHZAk=xBEM zyOow+( KR^E`=5a^r z6^k lfhHFiTKo9=$EyBy!-FP%(V4fRY%k1AKhKSk zf7QD91lz9C(8ZLUS;Wxr<8e83%VsU_tb*_t;xF&myv}^JV=k3Pj!jO0?oEo~Pe6pW zY7D3JGiq^pNTZp-|CRgV*LWeUtWH<2dobn+)=+0A%=Sd;o`|9xs}rx{?=LLwdsSg_ z@k2x;RU`b1;*bf-u03tH%c*kZBo~&iP6;CL0Gi;{;nDU7RObyqbzb_4HHJZo7=i^z zYKJUEk#<4s28yw%O8ns@nJ_zxP4dfMY&@Mnn&DtMN{f@h5N|do_Gv}=()eE~#^x9Y zpe#x*U`Ze+XxOHKzz0iYJ71ZtFtJfo@sf-^w(9==O}Xd;q3<)24-}?5_9ENe+2(Uv zQy< zK+ym%0ru|8?I6hCC0>| zMzoGU{B{wCyBJuA<0wM%o+uH|t7=-b`nLBUvK8(vws&NG9{!0gLq}9_W}>Qi-!s$J zuQ~R)xi8sa!geaUGzi^F9_wGuD(b#p%K!M+4rPk;n`?(5`6=8U=kj5ddZAs;2Qu$U zMBAPCdCYtrW0+t}NEa_8G cRU@OM;x8}EA z%=nx;y_9)i)s;M$#~}7R+FZYUzV<-8xsCpv7vCKOpPgtX-c;a(k1y|l vJTQ8vs^o%~M7-4(t{q`71Oz2`xcCo|wtR?KSTS+c z96IS{k@O>{7S3b4ff?xH2;COi;_$reB|e*V;e8S7uIEq37qoA@@z)r*pFyz&oBlSR zv6ssYpb#DHu3r}+Tii4}nUy0-72m*>#TUF31_ zl$6j5;bQ;!i0zE7L+xhW^h7atboHZuj)?+QR3B-@a#@rE4lUDLbKrj+dV*ohdEL?l z13)XpL6`hJodTsBtk@5n=hwNiEzpc5^0<8hEf5h5qX|tA +hEq zY<#Z4!YlT1UuG}}Hn12VKO3%q; dRFE3V5vA|^4HKfw4C4!)S>TJ)fm1=P00qmmtKO2~* zGFx@=<;I9E$&L7AAG!p;h@yHt4KR75_-H8O 3J1+)s~bHR`p^&6}nX zzT@<)TE`FgT~a)hm+jm3^0V8T&wQY?U8!16B{BSBR|80DL^Zjul}w=4ekp_(&WuBF zJQ?JEgH~V^46>l~)U%=-f=$~VP~5u#k3U5egDp55znxwbJBhI-05vZbJPLS8ZSvTV z*u>~kR5pcAVp;Uie|>MA4f`4M{rv?z^V7)6<2xYbav*^KT*SY)*s?!d@o}&rD4r#E zAxQ&klQ--P*B94_(+Y^d!$GjL@Ii(%%=MTZ4yf5CQZ{#J%mcmvB#d=sw&62?!Fqs+ z{X%%flm}(tL)i5dr#b@7TxCeRfmFItr(hOgzpqc}^L&cyWX0 *`zN;Ru>aVCyaWfW@a0TOf zZ#k?YsR{pN>@f3MW@lo#R)2;~v*Me Z%tjKrkdZ_t%ytC;t6Y@#D`?zQ!g zhVCAqV&2U5TJZF{jsXOb-u@$%_twx!j$k-Mf;^hx*kl#T1`57SdPf?9#`V)_C<-=0 zZ?CJbs@<=9WldtqQe!r67s$$#Y)KZTTEkxd;<0NZp*5l`{IlxQo^m3$(!;tGj@9j& z{}tn1THR)+XK&C#YmV@@H?zJ*J6`g#Y;kr~(yo`=LpKZ(f@l`iXm^y90|Gg4l&Hjo z394BmIh-s2>-timX8|1_Ub#9+7 <;dmpz!mmyau(vt+Zdt!6@nh`f=~E927u|u9-s&~ zBg%4Gdor?t z8Q82-jRuCn0EHg|?ZVU?UYJ-+_*^0J?bvWfbS!wh? *_b=zup$~Z_jVD!)qLc>H4y)FG9026(P0M#kdw?|YL80iG7gsD)^7q6l_ zD!%U0HA1Gb% DA!t|PHfBmA zPN?rQN2|v_*?IbD?mwq`KDM%+^FlYJI<+vHk!zGr+UbZr $$Pv Aw^Qn;R+d(feo1Jh?h<{&Xz )Ly;Qdd(S^C&)3c;-OaLH=3}*4Lo&f^{jdySH$QA$#8h${O zVsu#=Imx0(4yhUknTqR5wj`vICHGx#W*aFOR2PFOX>aUQ;4LU#kR%D|f DM+h`IyZb12_bcgNU;Pa_*+E8pV z{KQ`E@)NIqxbK|Bh7pRh7t-n58Trf FJ(Kg}2j#I@B z%Pp+ZxWUCOjL=ACOgq{NxwW+9jAQ)j;T6KS!x5FJA2)X#<%0Hej;TuStoIX(zrz&7 z+I}pzz*fc$^g5G@D?C0#Eb8sSkjj?+bw`!-Wy?HM%S9tJ&)|RnqdrmQQ z@3IWTg+uUOj>NoD)bOmi^~o}%%Uh*Y{3<%oS^F9Co0zFCb){Xu_iq2X^UJZ};Zupu zw&TLP9bc!_Y{51YV3A%%cQxk&qV&t}e4SX#*p44ZgstE2IK0}l|IX{QKU}Jg_M!JR ziBl>Wq^Garj#K$>r9z&jJs(*Q$>8=x%1p|>2@UC(P54cIy^}4*Zk^>_AtS%YKU?4l zIux}|miEiLLXU`aQky%F^*u?=(7(N mm)!~qO8W>h%xzf)N2Z%%Qy*)pv%wVLk!fLmrIzY`24s2n4-Dlu zmE;GQSklUMo$Kg15i(0l5W%so6PY!tWX24Vw0F&eADYalV*y$(A9+_>cbCKi=0lY; z;c41-T|UN7^on%=XZ{aYqBj%pH@p*F@Ui7>4oE1(cGZOv2>QfL !CAvm`!c%`Iae~D$B~uAQU!zR5Vh{5y>0e-FAlB|*Rp2`bza}U90FC{s*k2> zn<_row}` qKP#krczHDQM3E@Iij^>fKp0& z>@zJIcPVhca4d9J<@syk3>n#;K}%N&k{Tz(o-+4H`#Jt-Xw>^Ue?n7mRnt~d2ofQQ z w%IUBJPdNUCpPxv(31a07I*c}`&L4c#1IZc@bH#33vvI2%-qP$POrpq5ZEWE&(S z^I`5>OX!IQ$1qD*a+kD&T{*y1&e4k5cF44uFZ*mys(`Q34 PeUKJ?`ul?yO}8?njof|kQyL2MEQ^7#`8DF)2qNjgJ^_-O zKwHr43s9+4(2S`GU2=Su=t|fa#ljbZnqe~(_eLJLs)5Zk5_|@C5CKUw#SrmCJBFia zJ$hSXnVprl^#~+mgz-{;uc$@ueg>5!gOdhNct6PrA0e&P4f|)!TYoRtQXj=U&&Thp zqkVGZ+q@+DQ^H{l% zs7O57SLIuI)K=SY_DQJ$N-4NG0&RqdQDZvy>tCH&Je*?NU0jSAL#$EghU$HHhxEnf zueUVnGrEZ_F#JABfprr8{eNu&Jn%n}rvH*_3PiEW8Ooe@pV32j2L1=`B=hA@>Z(~! z {yhphGqH7pVRycinMOS zN;_YF?eMb;t}L)&hrESaqnXRdw*qisxd)KL90iuc@3Z5G!JA3H)VmH(v#m1ahm6gy zI6rKh^-kZ!;z!TM)!)7Pi5bunBc*%AT`oUy>=`nMrX<-Sbz@ua6TVgg#N0Z+#v3=L zb(;iI#{WYrYUr~XGeE;cF#z@Mb%bRFR&Z7cOCf{+%Z#JvN6~BX7i^>|d_FdbyQ9N! zJCOm1X4MsmDoI)trJ_O)@I{ S8p z=g$i(s$1ieRT2s@y{2OZYdxY#e5Z3xeT;UVwIw`;Of12xs~g&+>SD9IAcT@=c`e z$tP`ks?~I!-=oDNGv<-L5`J&$Sd06KGqXQZ9a}a=*3g&2{JsQUESpM=JMI-%Is@(h z?m_cyJge=jL5NtgClRm`P^||}$ H%MnETMr*Lo7cC&FiB{B2T ^b%N~JK}59 zVE{a8`A1Pv(h7gLKDpvh-$F>)qUMD%mY~m%#M*Pp=(dZEuVb z8Ma1U{<84vJ3kt!S^j10M>qp22Hr=i7Xv;Ye_B^9>rS*(XA!lnI!Zx0!v26t3^!V>_?2GaqCL;@rqr% 1}o_xU*XZ ^D@g*@7Ktf@ef~bU-6C z-{BSJ5l}4jO-f|H9a#=yN?1O5YMvK2G+o`mBSxXd7j~CRxvmm- >1_T)!cvl9%Ug-rEcwr~TA#m-pM1^&IOuU$cmfyGfUB%6Dm$b1Laa&lhd@MagGM zoz}JkmllqGjg3Un46!2~)R|ai7@|uw4l8Hnj-B>*OmxQc&~mFy`>#pz+HRS}qL3ae z?UGO&x|j@lVJY~{TlN$_Vmn32NTsx4PaHp6ub+{(i-VR&y7+51h;0O>;yrGLLjMCT zyCEU?bsVWoN1AoT^S%nYz1ZbdNV--$W#WK&)Ren|f~M^}MbW=}4xnY8mIsJah z_sIGx8h@)-eG~=>?aP#L5?gQx`n^^t{@@+Pi$+3>J;Fh{s^AFFbPuV2>>$w0217KH z74&l&p%I^pCUtFrS|Z;A{2YT~DY?GyH?k2hM8+A5w#$!6lNY+7`6eOGzS3@&FGyF4 z?kSBG;$~MtmXl*2pbwoP0h{vQ5D!@x#!Q$AAU_bbO4`1h;LKlL#5wc) T5}NsbYtZ zyKSr863#d6yWl~Ao**FQ3JLdnIWv)#=kC1kZQ)$W;PqdxBz-k*&zULRpqFpoNHE#) zi93}_DwL2dxxRad_j*}j2f_EgW|L- %Chv!v*Zn{x#J(KY@F5>KV_L >W*$W?3 u^ zg`IB{ieesC%2&FL@^dY?aIk$R*-BS_UPfOFKfO35VSQTU#PKzGKbblCKof;Pewoij zvA4q2`JX+OfB&&YkdJH24?zT)Fw5Mfpf%3C;}@eOZaQ1>A}S_Jyr7_)e8alEg`*My z%hx*&5e9PlUPXO-|EmY7*k{*%bW~FvUW95LNv@>5Ojm3V!UZe+Ec`q&a$Xe4(tX kqD6&M4DMU_ 4UbVJC!|(>4#W9SJ zma
-e-;=Je#Qe@n&Yw>7 z#YZLV(2LZ)`hj6Ja*;@sp9?CFgYVG%G^hHcve9*D8YJ@w_k;9VS q$joW*trHj8Cej8jIm5*^dxjJe?+A8 z8g{I+9q_)w$U(oKbdzArmAfN2DSN^=?9$_1Z*%qK^J1u^WR+oUL~s!_ zS(jx_P_g02{@%5SqVDAbWBU^2l&_s(H~SGOsGUJaJ2lHib)-#rX83>o8ZjAxELqyo znqAy4EwXJSy!S2SSK())@2!2%iv7|E9U;)zq+t^Bq(Y9vgb?LQp$G!)#`17zBWm|a z)DD;A`>_mn6N}HEliZZ~jXoHTpwQ+*qAW!Fw`=qDfW1Vk_|vyjaIlX_A2Zordvfo~ zr;bXGm}YbKYT?tbEi&Di6h>~q9kNHS(z5NX%vc+|XL3FBN&cvxYYnUUnUaW3**?qB zLup|d=jt_ql3Q8+a^Ka8#Yx;Zt)i=(?DKi1yXN$FZZty2l0i1Oub%O~C+KYO2OH;# zf8xfd_b|tex+MWjQPSlFjii*i7A;@Js-2%et<;$zr?Gu~xNuvqqw+5@c45j%d+Bp& zIuBsSw$abXatl(_CGe#2xW=-+mKU72+0J=^rsNYoqnka373#5qIyTv}s?Nhz#m#DG z_vSx7e8TY5%SrFEBUzAscj4F|u}Q~}M${NrymS0?pNNZQ&f(<5*pck?jVie&ah>Vt zhMOr !LRq_A&YVa?Uk} zX{y-9mFF;{kUehJh?WilW!$6&p@^gvXV%-NPr9jSKd*T2-)%LNP;4jJ&|}MkRM(`y z0?US8rraN{GSU)x={GKNyz80ziXv8TZR-9#B=0*wYY!f9iEfs#!Mv~4M8t6Y)=D$c zCl&6k5hWV;Y>IW4CziO0U!S4mh)?*8#%xIytmA}{C|TH@kn^v7?X}fvmC=Uuw)7kB zsr=Nhj?EogT1^dTPQFsA*pG4!nxj=|3CLMvNrRf9#cvCGhhD8>u<_|CUkSreH=KkW z+g;)_tCx|B8O;66dA25elamTxQ+bNW|6aY`zSlj@@d~4NSJSbiqYt-vJ~w!9y15Ts zv<&Y}nLsa|&A;JV{&UYgWS7%-R&3Xj^#%L9t;dxcE4Gc4jFCQ}pJBIE?+?2132(?u zNWU>}^`qrwQ@N*N%8gx9GCFaPYAq#s@uZp1z3Kx!N^5AL;YJ4smk`Z&PqykuY4KjG z;r^|_%GPH2&WNrUjvpWN>uo4?HuKAJwO5^0vB?^QA?`K*+Iq>kYxz?b-O6yWj5Qr6 z29A?jEsA_cww2L@>UB?P8y>u5;u+JS4YZ;y93#2%Gsn4VM3;(NMUFc82#4*L{lc|n zG)?ttX|S0gjgjD4Fz+ioY;BYN(%JE#g+)N3++$U2C$Lh0F2ObGFLHuJILRu{$~@Lw z7w?{IT6%A|_?j|N216AA2M{bpWmgHJ)oG`A(so8JAVD@zpL1vN4;Nn%YhaJ3l}-Ji zsC=1`g1%~5*>HOiC<3Q|To@HX_eKrn0jXKVDNa+#$GR;%Ue#k7UF_UOgN-D0n5K89 zA<9p>iUoz4sP{Lnt(XPMm??g@`_(G@CT{w0nvvW~Qx`6QnRimWP$>{)W0D?(U)xc_ zRXTQMUXI-R-cA@+XdJx}*{O;bi`-O>^cUhQciFpa>D;=Jw{C(`WbA(UZeSVov`xXs z*ejsc1w1%Xyf`7bQB*7rq{FoWpaf9FBqFstL6-B5ID^i@!_NT^zy{=yNhHhcDYSCs zRxqRwnPXQvNxT4$RT+5HF7pytU~K|%C>uaD#d#Va##@1A%eR)mkN^$^=Xp4fr4F_J z?ZOs|yM%n9dmu*N&ECV^aBV6@$e0bxIGgkf@DUCX7NfRKt)wTfF6?0m`%BpDV8NX` zgB+~`jud#mbF~Usk9JFI@+*{)DY;NPko|?o*ob*3e)o;3oN`+KElr4GaSst{^IWN; z=1^}{9e}*PBG2v(VO1ZT=Q<;$x=sY?bp~NaL?d>P+!P>qM)phTXB(#O)uEPG)p{SF z25S;sIZ1l|jKEhwpK7UzmaJ9eSC-8iao>c!cSuYYq3xS*;{j28RM6?*@q&xKHZJ>C zYz)zZ3C0?~T@hSy3{LJ)XU^>_rN*Lr%bf&NR?yv}?t=b_Sb6AfrqV|9d6+hI13NPz z<=1Sg )o5Mr0y59vocA!VFRgci5Dk8cy?pfR%D4=cb$ewW*uoDk zs nROA>&%EVXB5I!No8+)4VG=CCIZ_Y z&iV01Oixu;k!eE9GswR3dNY>zBQh)ihAJxKe}5MI` (T*mh|>Ka?_G(~nwHQYoWox<{w1q?E+HPTpAsP~!EYeYvwM54Am}exI6N zlDM8WH=d}OF7amYar&9bSA&A%=yA6ob)KX2ZEu^Q&kbmX%LLkaNZB275|TD^n?8Mr z#>i}GdvG<{`S@8W{zYu oWf9`m?&)w z9{rU(62DqyL_cMVwoZ^WAiUKWabUBsQ9wjthTT1zfsCaCf;LB}(Ib2m&ol(1EEw^3 zR40K%!3^FT$mnL)g6Lt)d_y7aKAe1)$d$H}6`xycCN871+m8rd=wXB%>3ioraHwS# z1ZE&xu1?uc;o~2lHx9Fx$;=YFn<^da?A|4fj~tpZ_RKSre-+)YJ$wJHuF&jewLGk5 zpLVd bXZl)FyVnkeN&O#0gScm}#Tfi|j~ z?>O=Qs`~t2|A+pc@0N_`L(j1onM89YDtp+4ETJQj MWpBpuf0Y^> #jAbd-H#_>Bg>|s<&Vy$e6hfp#Z&5t7@0Y`C26?K)LKa+q+;HfME<& zE%Awtr-^csalpcBQ%*N{8idN6tw 7Z##T=~Z60ml;+{Ox!W&JG>)|z&rZxN)G zwQOwuaLry-hmGS5dPrq>=CO$dPK1u1rg&Kyw;XC^V<)tyX6Gmb+&%qk+z>tH_5eo@ zVqFlL0qi*tvzHyiBEiH?0|tmPJfH~jYA;QU<5>w9#-aJ;&0VL7j{))c->vBO3h*zw z4Pu 2s^?4ddY|0tkp9j|1d4#QYisqvYOHF}6ycizB`nSi##q_hc&qoPv zZNp>(eNKyBGHjGMb@gGgri-#j`T^%s?P1j^?0q2SXo0&_X<|wZRNy+d3hGT#v$nzD zngwXY%cLLG2mE}`^*gNGcUv2|=ZFt2ort^^v0qZy4*kr1=IallyL*yXJNG;6m8(x| zw!Ge ZPH(xEs 3>%9w>_knreIIsEDiu)L#Ff#LUVwb!%QFQP~; zt@9pz=q^g)T44V{fs 6Z0U1+3C9}7cBQbyujAxf3CxcZ6U?*n(lo02T+=f^PYQ^-*deU@P zJ%FwDMLB+6;|BF);d?!s8p%-PL`C^?E+3PfRfVykwe?)CbSxt6&Gu?i{lg_N+58Te zmrcMaJ}WRV$o4Lfv}n|#87MT%NF7pM;ybN5(W}lWSvv pEz-Jc@Uh5lUOZ>YaQD~C&vXHbp1G2&RD5ht!atP`b);eN-R?ZN_D zS2@0gtMpXnu4V!Q6mTaJHh)5EVYGoQ_=2QsEr)&`Gv1kd(%rAp&dwiY63=vQ)XY~` zyrj1MSfl!KSFTyn=HK=8oKuLt2MuqhE -)+0{S)mLayc5F^eb@v30`Pt1DX`eUHdwqx7Y6>`Fv$%bZfHVjlqihJ43(Xy+zlK@Jm!; zXyoWrOv22K^-JxC>jMS%SbSCzb-gH;d9r^FI`iO&NpySh;H{#5-OT>=fpxSLXd#0A zjBUaXjnwFg_3Q0&b (1EX%dJHRZ(pys5SSKSs^CeBhIA?g^j9*7wpiNP{|Nz{H1Ox@S)5( zD+}MI;A!e~Wmm$b3#0b2UdBJyzU)8}{yU*~ppp; Vgl5LL*8ld5+L*MltuW+PSFxt3A;S=Lgvr(#-=4{~n%z?SyPbjpj3 zhpwz_PK4fe&;gwX5pNtP;Bg9nxSobj>pVMTSIob+_)W5ULd(W&U2TUIA|h}OJP3DE zjJ&C*UQQfgP1v-4E9relvqaJ|W}Vm*-MFJ!*g9!P=WfQyoNnipZ0)vGYy%>NbX0ZK z1??Z-p{^x#>PpbcLn~6HqjEd+dP>HpyXNEJk%JdmtFuvb01%+hVs+>t(4jU-T7j9K zZ!`PpT*^y1h-@6F=owSFcZ8cer)C>zr#p%Ab=3y8uMn#i-@`n7YVd$;Bf{0h*s1*8 zDb+!9gIKRAQQIj?HC9*MuP@rU``1E>olNjy_yr3i>^W_jV0zNC^oqv^hTX6YWR2?P zWh6L#95%|HD{A)gII;NhwE4eD#NldT<`h^x9h?`{n*K@Fgm7y2`Z0ap>DSi3B8W@R zpBd?{gjcFO0U}M*ki$ers$5-~Tv;*6bM-p5eCUG}s!r!eE)#gNli5njx4uR?J*jW1 zmv4K>&ya~)xkvpH5mvNlnQ-Z^Gb>7HtpeV~ihr0tvm-U$r5A@sB&HlT-!SXJ`A^WN z6BNrKZk_YH(%rr&Z2Lex-Q~tDX-r5{pb++hE%`WjED*1e+(?y#QlFt8D2Wc=tRK2^ zg`5SsZt}$-SHpD+D!)PJ9%T30#1_xtn)l2OnB4cP A`ua$S(oy5 z0{1e;J#Q7gkFH&ps(d~){74u^q3x%$ax4;2JO3{sq!2@CKPO&?3q2>`3=(odL`ZD8 zd>lI_w}1iShYX4kd#g3{duz1{BZSDj)IP8Q9#&??2ulMZ-A31u)MkCK CSDwJ|K5z7w^dYFPt pu)F;0T%&(>1h!iL#;JT162H*yxCUL4x zh$tqq2#7xa)g$u{m$s%O(vy@7cOyyE&+j;>Nob3-s&M r!+f{ zYt!vCqCW+><|Uw3IGzB(A0XwTn6d*4i!@X-=-UK^RhdoT1)Qx5Gjx*bY*JFLPrhK- zo8~3T6hF=w#IVz;Gch853J23uF!dF=rpJq&t?3UJIlVAK>D-=|<}eeIp)Mh(6nLaE zfbbMWyPRIm7@(<$1Co|NV0$N%%HNLbn;Fwce_zYWr6X2g ^Ee}22Z8L`!t5R|GKF}_b+?iGwv&G8NQ~aOM zCVndE8-9zQL>+j@iiCRW%Wt2o$Q&Ly^8(^uc!vRi2dGvuy-l})mtC6I%NOUS{IoLu zX;qAIYGFE$XR~S-C39Df+GKk%+nC;9p#%RABEw(!2+K1%Tq{Q(Yu#DM$1biY2k$|F zU>UIe9SAb(zZ1SXq4BKXbDq=GyIyi|-IKw@$PA7ladwqwOJ(X6Rjx-(K+v_HO8PLc ze|q;LT6Jd#)QSpRPD!XXLjAabethGIR1W6hM|chUumm>iG8@`J1j5(~NLLa=PqzEf z*HT@d`q!ljty3qskv5rp2bwWw$4$(hoKEckMj_Oor$Fm^s<^9w`b&T0QmJXN-KFR6 zSS8ODC_-^4+STPDiE-Z8hErn)vBECq_?NvVRw 08fpEW*L>KP z#qd?k=nIy7;^pT*CaaFtFOlZDx{JPky6W&V)l_Tkx5B16a|94)>70xz_q *;TkDFNX|QzJgBIHeJ5KI4ui)3h8wxy zRZlF&tu@_=sQoVAMfv}-Q;z<`L^5N}eXWS@=OOt}@HHWCwY2w5p24cevLO;vWjT%) zVgkW)nkZeV7`!at>D~R~ZsR}J`NXNDBe;_w f6gyNqZ5)T%{auasN8#Fo5&?bXcZ_*{Kaj6r{Z zdM^4EA(BM9s51Pj?qLVIe)G|?@ELS}rtkJ0U)DTyE)ZS-fjHBlnOuZkw4;q#;TzMo z_0`5uQVgpHNpH(vE3m(AZxS+&yI>^N=G|7#a{!4O@5;vPyGhB2%{DH!^Y_Ohpe?G< z_f>4hPs UY&n51H?#|6#6CoClDIc38|?Z0cd6gp+$4%B74#E-?1tY3V3x(;-2}u0 z1!#(e-G2p)u%u}MyAdoQxn@`yEsAG7)j;Axst8$0@bl~IQz(wIoYVg%FrmK$CW%r- zSTP6!`{ykZr!0*W08PEcZHH?8(UWn0YDhhS?GDt8(0^4k03^#kjnAD>HMs~? z@omBZK6rZhghBWk^E0%ANwV&BOQ&kP+OVJ9I^$pWUelsg?uj*t){XuweU*sUxxf!x zvRP#G=ANoYm~M12CgQW1D6U6uCv7HDDWj^WvX2sX@6LXMbN)P-TDtPZm*dC%VmhmL z?E!7IIW512PHotf473-C=O#Fh=k&QO%H|bUM?7`a>MW-Q&c|XJG 50`ZRFIb0@ zjPL}>512heq?p$A`qf7rRu|t3vV|-_wMi$)h;`R-KY7>fhl mvM+I5cQV2G3cP#cViO`)@zG1xUR0 z?a6etGEn^sgO2%egN?ZG3Tbu`-GM6hdr4qy`)f%z6kEb)o#y+_d-4%CjhF}B>JnrI zHjztxLv>WBm72sgc4beA&G+;S@0C=RXLR^Ux2q} zWnpgv_1O6Pg~=ZFnw~}Eots0=;;bw0-@fH5g?zwZG@y#j#ihBr!F0B7H%8wg56K~s z2q3o~w_|Go0x_4&L&8>P85xKH*u3e=*FjHX{)o}8ag-~|GXD7KL!?TG3I0mqKYulg zh(s{{;k v!XRUF#l5aFj_GT`RI9oLD8y zVp~v#%MzbPJzw7{`eMB+sRXa|mSD41#KweGqXt@zS!LP3fA#9JO$!Yge*A)c3O)-S z_#59V&B)ePY)yMiBdV3NAQ?9nLh9DW^th>a9GW@XPrb3IOa-qVs*$IEsXr$D)Dx?Y z8VqHa>Ogf{!2M_q{0KgPJZGA2OOMKmt~bsuon%^DO#x;~t&MI_uc!}M#a_}iY&@%8 zz+$0fp;{@uLj-Z8IoUVD5jy3ubIf+IHT8~1!jcGZ+=Y;?4Q`X%!Pr{0JWi@Z0lfvk z;^7`W@!yOJ*{p`}8}CbF4plC0|MG+^$Tx2PavM1wL$N>PxecY!<>_B|fN)s8qvPV* z^lWwt+F;zaxQtIJJ+$EP=%Ivx#Aq5i4s<|JG^ihhCD@KG_6MU> 2UP zrIK4nkq+}ejN8)P{ZoMd(2!;wXnRbdHP4oZww+KsVi8l@`7ldV`c99Pwes6NCX+bc zMtknto^Qf}90iLk;!{lQD-b7I9Yx`(Wombb0H`!lOT!Wq=mrhL_HPd~@|F>UMJzrF z=0 m|%Q z%8+7V2+32l#&MTh@ 9@L?$ajKjY&<#K% zyHvIr7M*A83+GEji+0FtsZ&mYUTuy%!8R42NiT2Ry!z5GruMzKOco?=@LQ^Oz!^ha ziPtk>IxV5;$G^@PS_0Z03-)Xr(2~_&066*MFtjK|8Nr7T_>{8BX`H?n5+jw*>b})A z)Lp#VI-2+G>PoI3tUx!`H AZPzzGQYpo>Ar+ZQg;d&-!l@0S3sF+q zOeF~kAry1kgd!#tDrqYFlquWF=FGmdA+j%HlVmf@*o+x-&e?CN`?;U{damoapWplW zyr1X2pZC4~7@Kou&dqVGW3BJ{u63-{<>+)WCQ`JGRs>OLw6VLbqCdV}Js6pjPZh;J z>2W>0`aQ;@J C6!rD+b `odByJmD)r9uGZ zB=)&JmVZT~JgXRCc1I|U&v8k&p+C9RP}*RTrfSl>um5h`mza$6hCMH+bgja a+gxH2;~vU%Q- zjI-u96;9+|MvFW!`}2EBidz@85M5QUC!JZU_e? WL{N&$_YgYadb8L86p4s5o%AT$Q@PT5sBa)=jQ zj eEPvY(WpdN(PHN7ZOmcE~DR9 zQbPob8cGK#y_ihlVn@pCO}8HAzNkN&@G@FOY ?0M6l$9TkHUcG(KJRm|s%2ZlS za6`0=-U^Gxj-4pfAZjs5AN6+3zffsm*FaoaY-)#ac~?#&t!mZ9+`~n47`OdiEfZR` z34?^pPZ12`2rb(;TQgKYJalF(P)ekgw0j00STn0K+9c+KMN+J1tV!z;oyIbnM42mf zZpQ{6$MTNr7Uz$$YijlL>_%4$rO+Zn#D)sfn9+TyP~K-dH-vZoSz^oUm|3V@@7yz| z?ih;u?Eetbr4+w#yp~V|*gTooFQ1RXA1M=wy{|p+!*)uNYtb#24QB~<34~U|t-d3R z+J7rqbck}f)XE}FS=oMX#yQXPN8iBa{vIum>T z+3t$(bBh}CN@0FpfzLG*pDkLtq5MgIkGk+Y6~8^M1fn`s^l}Y$MqQyu+TUq;NKn?p zq{; n%e8${qJ>&!pkNL||Yios|p}x}VS&g}n zN4`zCp7@B((N*I=JwmlFTyjs8#kHT!TL0c_`PB}Sy!C;I# 5$t}3h!XPv)#P{76i2RI~A0M%4Uv=tq z=9%KKw2cqsjf6P?=0|&tbmkO{4Czv4bewe!Db|)=n7nLFYuFK=aiuy!4L{3I `&^!MLT0Ze%xh z-Y9kHy|Vz#U$eUT2*#IQ#{8P#vewKrYPL#g#EXf2lbSHbI~1pued68hF{a;KZaZYM zOQgPvn|FCFx64DOhB(dQ?ng3jA#LfDryC