From 777c107c63556a5d76191b4c130dbac09f0c9352 Mon Sep 17 00:00:00 2001 From: Arc <33088785+arcbtc@users.noreply.github.com> Date: Fri, 22 May 2026 20:18:39 +0100 Subject: [PATCH] feat: UI fixes, ticket waves, ticket image (#52) * email input fixes * promo code ui fix * ticket waves * works, but horrible slop * slightly cleaner * filter most recent * make * pyright --- __init__.py | 3 +- crud.py | 45 +++- models.py | 117 +++++++- services.py | 241 ++++++++++++----- static/image/ticket.jpg | Bin 0 -> 106602 bytes static/js/display.js | 57 +++- static/js/display.vue | 18 +- static/js/index.js | 502 +++++++++++++++++++++++++++++----- static/js/index.vue | 582 ++++++++++++++++++++++++++++++---------- static/js/ticket.js | 29 +- static/js/ticket.vue | 49 ++++ views_api.py | 240 ++++++++++++++--- 12 files changed, 1569 insertions(+), 314 deletions(-) create mode 100644 static/image/ticket.jpg diff --git a/__init__.py b/__init__.py index ef37528..827b8d5 100644 --- a/__init__.py +++ b/__init__.py @@ -6,12 +6,13 @@ from loguru import logger from .crud import db from .tasks import wait_for_paid_invoices from .views import events_generic_router -from .views_api import events_api_router, tickets_api_router +from .views_api import events_api_router, qr_api_router, tickets_api_router events_ext: APIRouter = APIRouter(prefix="/events", tags=["Events"]) events_ext.include_router(events_generic_router) events_ext.include_router(events_api_router) events_ext.include_router(tickets_api_router) +events_ext.include_router(qr_api_router) events_static_files = [ { diff --git a/crud.py b/crud.py index 3046f0e..930915e 100644 --- a/crud.py +++ b/crud.py @@ -1,9 +1,17 @@ from datetime import datetime, timedelta, timezone +from typing import cast -from lnbits.db import Database +from lnbits.db import Database, Filters, Page from lnbits.helpers import urlsafe_short_hash -from .models import CreateEvent, Event, Ticket, TicketExtra +from .models import ( + CreateEvent, + Event, + Ticket, + TicketExtra, + TicketFilters, + sync_event_ticket_waves, +) db = Database("ext_events") @@ -51,6 +59,31 @@ async def get_tickets(wallet_ids: str | list[str]) -> list[Ticket]: ) +async def get_tickets_paginated( + wallet_ids: str | list[str], filters: Filters[TicketFilters] | None = None +) -> Page[Ticket]: + if isinstance(wallet_ids, str): + wallet_ids = [wallet_ids] + + wallet_where = [] + values = {} + for idx, wallet_id in enumerate(wallet_ids): + key = f"wallet_id_{idx}" + wallet_where.append(f":{key}") + values[key] = wallet_id + + where = [f"wallet IN ({', '.join(wallet_where)})", "paid = true"] + + return await db.fetch_page( + "SELECT * FROM events.ticket", + where=where, + values=values, + filters=filters, + model=Ticket, + table_name="events.ticket", + ) + + async def delete_ticket(payment_hash: str) -> None: await db.execute("DELETE FROM events.ticket WHERE id = :id", {"id": payment_hash}) @@ -75,31 +108,35 @@ async def purge_unpaid_tickets(event_id: str) -> None: async def create_event(data: CreateEvent) -> Event: event_id = urlsafe_short_hash() event = Event(id=event_id, time=datetime.now(timezone.utc), **data.dict()) + event = cast(Event, sync_event_ticket_waves(event)) await db.insert("events.events", event) return event async def update_event(event: Event) -> Event: + event = cast(Event, sync_event_ticket_waves(event)) await db.update("events.events", event) return event async def get_event(event_id: str) -> Event | None: - return await db.fetchone( + event = await db.fetchone( "SELECT * FROM events.events WHERE id = :id", {"id": event_id}, Event, ) + return cast(Event, sync_event_ticket_waves(event)) if event else None async def get_events(wallet_ids: str | list[str]) -> list[Event]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] q = ",".join([f"'{wallet_id}'" for wallet_id in wallet_ids]) - return await db.fetchall( + events = await db.fetchall( f"SELECT * FROM events.events WHERE wallet IN ({q})", model=Event, ) + return [cast(Event, sync_event_ticket_waves(event)) for event in events] async def delete_event(event_id: str) -> None: diff --git a/models.py b/models.py index fa1569f..331ad6d 100644 --- a/models.py +++ b/models.py @@ -1,6 +1,8 @@ -from datetime import datetime +from datetime import date, datetime +from uuid import uuid4 from fastapi import Query +from lnbits.db import FilterModel from pydantic import BaseModel, EmailStr, Field, validator @@ -20,8 +22,23 @@ class PromoCode(BaseModel): return v +class TicketWave(BaseModel): + id: str = Field(default_factory=lambda: uuid4().hex[:8]) + title: str = "Primary wave" + opening_date: str + closing_date: str + currency: str = "sat" + use_ticket_image: bool = False + ticket_image_id: str | None = None + allow_fiat: bool = False + fiat_currency: str = "GBP" + amount_tickets: int = Field(default=0, ge=0) + price_per_ticket: float = Field(default=0, ge=0) + + class EventExtra(BaseModel): promo_codes: list[PromoCode] = Field(default_factory=list) + ticket_waves: list[TicketWave] = Field(default_factory=list) conditional: bool = False min_tickets: int = 1 email_notifications: bool = False @@ -84,6 +101,8 @@ class PublicEvent(BaseModel): class TicketExtra(BaseModel): applied_promo_code: str | None = None + ticket_wave_id: str | None = None + ticket_wave_title: str | None = None sats_paid: int | None = None refund_address: str | None = None nostr_identifier: str | None = None @@ -96,6 +115,7 @@ class TicketExtra(BaseModel): class CreateTicket(BaseModel): name: str email: EmailStr + ticket_wave_id: str | None = None promo_code: str | None = None refund_address: str | None = None nostr_identifier: str | None = None @@ -116,6 +136,22 @@ class Ticket(BaseModel): extra: TicketExtra = Field(default_factory=TicketExtra) +class NotificationDeliveryResult(BaseModel): + attempted: bool = False + sent: bool = False + error: str | None = None + + +class TicketResendResult(BaseModel): + ticket: Ticket + email: NotificationDeliveryResult = Field( + default_factory=NotificationDeliveryResult + ) + nostr: NotificationDeliveryResult = Field( + default_factory=NotificationDeliveryResult + ) + + class PublicTicket(BaseModel): event: str name: str @@ -131,3 +167,82 @@ class TicketPaymentRequest(BaseModel): fiat_payment_request: str | None = None fiat_provider: str | None = None is_fiat: bool = False + + +class TicketFilters(FilterModel): + __search_fields__ = ["event", "name", "email", "id"] # noqa: RUF012 + __sort_fields__ = [ # noqa: RUF012 + "time", + "event", + "name", + "email", + "registered", + "id", + ] + + event: str | None = None + name: str | None = None + email: str | None = None + registered: bool | None = None + paid: bool | None = None + id: str | None = None + + +def _parse_date(value: str) -> date: + return datetime.strptime(value, "%Y-%m-%d").date() + + +def ensure_ticket_waves(event: Event | PublicEvent | CreateEvent) -> list[TicketWave]: + ticket_waves = list(getattr(event.extra, "ticket_waves", []) or []) + if ticket_waves: + return ticket_waves + + fallback_opening_date = None + event_time = getattr(event, "time", None) + if event_time: + fallback_opening_date = event_time.date().isoformat() + if not fallback_opening_date: + fallback_opening_date = event.closing_date + + return [ + TicketWave( + id="primary", + title="Primary wave", + opening_date=fallback_opening_date, + closing_date=event.closing_date, + currency=event.currency, + allow_fiat=event.allow_fiat, + fiat_currency=event.fiat_currency, + amount_tickets=getattr(event, "amount_tickets", 0), + price_per_ticket=event.price_per_ticket, + ) + ] + + +def sync_event_ticket_waves(event: Event | CreateEvent) -> Event | CreateEvent: + ticket_waves = ensure_ticket_waves(event) + event.extra.ticket_waves = ticket_waves + + primary_wave = ticket_waves[0] + event.closing_date = max(wave.closing_date for wave in ticket_waves) + event.currency = primary_wave.currency + event.allow_fiat = primary_wave.allow_fiat + event.fiat_currency = primary_wave.fiat_currency + event.amount_tickets = sum(wave.amount_tickets for wave in ticket_waves) + event.price_per_ticket = primary_wave.price_per_ticket + + return event + + +def get_active_ticket_waves( + event: Event | PublicEvent, today: date | None = None +) -> list[TicketWave]: + current_day = today or datetime.utcnow().date() + return [ + wave + for wave in ensure_ticket_waves(event) + if _parse_date(wave.opening_date) + <= current_day + <= _parse_date(wave.closing_date) + and wave.amount_tickets > 0 + ] diff --git a/services.py b/services.py index 159bbdc..057026b 100644 --- a/services.py +++ b/services.py @@ -1,15 +1,15 @@ from __future__ import annotations +import smtplib from asyncio.tasks import create_task +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from html import escape from lnbits.core.models.users import UserNotifications -from lnbits.core.services.nostr import send_nostr_dm -from lnbits.core.services.notifications import ( - send_email_notification, - send_user_notification, -) +from lnbits.core.services.notifications import send_user_notification +from lnbits.helpers import is_valid_email_address from lnbits.settings import settings -from lnbits.utils.nostr import normalize_private_key, normalize_public_key from lnurl import execute from loguru import logger @@ -20,13 +20,13 @@ from .crud import ( update_event, update_ticket, ) -from .models import Event, Ticket - -DEFAULT_NOSTR_RELAYS = [ - "wss://relay.damus.io", - "wss://relay.primal.net", - "wss://relay.nostr.band", -] +from .models import ( + Event, + NotificationDeliveryResult, + Ticket, + TicketResendResult, + ensure_ticket_waves, +) async def set_ticket_paid(ticket: Ticket) -> Ticket: @@ -39,7 +39,16 @@ async def set_ticket_paid(ticket: Ticket) -> Ticket: event = await get_event(ticket.event) assert event, "Couldn't get event from ticket being paid" event.sold += 1 - event.amount_tickets -= 1 + ticket_waves = event.extra.ticket_waves or [] + if ticket_waves: + selected_wave = next( + (wave for wave in ticket_waves if wave.id == ticket.extra.ticket_wave_id), + ticket_waves[0], + ) + if selected_wave.amount_tickets > 0: + selected_wave.amount_tickets -= 1 + elif event.amount_tickets > 0: + event.amount_tickets -= 1 await update_event(event) return ticket @@ -55,40 +64,12 @@ async def _send_ticket_notification(ticket: Ticket) -> None: logger.warning(f"Event {ticket.event} not found for ticket notification.") return - subject, message = _ticket_notification_message(ticket, event) - updated = False - - if ( - event.extra.email_notifications - and settings.lnbits_email_notifications_enabled - and ticket.email - ): - try: - await send_email_notification([ticket.email], message, subject) - ticket.extra.email_notification_sent = True - updated = True - except Exception as exc: - logger.warning(f"Failed to email ticket {ticket.id}: {exc}") - - if ( - event.extra.nostr_notifications - and settings.is_nostr_notifications_configured() - and ticket.extra.nostr_identifier - ): - try: - await _send_nostr_ticket_notification( - ticket.extra.nostr_identifier, message - ) - ticket.extra.nostr_notification_sent = True - updated = True - except Exception as exc: - logger.warning(f"Failed to send nostr DM for ticket {ticket.id}: {exc}") - - if updated: - await update_ticket(ticket) + await _deliver_ticket_notifications(ticket, event) -async def resend_ticket_email_notification(ticket: Ticket) -> Ticket: +async def resend_ticket_email_notification( + ticket: Ticket, base_url: str | None = None +) -> TicketResendResult: event = await get_event(ticket.event) if not event: raise ValueError("Event does not exist.") @@ -96,11 +77,10 @@ async def resend_ticket_email_notification(ticket: Ticket) -> Ticket: raise ValueError("Email notifications are not enabled.") if not ticket.email: raise ValueError("Ticket does not have an email address.") + if base_url: + ticket.extra.ticket_base_url = base_url.rstrip("/") - subject, message = _ticket_notification_message(ticket, event) - await send_email_notification([ticket.email], message, subject) - ticket.extra.email_notification_sent = True - return await update_ticket(ticket) + return await _deliver_ticket_notifications(ticket, event) def _ticket_notification_message(ticket: Ticket, event: Event) -> tuple[str, str]: @@ -117,18 +97,144 @@ def _ticket_notification_message(ticket: Ticket, event: Event) -> tuple[str, str return subject, f"{body}\n\nOpen it here: {ticket_url}" -async def _send_nostr_ticket_notification(identifier: str, message: str) -> None: - if "@" in identifier: - await send_user_notification( - UserNotifications(nostr_identifier=identifier), - message, - "text_message", - ) - return +def _ticket_delivery_message(ticket: Ticket, event: Event, base_message: str) -> str: + ticket_image_url = _ticket_image_url(ticket, event) + if not ticket_image_url: + return base_message - private_key = normalize_private_key(settings.lnbits_nostr_notifications_private_key) - public_key = normalize_public_key(identifier) - await send_nostr_dm(private_key, public_key, message, DEFAULT_NOSTR_RELAYS) + return f"{base_message}\n\nTicket image: {ticket_image_url}" + + +def _ticket_email_html_message(ticket: Ticket, event: Event, base_message: str) -> str: + text_message = _ticket_delivery_message(ticket, event, base_message) + html_message = f"

{escape(text_message).replace(chr(10), '
')}

" + ticket_image_url = _ticket_image_url(ticket, event) + if not ticket_image_url: + return html_message + + return ( + f"{html_message}" + f'

Ticket image

' + ) + + +def _ticket_notification_payload(ticket: Ticket, event: Event) -> tuple[str, str, str]: + subject, base_message = _ticket_notification_message(ticket, event) + text_message = _ticket_delivery_message(ticket, event, base_message) + html_message = _ticket_email_html_message(ticket, event, base_message) + return subject, text_message, html_message + + +def _supports_nostr_delivery(identifier: str | None) -> bool: + return bool(identifier and "@" in identifier) + + +async def _deliver_ticket_notifications( + ticket: Ticket, event: Event +) -> TicketResendResult: + subject, text_message, html_message = _ticket_notification_payload(ticket, event) + updated = False + result = TicketResendResult( + ticket=ticket, + email=NotificationDeliveryResult( + attempted=bool( + event.extra.email_notifications + and settings.lnbits_email_notifications_enabled + and ticket.email + ) + ), + nostr=NotificationDeliveryResult( + attempted=bool( + event.extra.nostr_notifications + and settings.is_nostr_notifications_configured() + and ticket.extra.nostr_identifier + ) + ), + ) + + if result.email.attempted: + try: + await _send_ticket_email_notification( + [ticket.email], text_message, subject, html_message + ) + ticket.extra.email_notification_sent = True + result.email.sent = True + updated = True + except Exception as exc: + logger.warning(f"Failed to email ticket {ticket.id}: {exc}") + result.email.error = str(exc) + + if result.nostr.attempted and not _supports_nostr_delivery( + ticket.extra.nostr_identifier + ): + result.nostr.error = "Only NIP-05 Nostr identifiers are supported." + elif result.nostr.attempted: + try: + identifier = ticket.extra.nostr_identifier + assert identifier is not None + await _send_nostr_ticket_notification(identifier, text_message) + ticket.extra.nostr_notification_sent = True + result.nostr.sent = True + updated = True + except Exception as exc: + logger.warning(f"Failed to send nostr DM for ticket {ticket.id}: {exc}") + result.nostr.error = str(exc) + + if updated: + result.ticket = await update_ticket(ticket) + return result + + +async def _send_nostr_ticket_notification(identifier: str, message: str) -> None: + await send_user_notification( + UserNotifications(nostr_identifier=identifier), + message, + "text_message", + ) + + +async def _send_ticket_email_notification( + to_emails: list[str], + message: str, + subject: str, + html_message: str | None = None, +) -> None: + if not settings.lnbits_email_notifications_enabled: + raise ValueError("Email notifications are disabled") + if not is_valid_email_address(settings.lnbits_email_notifications_email): + raise ValueError( + f"Invalid from email address: {settings.lnbits_email_notifications_email}" + ) + if not to_emails: + raise ValueError("No email addresses provided") + for email in to_emails: + if not is_valid_email_address(email): + raise ValueError(f"Invalid email address: {email}") + + msg = MIMEMultipart("alternative") + msg["From"] = settings.lnbits_email_notifications_email + msg["To"] = ", ".join(to_emails) + msg["Subject"] = subject + msg.attach(MIMEText(message, "plain")) + if html_message: + msg.attach(MIMEText(html_message, "html")) + + username = ( + settings.lnbits_email_notifications_username + or settings.lnbits_email_notifications_email + ) + with smtplib.SMTP( + settings.lnbits_email_notifications_server, + settings.lnbits_email_notifications_port, + ) as smtp_server: + smtp_server.starttls() + smtp_server.login(username, settings.lnbits_email_notifications_password) + smtp_server.sendmail( + settings.lnbits_email_notifications_email, + to_emails, + msg.as_string(), + ) def _ticket_url(ticket: Ticket) -> str: @@ -136,6 +242,19 @@ def _ticket_url(ticket: Ticket) -> str: return f"{base_url}/events/ticket/{ticket.id}" +def _ticket_image_url(ticket: Ticket, event: Event) -> str | None: + waves = ensure_ticket_waves(event) + wave = next( + (wave for wave in waves if wave.id == ticket.extra.ticket_wave_id), + waves[0], + ) + if not wave.use_ticket_image: + return None + + base_url = (ticket.extra.ticket_base_url or settings.lnbits_baseurl).rstrip("/") + return f"{base_url}/events/api/v1/qr/{ticket.id}" + + async def refund_tickets(event_id: str): """ Refund tickets for an event that has not met the minimum ticket requirement. diff --git a/static/image/ticket.jpg b/static/image/ticket.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e05d9318a5dd38ef26268c0c516f0c057a115473 GIT binary patch literal 106602 zcmex=FcP*_4lP)J;ek6%9GX20;#{yNnZ<83h@b1R0qH8UG()kY!+CWMKw5mjMPC+1NRlSUFjk{~uwH zVFXLVq(C$aD;ql_69@DETMRtRj0{Wy%mNGy4Er4?C^#H_)_dX9(9S+Qs4;V`S;%=_NP?3%VJ;O5WAE580xzf>DyCvhaBlh>v9 zSj5Yqg&69nrzwabuI@Qr#YN+6A)oI%;pwwOs)8Y1kcJAo&vUD ziNjC;^3_)fw0U&>>G_Np!zI0f z8?vPNZ=@d7bklvgc=?JfL&*(l_sTbh2=H__Fo=i<2s1TKWME-nV02(%VP!C4W=dgT zVNkSdTese2(f+o|N#ECRuhEm=qwqk3;ivO+^&rvTl2XP>uPh?dJ{?WJc=_x3@KEVR z+MAzNZ>|@s;B{bNWngfKb!ARuxZ>L&Aj0dM0E#LG2A*aCW>@v>OJ74(^E6NDRxf?q z_4ul(v(VxG6fPm<`ngZmD1VuzDf~^Xxa!E;@;!EUr@wh2w(HfpEEl7Cv(I~{Ni{UE zIWTZ=NHQ=aF)%Q&Eoxvp#MHo`w z-5Z&ZWI;Bj&xc+pSwxe(?T` zZ#wx|hxV>DN~>~E(e0e9d}{xMtoZ^jp7`DqeR(gy{X38EL*LT8z_(qREUcHW#;&hx z_ujf^lFX_#X-jgy2mR>%tJYUDOMLsINz$qpzVsBErmlJSs^qHAw}eGGJbP@7OjEsI z*?Wbj$7CJ<`TJ++zJtrs{mM7qyd379<9JJP?}AUMjyi^WW(S_pc8|GY7Cdq7S6-Ht zi-cZiy-Hk^{_*cj@v_5li>>2S9&}mP?lGUN)mfYV+N#TMmFm9QJ$F7X&zSe1?FL`w z=I?<|KOXtlQh6kI?&6XO_anMKHr3|7V4aiYsc}dy+u!EyImwiwv`q=3pE`Err27Tz zxl>WUVa~~n94Rpdwyj6o3^x3{bNyEInqS^Ft5(!*cRej~=K72IH*X4*b*#@YnI3Su z&}{N5&eqJ=r`k>x>F#4|3%VF(wQuo^jFjw zFO&MQY?0Kl!?~_1X2#22_|>Y;I5+2hn&t{F2E%71j}NKlR@sO=T6}6-X{~3frQZDx zeF?drohgCE&QD%7#XgOGtZ_wH>i)G*)ApycgYTT#?x>yFvF?V-!}DeKo#m|&Z=#Y8 ze|o#*;_QvPl>E+jz4MR#wQAXKbB+7QWuAJ~WpjREEl;Ryy7cqylcTdP`>zPSJZ<;N zRZY^4vGP%0#P3PRRL=WVayofFbJ`a_=VYtAmglE3r@TFTEzWHPL-O|d=kx9N&suQh z%jb@K36;IB5mu7MA-Z+p{jYBM?@EalzrEq5*!ro)Iu`#K8r^$-pZQj|@hgk!h0=Tf zbY2<+7Qf(Mx?1w^yf*RVOuMg@9$&6r(dJL&ne z)ym1S4kws1lozj#Yu0EFmrLIlV|3%r$$W=!MBS{`Mai)|2MpK5SDyMBPv^r`FpH)9Q zbYJ9&ZDhpS6|28ac)3e>-pRD!^X50B!^57(EbTDZ!K^vKaGF6|xTO#G9L0tcY|Khc z2@DK84}BUWc@!9S@H8YyGCk&;vqB*$b-iTul}$SxZ55-wh`QMA_-y;->D1#NzFyoe zF!3he?1h#uW+mHvE}gM#6-TNMTfhm)S&t;Mq$L_SQhhWO*c^Bmm>N=jgC&g=*iL0h z@`e~1Pe{s(8lO&np^J`&a2)W0_^k=l3v!d&0pj z9;@zNoNgj|aqD`=ki)C{jqjI<{aCi})S+W}NAKt4`(Bvy!`pCP<-Rj(r=3lV&-wMj zEP%K8`laumbAJcv{c7!0?e{Looc3~WjcCgC14&$rYi~TieQ9!us1#EJf5+#i9jo5# zs;#~(nZ?kUm~~tH;<~S=uB>%n;AApZny_e-*YAw*EXGyR`O?$w*JZqj&<3T5742dF z()YzoTE=sw!Ypx9w)EvG>UKhB(&y!GS{=JJGW@sIp3+UbP8qyRVNJb$LR_h3SN77i z(Hov@d|Ab7IPIaZ#;0JZHuVeZnWkKd@d|jGTt0I_aFpYPD{*d{Lu;){H(9?E)VaAZ z(tmwbqSMJM+sdk*{+6z9Q{QmH>u{6D$4r@a(Mi{%{kGpVU$wSMmlnNznd(PPjk_qgDDy{tRlEBdkw zSI))S)Wx>q&QG!nS7a~q?eIF#ofha-T^%g>dS2_N`|h<{?>eo%v(-j5@nG7nU&Xoy zmx(<4wbb1Ao%s8izgWzk|7Y+kKDIW=y4mHfQHxplG!Es(uj86E+OG4Z@7J;Y>~pxZ zbi)0feG%_wEwp?$E!n2Hc4oBhszdv3UcIuFWBP3`ue;CNgNj$R9$%O@>upxp8A+46 z*k+&Hy@~}ZyJRnw2p3*mb|Yxomy=AhP2?jpj=NrXm0g$-y~@Ai6pvuZIc`uX14n5{m_i3jda+{t@%kzU#7#p&}epZzU0;c4^yz1HWgj&|mW3KpLd zcp3GesmgB43$2dQf@iZ|9#&8Ks_k{X_2ip;zkoU4yp85r?mNA9+SSDPH?Lkrw|Ku< z>YH}q-K2#@SN6<#S$(y7*2;vF$(LEg5_4BdcNjYF@@vYo+Is7i-`$xB#bMQ-btPY{ zk}atHdgEZm%hTaS_a6G^S0yqQuMl56M}Hg7BHK{Ctoh23ZnE0jzO){E?YGo7oZWs` z`MlC>!8=w;FQz13IvnF@s~BA(>0-5`*!I)&S*}ZO3vtJ<=x#gufXTLdh zGdE21*SYm}GtQs32#$QVbwkcw`^-yqEsG<9GQJ#SopC$M>4DF)_L8|QcT2OkiOk(; zc;MMt_Gezk7ekyH7`wfFqkccT&Q)1`=|#>0*4NLipI$ogHayR29q&ek2HVH$r^|Kh z{Pse8#&M9-OtXdVc*#AVlK5%E5z&iZw>#QC+`4~O{%+pt%LP{>lV2>(p9ZRZs+Y_- zZ*xB_>~z+K_0vi#7BFOQbJ_Iy^VF~Y>!S)ARr`GmB3rjD$x}6St^xLmS+sfH>B<+tfy_>yF z+d=h&&AvMqzYC<#-)nhZ>S(8)tYGmegOiaPSxZVYFN6yGdU)ph=_bG9FGHqo=9?UG zZtguGG5g_1u-w8Zn` z()Yz|mN>IN`Jnb){=$fRa~4{Dn3QbuxprpM+FhVb@?O{}mlU4X~&O@`axLVsTl@l#X48?ttuDtuc zOg!d`^>i!!*UOhVhUR506W*|(_-bat$v{g}>%A}6cDc&3Jmc#Kc>dhK_}tE@=vJ3I zpL(pqvOslEiKvU+&SKlo3GZhWNv~d0%ai))*q6JUuw-~0k_=UMxa_(aRQB9vSNB|T ziyd1`ug2+~?-y3ay^$&KPZ zNmHJEU$)8cjsN>Sd9mi$;T2v(f9; zmY%z^_KvS#aBRJ4!IqsX4434TPP@N)mcOpm)Dx3cUdH&BbxsMdcG~sj=lk~_t9|y0 zomJ{N5j@*K0)i00g_xhB%@a?;3&fD|y zl<>S4&y25IM24=rwPy9#xp6s@G=ie%_zB4rJS`VwuwrWNg=M$sD6ufEP-d%JFTozThm z4Vd%Y({P^UAz0P&kye}>$<#|ImPFnzg*aDcjMmNg_iI7l5IY>f@=_& zxrd(os9ELTekx_fl=-*j`laod&9;DnLEd-kthJ}kKGy6#cvW`Mxo(f^fxZsB4Hc&r z7uW8L*!1Xj*6dA+Eil~^rO#SLSbG1+Fu-tq+=|X{krd! z{MmMPT=*%a#~b%rIu$>ZIacwU+c;vQ+(D%|8+SFa?<{hcer$L2BwypqtGi{Y7?};` z=4GwEu>2V7(zo^f?VFfp+}>^W>b7|D^PLm;m+zbW^m6czNt(@?t9^MiG%Qut%)VZ> zH#+LsYvtp;LX#OM3+MDY8-%-u-DL^nTW52==7#sPIbGV7zrEyDHr_RNDt@SPtm4^g zW1ZJKs&k4xB#rM&Jhn5@l3vp~ss<;$n`?tOXU7Q1nuf^fhU28LT!3Wa-(e_dOCx+Za5v=6_P zntG}+OG#<&g;arCkI#I+ye(Dgx>D$p-n}>8&0c8vVOp|vaqY})rFl2>UGDVTCN5vp z#8mZi$;55d(jmWZg{@q_*1vV(i!P(h7s|snt~|ib{o8fseoeF8+_$g!%d}+ORtwpn zaMgSE`ppasxp$-2EdB0%bzk}3I?lf$Hhe3-zRUc+al6i*ZO0W-i!7G#>{D4~>dRLU z?jAOmHJE>$%-NcEPu>KZ&W}AH5_=fjqc{{f<^9=fQ8RcdV}v)Sui?3QrF6M@YOYsQ z&A(mIaaw!Ic)5#AC+Ao=Py9KHZ~0nM#Z zJ+94V`Q9F`m%eWe>qkG=t#`gIy_->d zd6&qySM4^IO8U|ruUECXZPWU?d=b~FgPBL4cyMuLy_#%r^IyY~WC@u|EX#B&)#|x7 zh^q#i?>(VyZg= z%DwEnVBUnazql4j$NZf4tHL+wSJ>BE9ha~6y}TtnZ{@P!<7PLjmOpzj??sEf_Dr>3 zI+-$$q|7V&mfq6(*cH|HV($8hs~($eUz*{y{G9ndJL|R+k`hyOX2`tGyLzG4Kh5Q8 zZ^NUB#!+=Gi%op}tO-w^fR+m=pR zZg9VU-anC3wX1?_{`}ScYo5J9VKalsb%U*Qb)By2W$c@*#3r}0#9xqCc}uj{d)|vS z&VM=Y$n+RLU*mQ2#&-LIv5HY&cwNlE)m(m6naTR+Iy+oe9V>aud49@d$@{b4Eb}?G z;F@km@$oghuT74>p8Idv>~yz3=C}QhJzJ2nb+5M5m3LYDjgGD2;1b)sL?d_B7gOcg z>%V*dR=JoJw(h1f52ydF!l|WJCzxZJu9a-%4B5HtHv{kP+i4NkBd$Gs{`u>1yD0aL z>Nw~4-01sphSGVQFS)KSSf#l2_{{gq*WA-wm|1+){hm&`?}a(Pd=2OB+;t}S{Eh90 zQ(oQw{PD{twV+*zwT9D5uWq$qoVj+b0!ZmY#mMz>Mo$mIZVV>D(iR03>d;ipW z)s*IT@6iaIH6h;P+Fj=F;7VfKCpXVU7uO|d={?*y*V3u@rOv6cXRmdAZC_8y5np`% zw!eM*w#v(omoHn(^nQ|mDua6Tq_wFoPd0v$i(04~`Ss7utZOsh_9m(NMAWQ~Xv}Fg z?RbCpwo=urO7_dwn%E*#Zj15ns>o@5Wvz1};kWk3o9i5320nfM;oGnLXS-%y@di2P z5!Xwp+Y7oBuO6Sde&w%k%adow-0Wwc9`SD0f}J0xfjaWjvR9u=mb$Rns;ai8;9TAA z>=%Jg*ZZvYIP!X8tHJxJ?#sOWcC0NCy|O^+_ne?e?|akow^(}!b1a{H{QlL~k52cT zc)%5Q`@{A%!7-b=EiY_oVfBrAdyRd9(Usda+j6Ea{!#Jkr;Y7(=ZDLFUte~t#mOss zjp!xO{f;7sv)hg1{ZHO;+;Zyks)VwQdrh5+@9LbYc=lUgx3uc)V!4ukz1PI2@bCLq zw{)MxV=FiH1d%J;%C(mTb89UV9Mc%r#m&0CZr1Ztr=9PX75!ZDir@H)oSfXw+*PmG zPGze{Oeo)S$T&{pb_45`;Mm8vBVVpRDZhU5@jJ(2_|N=jsIQgW@X`oWfi8Enefah8 z%=L4R^iE6NlD$HxXFIra^V8FC?#_K@Kzf66`=_@rZ&W&Cv+ZZaZ-xa@kGQ0_Pdlw# z6Kr~K!uHz}uOH!Uo+VS@VVp4~Hu~HrgJleiH#Pdte-HJIe4)KM?8c*pU0qoWxnI72 z{9X0)*A(?Ht3Dj}{nFFdxP|}X)%A{3oU{8)?g!s}9^n2VbG8t>-p0Syj>UIXj#WPU zZNS?-yKd*k0*)M}Sq%&f49wd?<>tSun#Z>?=b4I9SlhuCqXo+rqzJKmQZ$Xay0l`+ z+h=ETf0kZlbYNiMX=C7dBXi;PZLt}k>UtN)GTG>#i~iL6Jv^JHem0tUZU4(t-1A=`zUugWntEK-Ta5GTx6chL zyW_o1y;`gJLy0{M*dFOY0Q6?CydqN|G9e+uQ`06&RRiIPffU zJ;toX!M3xh%5Ccl^F5%R-dF2;q0Dm9`UwtdV%E!E_*E~Pao*-`8r#15d>*D); zGZI)2emZfq-_CDpZu{5E8J~h>u01jP}g*n+fpI?^jkH1lJEr!4P z{gY&cl;pVFRgFHXCrl1HNo-i~u5wBEn-qf`=d5{4R>@7xZF!$kZhA)alIe0s5f|-t z<9H*{MxM-LPo$QaTT5TwqHZU6CcUpF6JwD-Xg$(fn)BrCn<;!~sOHcg$a<}89Q?FURv}Rv0HTd=L%=K$mzb=d0qP@au(zZ9{ei!Ea z@HCwJbKfb($rq+f{WoF5pDky#66!Af`nmVZt@|e;0xXwv&Df^=Ue;*7(;dCHf!W@3 zV?G2eRL<;un8xvJhSJ!f@qqRr%opE~3hug+`I0vCPDxpyteJ*E=4w$O6p zU2AC0eyy*Q?YhgW<@V#0V^RBF{`vRM+;iruzaEPp6nhC9<`~#~IakkBu-i1~nD{RH zmk)R4#eOSty=gveDPNxBv#1k2eIlD*zu(y#zkb?+W2Z02O<5Y5G*8OrpWU2OD|6+G zKl6L9(Qs-*d5Z=ZW?QJ$ueqDGUI-L@A-GfE4dRloAy_~6Zs zC9}g$JOww6c12G4eD+&JHb+E2zy7*j_htPpi|jA#_qSa7bn)q@FGFYCTPfbY`j@iW z%iczAkORMouV~sl-)Dp6Qs);{<{QqxStWC~`OMs@sqIpy7SDLwF)LeeerV4L#qGRrXWxGNSYWeF@#&Z&+!Laz%C2PW zH7z-Ipmj&=td-fqcN`=A(<)>xE&O6Cer<`tP?A_|V%|+54#`t zUG-V|xn217+Sy^ZUS-`#zA4~0GeV%0L$LAV{C&bIZ&q?&Jt`t{?0eWMZ>_*~uY|t- zwN^aKWMeAl{h6(|YT{n``rhowtG%GE*-EwG`{p;(OdH>GOA?5s6dg9c8Akh} z*{4Uj3`Lj|jC3dM2U##fHX zS1G0(j6Kh5^<`DE*z$b&?TvHVZ1%-%S;cN}YuC0f=MFcmD>$BAl=-uoE#>Ih->sPo zj+vj@92_asVid~qr8o0Jd_`&Dv+CCwzpm;=UcIC0b=%B*xrSf0%1r;5`zZ}aUq}W0 z+H2gfpn)Ng=W~&|)@Ap5Hj_`}dtXt%v%*J`!LZM48L!?|?jti4KJzu4f4J^?+5%>_ zq6DLgKejuUM@1O!S*4#oC;0C3u=UGIjU*WQFRXp~e*4sxrMf%$^p%>d%(lKTnvq-d ztol{fu2r=>wcm`^E-wQWpf##9&dc0S(_G8;(u>t_7t;fWUUOW%dpPu{ zvBmPu$FDOhXto-^`S4|DiS2nYpGBpg&aVGx)R4|*G*g`ExzHW8=!9%*w%KuC&aI6q z<#8;KJh#`WS~c{>=B`ipN=bX9H1%kOGnwpZ;d^V`vn2>F0^ZcAyQ05*{P!%5HoZWAHKUno_Xx7bn%3SBF zlXvB{Jl_@B^YQGrWTR@iHFr}3Q+F07tz5jE_vy9sT5rC;7u{K9H90#!nRDUvc+b@B zUV+-7`Hwwkt}6TWm~YFDEUnv5Cl@|$>sq_i?5*T$vrh?A0$%c4{yNdI=XTuA;)+Le zyhLtyTuQ52`*hlUi^R*9ax*_Q%kE8Wdv8^&8T#qQrkaJ>dsm;d5x)3!y<_det*2+c zUm0|6{o@-o+ar!WPyUtH^88RFXoe};tWtK(-^{=#Vw}@YCq7-g|2SuJiUYTjs-?lq)p4WES4emem zmt^NP{yx0A-S~cV_^~x-x9aqnc`tjBm**k3#$E09{*r$a_pTL8ys>f5Cd~!`fpcjF zMmu;O7Afd4t63a#J66!x$J4;T5W`m7*t6|Wa_WI)hCGdojE=_Bua+xL%F3L7(!ICz zwO0F*!ew38&x)VB_;uakdnI@4m*_r!vd{SAuGd}N>>HlVtJ%oYIkzD=gq4NiCZi7< zLjY*L?Z}A~R#RuKRE7iT0RlH2odp7TG#hTPu(0xMT%&!#^zHUJ*X_NWPA46G62|pr zMeKXti7jE?;VW0WoxS-%N;z|r&z|LlHQ$ckK7aZ2`-iVT1V0Rzp4OYTvdDH9>#lSW z>0?)av70>?4xU_kb$8XR$4iRx@0#mh)pOn8w*Jsc{hdbr>f5K^_ua@p@5k?-`Om(6 z{I>l2^;Q0x+*XHeRlCV`*SL%Kb#im<+5I)Q9xo`$y}xZ|M$GK(XIDRn+q86jYRvEG zY{|RJcdk0A7bwGI(dDW2)G#~ZwjQg;yNV;bPi)UWol|A=?Q_-7-!`>-w1RVjw65uR zZgpB8uvdA(v8^|^SKZm|_NcwAv21RY?j+G!PczLlr~H_r{`E)oAKRMgzGoiu_Ai_L z$F}D0%D<+sYHT~i%>LAtbzIE~SH5Gu?vjP#-RSdJw+S1D*_Dcnioz~u3z2xnA_jQw=I|keQXYkjb_p|951FI=-bAW(|h)(KTKYP`W zC9Dh#4gm~cf`vguGO$uLYr(o!P}`OxXW351mCRq(rq;wR6im0h^CKgId)4x_UviJH zN-f@8tM632EooKKQ|0SrkndF_8I$&UzZpB`|#@FneW%GeqH}^-K{ovZSX{2(3$kQyiGGV|M;8n z+NT<%QgIJmd-eL=XR>`@{6y|U%RTh^-=Xj~TA!DE|E}k~COd|SwSS-H^*g(? z7Bnz5FfcSMXkY@-3r==>X2-G?H%`czGvRfB&MiHcGm-q?RcD0AJy^2lyxMJkuTQ?L zCB2y!{3}4M!k3RNI!+m%$gN+j#|<74pX9SVE^pJ#%|E_ozRv2p_0jguh1z9LI!f<< z{BZy6`=_5id}ZL4uD-nfWKF083xkLNLjWVA0}CsI062U%HSN42a$Uc+E>3QKeZ0o2 zc<)^|bgQ~=e>pqr$?3$Wah?nIOuYn`R^3*1m#u^h708?_ea@{r z+pa%s-j)?>_Y2=WBk3z_bojkLm+EYBk*KnE0TFQ2s(uV$U~~j^_ZS&iK@psCbjj9T z4LgH#n`2im;w@60&D!zE($?rpZ;UAa_LsBGPnI3Nw0F`$TjeOwocZ?8reF6>GOE54 zuvM{Wwj^jeH^^ssUf!mgmw&v?dhOMB>!awzoNV87--6_WwYk>*>ASBe6xK@9Jsq`=4P9jIX16v>Ij3!>p?U5@qUulVsZC! zcg*QJ{^cqA`6-(vAJ5h~mh#U!a@VEB+hzRA440bN?B>~TvvTiVugkAzPgl^0xP5O< zo%f>y3=0?-6c`w|7!))Z7rM{^7!-_>E=65;jCm!=-1v32&$Xx9OY+*|wcJ~Z?yi`-+b43K@3y$9PbP7_ zM+Uc#*FEnXeG{$@+41E@%b6rchk;M_fRQ`0VnVCnL$$${ z-`AHag;#vn_FkJC!_?Zd_vP^?`*aw%z$Mv&h6N1^m>3w07F~+E=@_fHYStSW!5^1a zep}d6f?2{gsw^=;4?%zLu z&X(MXTvL99R{XKqYb*E9E+mVA>3F=z<4>_-4xp+BRD^(QAqGjoOx2tP>slFXwI=_u zsoiUL&(5SvzIexEA$}dxQ!Szwzpi)q`|vAh;5BBK<%3V(EpjaX<5s+`e=z$70|UdAjn~gw{GNVv3aFxklv)k}j0|i(kethEAi+Du%x~>CyPwMv zesx_;b6&E#iR;C$+Y7Q3LH5jjval(Ai}tR+E-_{1%U<}^sLnX|=YE>ruJ`g$+x>le zDngd}XecnSMNQNDu{?e<$Eqe!_0s@OH(U%1%u2edYo~~wYgmxOkj1EZ)4*ff&bfLs zodv?(!~QY{@~x{mUmL;wXm(e2H{)u)NfG6C@B7Il*;Hk> z6fpcy)`|Ng=wX5=&fBcpCLOO|oVORI-CFyxC z66HDUiwjz=E}x}8_e}4kj4jzSn7VhQyjJ5?@Li;1`J`&*>Z<9MT{rF>o!i?WV5cRp zId!Yr*=-?D`p(av?6IcNyyN(V&bqgEH$GMGJ2oM5POnYdvC<>=d^au>OwG5N^hP6E zPgA+fBVr3L-^R7oW|N+Om>)D>Q(mb5`SS__(|YL`|W>I)@d+kRLr?_ z$ksU|D)i#aRV*Ln9MuY2vu)<*XXUNB@%EyzNbh6z+DTcNIqfR_-rsZm&v! zcD8Z@2S=L0rpnWKmo;mg^*p7D`Rn|Ya(1lgzr26?LiZ`5G3Va!J`Oy5)wSC&>gyHD z;2Y;NYtmoOx8>$OT)Xjl$c=5m8y5U$D2D^WlweCjmR@nyY5YBo}RaN zGjr!0#R&{C9ErtK4lqtEP|$PWVJm3!;hgB`%xozU!^Jt_08@&AMvZ8t+ucHsSg9!Q zIIYYprdgk^&%Z0w*!W>@z0=am78CQn@FlJ7*qy?uwPy0odBq#k{1z-+)zW@lb`wL& zmD6gL$1IFbENIxl(`GbNSi<29gM&{OXhxW^f%QbPS}D&AL&M}5e{Sx%SmqdH6}nV; z?e*m9cgr4lE)|ygF26#-sP@~_+NSM?U(OGzUHhCbZqwa%wlBYaGAv+hG-3-l=FqT1 zK!m}kjah-afmuyqhY`aeNuM@nrU%S2Pgqa(Epd$YSD4-0|Ka}O*ZcCH9LrW*6}F&( zLFVfWt#$qLZYGQE?$zsk6ML0`r-8@O(OH1EIg|m^>}6$TxXL?0M4ai#5di@aqXRPy z8E*PIT({Wh6n0)CMdNMH1qS6O1I7 z6&x6O6t_hlN-$!awuo`UiCGPLE+=Lf8F%(XIA z!CioXk(rr+jh&T^g@uiYiJ5_!iG`Jck&RtIP)ImLL{v=N#MDedQOPJs+0ZyRG)2Y0 z!9~^F(TPJs%{4GJAkjHl-L3HdEd~xoMg~S6#_LPlJ9SQnz5M+(xokhn_x}tJiQgfDQwC&2uv=2QzA75>E*HnniWap3hr?x_|;D*}w6*gs`9W|}o+3q&~ z{aeQW{p$_?`_~ig>tkE~{w)(Gz`aUXal(HFgjx3YuY>v6tupV6lTg-|@6S0PZld)* zJNMebkBj3NZ@cEn3!j{w8pu?2?c4n;n-o0nDXp1u=J|$;oO^e<&YiDvO76+J^1Js7 zq?%MERyf%u^c|84n7>;j_u|6#(&(fmUv^Kp75GTG>-M7W+o%3Zu6m!Z{pUb8{#WMR)aXi*Wz+6Wwmf5}gybrdYMU zd;jJgo8H}*y>vz8kU%Hv*W_*QXIr~YyYk4YC|S4oQ)bka)ztzz_PbP9*)E!I!(I6I z=bBxHb9^6O{IHrW^V>PuobUC@`CrVgZohdh@{M+R+4kLBGvs``He3BuJ+UdI__BFy zTEX{?RtGqL)b^N$`-SrFuD-SDnS160``r`P#2v{=nz!y${2>;l6RwkH=lH4fUT7}Q z)BScU=9JZ|M1eP=`|e3uU7UD`{r7){ADkV(J&(`a|9gx30!?hEI=eEZ9^ zZ?_&Md%A5AGz%)?DcP8^(=mJPygj0m+h_WuyKtS2@@4Sup0H%=j{UW)3+L2vOj(u6 zobtzCe{YG}iEXcPH{G~rT&k(}BygjZ=edhL8-mTaZ)b|?Utg2_?qzI4oDa$;2?nb|R9QZy;XUScat?M&mbkg^pmRQZ|_i(2MM7OVbanNn?FzirdnZV%v6NTGX0*-Yxq3E% zH+lKa?l8ZuySZnUmc5;?Q`J60=5*-{jVRst^83DXzbu;G`oqh~W2$P|rpd+}ujR{T za!g&Tbg*RAwM>m4e2X)0m&_??)D&B>Ehoy2_0%E3E9<6xJn?p0R_Y0%de6%{?LSOhv75m;@xJEL zm71G`Qx;wGD)l}c5golP{5aF?rc+P$-*@?AdWBi>glFUK-t7{G5zmeby0pH3KJT@` z8tW6zfpIHb)9XT{4*pp(YvQZD#rwSeZH#EUblfT~GJLa4WlwgWb;!CS=b1llHg&SQ zdvro*nAEM~(>IDnK0kA8!NzI&j~bpH3NLbZYcenwj*dGq>vQ@R^S#Dgh5Kt)AE+z; zs@Z1{5Vt!j{MfO@jv9ZHN>gJ6H}*biT4=KM&HP>WeodS9_}+t@^_vrJFEW*~zqUSG z>-wg>-)0_OD#CWAZ zN#Whn86SSl`~9E6*7{53YSC_%#hVu1`+DJGcbdkX3=vMNdrMx6Ub^V~=be-F? z!nFCPXV#Vevha1~C*!YFA7682_i>HRFpXc1H_vB^PV_wMyq|Z!k6g~-C+yqrH*VYa z__+9*-@mz1Rdv`_NUP2}(5Bfdb~A6?>Y0~QcAw;8{j>JWGN#E%^=&`I@BLfyXY1WW zyDZ)c^{?wTAG$?_T-Aa@kq^H zs`RDdYvKy0#5YT7KA*p1GttV=d!@+hi_g}GAG7*4$1G&?>;DW}H?%b@Iw4ga?DEI- znsV;uwOi9O_w)AtXSgg-ByvdYl$LHqXyK-SmGWi9PaW6j9&nX#nXQB$T=K7qJ z6}6`2uGja-+1&ezw)d_57Pjqt>E1cdk8NA{%b_%@X;Q(-4*{%iTpl}bom6~Ws6DIv z#p5u}$FbGs+iu6TwRDO(>29=|`rd18K*Zq_Hw`VV4wr=NNq9Q*4(qb`c7OzuRd6LNNzr>xe2iHUum)~#NWJZJH$mlDr4UhQ(TJY}`o;&`Qd=q1B* zfmaJZ-UAb%S4(=zrvS8^-_t4|VE5jj1 zKxN<>kArk02(UFJJ>?+XGEZf$dw?{8rBP+z*4_)eS~912N@);x%I4_RY>r)<_e))C zf64f&x$fcY4@+2g1=^f=t1>6%(JsNP@)w`4u|JNjH~wqCaR27)>aweIe_Y#Q@4>e= zAgAx0gRD$>RI!|R@cquum)Cp9ZLpVm9ad|v$d#tqTX=AL-twQX-d_1`8@p@X^B0nT zUQFMbCs3VixkK%G+PaGu=Y<7M_c54Tadq}S3F}h(^I?LMy-uH5!GE{VdG3=SgLAW# zERVC=?wbGn^>*Vwf6HFg&tG@f|MS$|Ef0L*_Zm} zw%WV7@7oO~6^pZk>u?@2-MsQ&-R@HR^Vjp1|NQm#%6Iz+o-Zwz?%(qGlWkog#@qGq z(i=nXOvR7awz#V5y59DDf2A{a>-@j2CC5FAb@Tr?3Vf7!=Wtswv)Jqh=Y0SDt=iIV z^P6MAP6OHhtQzdJtJ1U1$+^zx_+BXrN=W_gi`GWu^RN8JJNxr`zE{3y`@GkhyubK- z4gaxSSC{GwO1@dpC3R`zs+Tb`?NKf!<^SGXzVb$hH>vu~!+%Tq z*ZgO=?EbAJ^2esBOW9xhJ0*3$ao^Z>QqQV#-P3=(nsp|ze`my7^j`&8YVW}xB7J$w z1lHNpUiiFb=XnyN^JrI~-(R_`1AnsDMg@NqoVD>q^!XECOlCLweM#G}O_Sl@QN1fde6{IYD^t}PI_**Wi8#ukRM%nWOe|s;} z({``V|J9i-an!wc<)yZ#dD@=)a&Cu7vT>e_?tOQ=+2qLM#gFp7S;Vkjv7deXT70*> zvbJ}_3*OxuD$g^`;a)uF;NEZd7JO(IE__j;t|S!k;^}4gwf*Idp{p%Fu3b?#L2iY{ zcmDZJZ;$WkIqAH`_RaozbE|BwHvYICwPooeWz&r3SEf|gHkLk+n~@#4eC3nUjQdx- zu5Q}IZ!_`g;_UiHp|z3W%buD>ZP0$7th@Ck`(^ek-Ea2tKk?hE_H%P=l*#9I(RsgO z@2#je@4v44OusZiZYJBF_n>0cR%*jN`QY2d&*zk+8|CE`#g>7Ji`AgwBGdbbTh_(v zD>eQzG{jc$FO#~K`KppP%KR(W)+ch6%B$V3-)8ZJ+RI;Na64|oUZYyi-!ECk9@Uv# zm#CjHYy19pYt<`v$40bVQdAR2{!z3&TkCSc3+ZXsk0e}~J2&Fq6&)i1HOH3og;h)T zoP4)d{#T0B=k-#zK-syqtZWnSn>(vqmp{H^Ji+Rm?ZsQjK~kFW_{vm$gL9@cuRT5R zwmR$R3@tOp37qfZGGA57g57xj=aas*(v^aysd2NXTu;)w_FXe0(pOK)!Xm z?|oe&?;{^~y8hACcmElVCSCu}usrFt$A;B42mBl6KFT^;EbJ(Bpm@s}DJ$-0FRX)9 z7F}0;=3koe`?A7ySYqX=p1%Ijvcvb>k~S_=dKrJ-Z!a{d2K}ix>lu3a)4i9g>z{{- zc68q7bA8wn9R_xt*;gTG2;Or62cY)f+FpsD&Z{o*FY{mj{%X(dfAhN6P77V1TK#tV zL$1^!yAIbY*TXC)TOGfX>war%&!l{vvwSo4YoMW4l_K@I9};SZ^X!&yow&I!^WvP7 zJ0GumeFdIn?T`0duPtbBfcGe{YTtKJn_}{uQC?6+WMojWYYB zly&N_x#oI~_Jr4RdFej0XWX6N`PX!*Y1-XM=8_G$7w7-}xcKTzvD;P7Y;#QB=15<9 zsOVfJ`s(Ki@6~~s+iUF;ZMgW%3(jy|IJDc~ zrhM(!1o8c;x8AO~d{xM5OMd>Z)~HY5Opvdnwr2U|NmHxjZpY`YX8iux7m^L;m%ga` zq8@WE;u!0J+j}>bH_mgEzkBJO_NE!hDoa(H7h3F_@bN-R+0^XZqalGJl4joS_g8tJ znRo4e*{O@)mo=`p`nYyQ+yuE5X6)4p+}mZZ^c-~d0cX!&HeX@c6OiOV#L^jIe zEALlG9@z(pVN+f5oB&B^?tN<`@}39(k+*rg*m82=ai!_m!RtMw!1dLXJuYc$WvV94 zsbd6{4!M!bSE+zX2d?Y(&nH=J$$jE-b;|FLC%yhNMEURfoz1ZCPUhD4>pkpd*lpNd zaQ}l};Dt|~g*B7(vKi)0oL2M1h;K?srv2GjSrf|w>x%+2$xe6re-|4KCbgv}@Su--i#vzE`A`o;L!*|l2^T%5CLzs%cK zr;By>k560?G4YT)?*xxUi^C#?9%V_am3&v|-+a#e-}z2IrT8s-e|eiTU0=*^R2dbJ zvi6(4aDG!mk(rhk>xz_WonP*$^R2X7uGIHuuJb-K@72Zs49gtBaaq?d8?{hU_D+YD z!IUk}V(gah7k<7xZUWTJ6&+`N;&=9%OLk6S+C8!C-+zWVlsPj-fchCop>->E$Z}Nqj9Z8J(eNj#{Mo~g=m*A4jwNtLi zetsSGcDAl3pU}s!-^CmuJwmtb%(tu&x^2gPeQ_e+#UE^fnrD>1MPIj0y=h;Yv-sHN z;_XXJIWy1kvpvrLP@{d|QEuUPS8Jo0p{i1xX|7oxtq;vQv}94p>IGh9_g;jquhzQ0 z^=p#h=}Gf4T9#*-dj6bzYx{qO(#5ae9&26?u3Y-v)|hH+vE6Q0`Td-nr>uJ1C(Zkd z&qm1imag`SQ!>7hb>_Wqq6ya-AA62HSr`6=r{1}IM#{>~_we&oh9ak>yZS$@ahe)+ zTX)mF*V)@vtjWDI@93KcO!hL#@|(WNOZ2NRzLC3ej&jA%6TuH>tu5K}^zQo)=3JSQ zhfmkMJ+O+cM74U()+o&>f2#j86!^{7bCyX|-doDu5*d?wC+_N-$J@X8o!mX;-Z=;5 z$a8`muYZfQ{k?IlaQm*{y(dYbaG>9x*epP$X@9N0RIXnS(-$XAGOpEFWjm^HMaXc+IoiWd~>qU@$wcd5#uN|v7g&bDBh_js(<#sLa zeNt?y#CK1pc}DKHe+%|V?RC_9`AJH9(`w!QO*I$3#3TjSSLgngT-^4bp^vHiHn``p z_;}bF6AvG^Uv@QZzK5Sblbrf31l0B5Kep@IHQCRnbMvlrc@?Iz-A=i;Jw1BHYx@~} z^4eQ;rK=Y&YUnZxSMknq3=)~S~EOrt@~Z%oF}*sd9JG>^$}(cfF$4 zExlhEn*VQg+WFGIw-!d1cx+qrd$pqOJwJ;(-!FaixWvl2V|z%o(s9-1_i{@#okOQ| zzgsOc>vh`gr5`O*%pD6a&A$D{^xn3d1t+c9AM7;JhzyzVc4MmN+FNStY%du)sOB7x z{m-!Tv}nAr5o8LA9#Adh zenQCfxP*q&mFEhRO1F1B&GcGddhdJaxBIbIQ*J-nJ2~0cz4>Dt+x4^$`UjJ@hF;Ni zV7fDtQG9QrfMRma{3${D(^WpK<6g&NtCTp)EO)VS-m}|$f|L5Y+@yY2$XMO{@W$8p zn0#A;d--9FrE^L=j=Qd%ZQF7Db#H|^({e?gGr5ru)`p66Q%iV) z3!m>hxQF-M?3-b?X5adD{d;`wzHgg1>6#V9>b;yeQDx?v2`+T8kSFVp6}Y0 zR=uAcEIa0Dz5O-?8hzjRUhq!ddN4rS^7P{9-xIdZduO~@J9pMrC!Oc&lZ(Ia-0>z{ zt*~~v)@jeJ*LJI#-CCxbb8?2m+=^&D>qnNc`($qZXW-p%bJ4%QiH6aWjxdUt9!(X} zED@YDSD~bwDMMBA&>O?X*XeQJo=awWZhE`1>~^M3Z1?Y24|jKyDUpxn#OUe1Nz~u5 z*6VEfkIiN_oGccu`(%TB*L<=)VfX9irti1EcV3+Rqi9Wd?i4Sxf6qIn{k%B2x@6mm z*x6}<%l949OxPjYcy-%0Bknh#EXIBD@zu75jIKwE?q^Q)+FA2sq1HC8xm6BVn<}nH zO)owHJ6(gKNTke%l>xO-NX^zq5Q(W4jyM zwfmmF1xF2YH@&^p%C&8e>vs1Jr}w$jPs*08oUZksA%A0U^AcZEzw@PO$M5t?WZLuo zlP`66(#P<`_wKr9tSLQ0zjW@;TrW|2F^%!dGu<5*#nsN&ZFbvKxb2-(%XBkoiP+s8 z3Yo_^>)*x&&k6J0cYnjiH4Y-;cP*x=@^p!QZ(F_8$7m*7x9ha(8PR38bGN2L+e-c3 z7D;asMZdKrWXr5REokoh^7`Vl5$e6As{++xs#{F`KF&%w5_WoTaY}GZoZ-6DU+T1` zycn5|2!monlMZKl^5o zkiVxWH*&u~pJVRP53j^5HtDxCF8e*zpk!N5nd{$MhoW0nDAoORi9F)HI{L;5x3vc) zs&flHck}KOUe`KdhySC6+zXy4g#NqA?HYO6JnZhSV_v1*ZWgzznVZWcEOYmk`3ddn zd$hDe=&AM6HP4TkE&OFuwTNZ2r|E`COYa-zDY@QRy>-Wy_ZOdmdYM-~-jd|5*`1%9 zqOyMnlb6Q&7>hs2T3-|dvVX6pYkS}A{|slcFC~&Y{yR>fCw(_$0UJALHsiSSQ>iffYfrdx!eSa|P+oD5O;-_W{9bpuFr*Wmb z^L4{N!Rc9i4lef-kEh<8yu?OaAUYRTqO9i4F+BQla(1DSW;y@dsk^?Zz`M>S%2oz0 zESe=eHLJeQndaKecOlx2PxZ}gwcEPcQP+I0td2C>HD#lQ*DkNAibaNbN>{9{?k?Jz z*YRL3-}XD%wmUAW+Z}sgosC+f zu05Rha?|8$t=qX%4=%D#UY2>6e?$JmN8xWu(*8y*7cc(fQWE(jr@FbUO?76NgTSKo z|IgUFSn*F^)S0L zlbMdu$z7>(k!8E`K{f9|o9wljY1!L?XY?rE~r>SYNafnwY*)D=*?XV~TpV+kxyB-MP+N!|(gu{u1uSx1(d}Y0ChY z)k>4w_IqAT{V+v4>e}{Qo62S7M9-byckODsV);z3iJz_orhFA%`=236cfZ%f9Q}Qp z+{JRaUi~|i+_#OZa`!i0rxz>CojpZ;*P?+I#NvZPr^pt_nOMyEjsbd2v^0 zb6KqXqFtM3MJq4dB%|ZCp%k3qE zE1o+};N6k5wCg`Nv(-w4J>7nCHCJ$<=BYD@3#RQ|pX)Et_@lz_;t|IW)3pBxNtosc zO5Yd0ns>~_tV{ILRl8qj=ST9oT)O__Yt)8qG2uswR@YdbK6}wTV{Z7>ebHPmw_ZP( ztn)(7-)?Pu+;&gqiKxSxr=5B3**m$Fr}9~O=5ny=DyfTP#+|-3>F1{F zC8`J3EtSaFc6BZ1mT5sJyYv&*o%8wlFn(v`oz<09YqJ{rU+uV}Sog@!qjbv5v)OOu zR);R>oAGqRw&zTezs)>KFV8ONKDaIG?Y%dh6;8p{Vt#KU^gCu+TynA8!M9}9yV%`@ ziFaRe>?+l{^Wp4G#cdDaFD+->{^8c-+^133mMz*`c1Wo1>3@c+IRnkEy0!r4wG6-MysKt@Uox zs@L&b#Z^4EXISie|1jWTe!dE-n9)fB4tT$ePnx2yqT(bEophq z9JA=$qrVLTTBh4yy7N4jTYk^l4({(yCY8Cf|NgshW@79#(UsiHt&NBHX1`VG{&dnZ zxcS}9B8`N3{S{YrLp-KCCbchdJCJqmPj;64;^V7%(p20_=1zN@vN^5pNpD#m8*E7E z(%(~3x~q7WZ2uP{^H7QZ>-HPxa;N|HB!S~p2;=A7#O!tb~51nI<9Pk5rTdQ(RH z48~a1*^V^}xH2a3MAfJi2nL@y+FTuX;C|8epUKa^?Q*Z{(Y~L5|LyIck=J`5nSBjQ zo2+)(bu({^@Z-_m>TTf6&i*(y+w#a2ZSB?@NsQ0Fw5wm(xK7*6_T1C^>(=d_ecC=& zwcLE#zg=GM@^$`}Os(a8dg?#JzIBbdnr+@+&BFI>Q0P?3S-9$yZ_3OFg}{WqyNZ9( zdN)gcyEaK@W>oynJ=?!tyR$`aaUm@4g)C{y*oHOloJOysbJjNi{Y0Z0h`V>vqpMT^}0@cF?YO z#X5g0OP;$#HZW~kl(HnucG{YCRm&=ErTjzQ1w6d{Zt2m9<@~M-?`-wh^xnc7?3730 z2&YVsJ@<6~I-#W>?(An5O4a*V<)n6K(7N0u4 z=$C_glDOM1!5$d@1bidNgLW@=@;tV)okC3l*wM3Z_Qh z2NnJmudYfy)794RV!gTYmB17!koQ1s=P#VkKX1RXDIz)Q>UZzV4>>7!wlQtfrhc)#2`H`~)rN$x7^lQX4hH)s18=`XeXar!@l!GDHdxBfHC zsQ;Ur_$xhqqPJr6fs)Wym+XJ8JGW)-6NNR(mnK$ij@e*QqVtC-H zJ$vK)VaA`++PYqY?5*Lt)U(+LguV~dljlct(SxoDhuxI+v2=+>$(p``PcYk_n+MN`i$Hx<7&G11;yl`Mf;CHbEl&TX0hq<4koJ+~WW0!w>ZqCG6lU$d{2*_IZ&YSu{) z(G!7OZwt3iyz3O@ko2ii@(stbSGr%47temEpco_H^{V%W%bNIW!C!mo=agSxw(O{0 zS}2QmY>Bwd#?=dUK0Oq?&sn1HQ!?w_&UUL;%MCds9NzDmRC?s_mTJQ(Gs_d2 zHmA0|xTh@0Rl&6J?!`W{7lC|RdMdZgig5ZUb?85X#M>#C?@TsI`sP~w{8ISi!&k&7 z8(#g-Ah6G0KFaJ9>+LY^e+kE4Uo!u>>f9y{h7_Hkn+L_T%VXuw?Oyjrd2+tgt7VCg zq%Ajlxa&Qc_i&eJt^NT!kFDF&d6^Eeu99{$a}W%gtg~>o#wT@$z&V*G)9z<>-%j09 zP_$>-q@vRN$rAq=EVfS4o!GH-;sUXK%leuU$#?b2hRfl5O|w>#yE*CzIU zi}@7!JnRwgv?n{#S|=%&PK$}&Q*w1hWY?iAlaA79nOyQhyzkz;RQ#N)>7A+_#Nuk4 zE-dsZ<&PL+U}(=n-G?00)8jp=m+Tkj4~X+S9{$Jar%P$Z`4gRCzh86xXE2-*9XIhm z!`ZNqSu;;W9OSS3Sf^IF(`@4TLn*4qjwH@9Y(LU8RY9}h(-o%$8i8D^6(zULOkQAq z>u9~iXT{S?i^{cCR!`Nn_@?r&vO1uTIi_AFv#-%9bF25U`g6XOdQ%lYMv88nvQa4S z-^TU-8A{8)_x@-2y;}NY#8bnQ)na=;UYmBnC{ai&N@!`uzu$#ryFgQ_6JIi3UCrOg zdh5$$rPF%;_Lr_~Q*^y#1({Nvo88b{zm)TNLfPkr!_r5TK9;QzNiJC$s%dgca?>1- zxr%Ou%N|?aPw)A|RQBS1-(qX6GZAVV^|<$1{9fESFTBU4=|ECRgU_ZNoy>~6-OD1k zO#kuWEBli-rS(UoL{@}pZOY%wduq``#dqw_g1BoH9hR5ZLL!M zY@XNVa|>C;nl{}`PLz_*i(ZthY9A@NIbe%u%Z*Hdf3HpzpO_Q6>e`o=fyJ`gr=MI8 zXxP0e^KJcl$(rxtsm%%C2qI z+paZSE_tMS@9NTiXU}VlFWYWyUN3bER8b#O|0VWXS?!g~@>w=lK)qG&<5t%fiZvL2*@IAci-z2VmT_2lqVr4+k}-1~2B)X-Pi4$>eb9)G(*q+HJRUgCnN6IV{nO zoNz~b*O}li6T(YpEPtu6;>ELzz5-VpCJ9`d#%Gck#2IyI_5-<@xvQ-{u3afN!8K~J z&C#W)7a3lxsu77~12Xf@uA>=+$mV?O)xiUf=Ycm+)$V;q^%6 zy$5QPI@+>1F7n<~PTjIN|3h2p^-t5buD!+w*0u|3J}HLG@A*A}{!I_uUxw_ip+spo7ym(0()y?&`(+=kN4>zCRGl*ZO; zg6#eoe(__o4Sx>Lqy|?M<_1Jp{i(apLpRlv^dijQ! zo!Yx!*u|Dy=4wxRq9@&0rn@@*U&ap|<>oc3vP)L)zM&U0_3j&SyD<1vgly>5}=ju%E#3l9Z} zZklJv(!g=|!KB1!_Y2WAmfBz&Yvm3WJz4jD0n~|F+Jc>(rI)14erMOO+w&SU0OnA& zSa+N2($ozLoU8Ze^+{@8U&0HTnpyRI>)LJqUY&aWz02(5Inmg(?{f;nCRd-6+jaK6 zS=DRzJr7^6mHV8&ZcFvqdFxfa&$f;71&N#QdEB*U_PyMrrIT;hmsPzs-}CVGntPwq z*KMvoJ8##;cjv{bwoJa4^YvPP<>u>Aw$EikpWM6ud-L_kz0dB1N7{mjz0b~vO|Cw> z1EK|N8_3kx?t310y@s+thTi_Y`Fh0OXXj(nzR$KTU3{$Ut!kV((^V>7UgZ=u_k{mzX%-;?X{rQ&On!NQ2D>Xwh8~qjMJ<2?i_Dj2TWv;eN$Lp8d z0&oA(;!|$Yi>?O|oWlN-s~BIMVKJ6)SvbFs>_Z}tI( zuM0wE_Jj-dOggyi$$ay@_a8{uq_kM?d^5NF1>c4<(b~u7?K|}Sz%y;3u9+8S?=*Yx zpF#ZMGgWa-W@Ft6%w0UpqQ{>WhJU*JcFL+#>zhlRe(N56E&bw>?ms5=H4IBmXKZFk zpUC+7<%W+@2Omw$`!ZdXry$>eO|{YU)UK9{<}P>f!;}+?DoPs?=p9FCniReCChyJ(dZ>!zU^q-^<@_8RC|&?UXrZ)d47ovkHDsj8BMnj zswuW4w~qgF|x5DKmPTnU7}qw~#1Nxun+yX2cLrT=X03!82w`{{+X*S(i1ZL@TrosX5ZMqNiKg`j>Gl*%{j$3m2W3 zCi3WFYtkdNq>C2cB=`Stj?UmbQB!AX-FdYBoae0GdB>^?-ZrbT4LY9&Vw(UkwOH2>GIr83f-+}Ix20pG&?&%5b5q-b6`rg;YYYcW;@0+an!TCvK z!KR#l0y%s5CjYJP51V82%xuGnga;dzohr&*S-B`O$T#(K|DE5xJddSMKTA`X&LD7Q zx7?a(Ri~@#u69-mn8IdXq@VF>1>Jv?UkaK~V)=NkG$r@ME6EcL-4nCC+Ey>k+!dOY zzSO3~!1qzVz{A{6HBG!17oY8NEp=SGG^WaZk=(JSpjHIVKukEq7$>i_1ZJv{ro^y3~YJ73-Wx=H!Y!aU{nvcpRo!X=!ewSs& z8uyT$Zc`0^YMYeXb8R|#BHd%=S;@1WrnioTds%F`I$>snvF(<;l zc+wICe-fBqR`Yh%jhN}zZtWIV{km=S!=Pxpva2^%%CFrT%>`BgGU3Kf{k2=m)VyEk zh96o|e=&FUMkD*^tT#T>uiaYP>iHMMkB9MFPS!&C`$Sd0Zd-Tc#NXUiKR`-%3xoJa zJwftCAoU^rFl!UR=7oTqA*x#cecQStCu?(8T7YzITvIX8#W3!}v?$F0#uaU$lRB5L zKACc)T4aJZWrg>{3W_|NsfwT zNYq>-*Innir|c5j@%aAFY2VhqsbBudtX|)AO6Zl2kMVaG*BD5wah2Odk^tiO59m3+!nBdt*dga=RxkFO>;UOE>Cb3^l*40t?4Jz zx?o+ptA~r_iNh0>n7=(y-oExmcgKelOpN9-FIIAgFEEVVpLI|(_&Rgmv`KH|W<5%K z!ydiUEJtLvU5VP4OTM$e>YbU(di1WhVNx{j>kW-LPiEQQf8poy=-e{poBC~UUoeIB zN88DsIPtik+0Sj8RnyZCOkw8^e5eU)b8L&eaX>KH`rBjWbjC7i$(2Eg`ih-GSDHMw zc^xU7*uDP@LfM`cSL95m%@fafWS09{ZOdk!#K4X%OFT|23fp_-#MZrc zEQK9btn--2rTUmtR>35AyW{4VZMEw|G<9V=Lvto6ow$9?H=8rh+WF=!GwryH4flA? z`c0X1t*LwK-iv2-YupUjyCY@IC9@5g!>b&t3>ae6&PW*tO*y;4u{^M6-l|g-7uciS zbZ0E}OuOTIDPtL*chSzYJ4bRZ?ly_#WiU{g@bS6UsfXQ36L%+Uk>_18L2#Os#nV8( zgDW{0I5(_Od+gA*!{SnGmP&P~?i|M}SItt+*03fzhi=ni6<@ccGO^$n<9ESUUang+ zM53NHRU~Sp_8T3WW2$jC;K8J+a*UTJ#`r0B1$GNOSgXyW&eON?IKSZGQVHiCiGFu; z(JlHceTAF%ZVLLOo9 zB+t=}91NT*)|i1ka!A)ZrrK2}$-zCbmDAx@m)?JdnquEQDiJl-YIm#;y1u!2b*-ke z)A7Q!oB;oVzYhDXj zadIgtr*5^MlPWW{@Z9WmaS@x|?O5_7P?+smz_mNB7w#+0N^gDq#A@%gqfyg>5-#zr zFa6J;FR=ZG!r%4fpZ4DRv|p+C&z_@`Zl2(B+;=5R+v>XMu?wj=_og+MilpW*dd;G8 zP)W8>IQOC9oTPszAI!D>v@hd(kj&ja%XAIjDa$uqy5%@+)4XZ7l^-9KxXF9__QUnN z*8XR>{>k7!gNpo@%Jhkr|9sbH|GoFu|3;VRN@aG{~WC$@hagy|!+w!7GqIw~=^Xk-})~iu7 zez1Mzs(qg2%Xezm+QawmZ{7dd`~LSrv)>Z+6T5%etUod5H^;r(->S>r-1feI?|1Cm zH;pyM+^O}+Ra=wloNk8|Kb8)%Rj`dnWUrWeB<}WSH^rs0g<(ryfkt^aAID}vEU91! zvwa*I`2M8Sv*T=UucfXy5-I-KexgZN+j8mMr4RqP>AwJ_rZ?^_TsnA(c~?*7eGs_ zAxlIytye5x+26pLyw5aL=wCSw<=#^zS^!S-f^RM_UAmG zN51D}y5hh|a~?Qp%7#KLH7vO}^S<1{tnRg`TurVVop+q`O~^>&1=w#ZilLcu`{mK&C5#L zl&4o1%emYRw8GI6mLV7;{xkH3nv|uvZ;a9}=vF%ADst*x^!9V|np!2HVjx?gS@@ge z9&4dHy_|*XPKQoUQVqXf@$vBELOH!Bah{Ku<8Ndg+0zKNbxD=2)IurAJ2NbpcfB_B z_%-YIe+FBtFO932Kc3r~TIcXSSf1hD#{Ud5yE-SlI=-#&kmt&Szk-{jUU+Fm`L8); z`ays{8J-uDl)P8niMb;(YeMXGGqXooQQm813x8att8>AUG8IZ)C)uT=cTRkO)w-?mNf6bp8oyuWFO(f8!?_=jPkjT0AF9K0D{%nMGu zbAB8HZLkTEX>RjBG(k8?J!wb{7*BO2uK-{~%GOx#eV=+BoXgMNp>?{pq8(IdZ9Wn6o3SA5 z^!+?n&alw8wcGZc-cgjE{m4?QYgc2>y}U|uv){7Y!}tCB{R%q3wu;U3u=*hf@eRca z8mh7pCqN7M8efB~e$;b|>8|O`!%1fjAD4Swue5bp&%za}cLmwZ3}QZ_rkc-j#Cw zvQZlz^a|Rz+CPWNMJ@bh6lfZ|>Bn=c3$9A1?(BR2so+0@iujkx_=%SPoH~}? z&w5||Tk?D_c;+_w(>jSg5-w{OP4$|r@#(bh<*ER&*Wx=EbC<;EdA84VwW&Oqapy+r zjhTi8>x6j^ym8P~ao<{f)9iL_?$fxfvMo1Hy7ldy{JcN5TK&A-u5bSHY@^Ej=h>Ei znQv3|`lVjQ*XxsNKE7VRWas1XxRalcuiHE6d2ej3DpYOhm+3ZDuWy2sZK`<*R(3t^ zWbyHJ`zAg2cb{7GB3!uLRpF~@SomJ^+i&lE^IcyWcWukEl$*=082NieMV@<>C$z1H z>)ABb?MHX0ZF{Mu%M~-(o7X(_v)Uq+bh&Ta#jYGTRt{L8mNRu>o435;hm|#=Pp$OY zPfXgh)phZ_{r$5KsO-?Y_$|=r?91x|DO* z`=e`Ilx?SdBv7w+=TI7;RF4J;#Zia1+DN2I1Bl zdsGSwza3Pw`q090ZJUp@!;!UbW=wLPaosY^HBxi0RP2VF-Lc{KJEsY>Y}p<6_E5@P z>BqNszp<_mQ;3|izL2$6bB4V)$ApQVM@{SwS#sJrZ*sl7bH|O8g2^RU+EU9CG?=cJ zinf_bDO>#3J$io2gg>9wDbM8Pz0GYh(fxeFx-*;8j)qS9;>BuSr0%p)T2R$R&+CzK z!r3iv=WXaYofIPO;XQ394@+Br`r2Kq*Y2tQ=xO5IP_yswu~o$ift$s535ZFZJ>quw z)`tC-d!uSRX1}QAy8Y=*&Uv+*bxPACulHm|s6X1b-0nI1^8V-zZ7Jbre|f3R-%=N- z*`3!jQ_|7(h>_y#n`f7)xT_^j(0z03;Pi>jS%RmwJ~ErrpHh+Sxlx?yNB*2J$K1{R zxt@XyPc*GWtehO+tNMba-o#ai#;kI(wN`< zHWw(??kLRuedWGtL+12@f-B477VuSYE_Sou&VSS6Qt$30QBzkf6LRoj$T?d64s zA6?IV`+iqMbj_z@Qw!FzMgdP6X#lx_G^0II)1UPeTrdwr)t_?6I_sa@XqRh z&DOj-jx3A*YHZV)z$un{U|D79g}VxMSM_pDCr%0pV`5EU67ydbSJEdhHs{30rFuU( zZdUHxw^Jkh;Dej<(w3Ne%ro2g-gJ^!q!{nSgeJ54)sOwoH}4N9KHdH}>cwNuivsB^ z3hakmV>$2Nzxh#9EOwjL?c8-&XPvIucwleFd44y2?o0R8u1z?RE~@qDh3GfadX+ZK zo1aBbFWRm4aq8YxSNr1sE$mryO3KAjWn;&|Th6(;vZ9k)WOr>37ge$h>nPAKO`KGH zBAV&mmg|fcRi%8)+>W@OtGP=Y{oVE;ciqG2ms3&IUoIVZdDJ$-7eOtSAQpPJ`Wv46KPG-E6P32=x zoG%Xi3ReZTA+r^37DPiL4ppjP8-wp$=6dHFuMRG(n2CmVLydXzM43hUQ_ccgmvZq&VFSIQdR2Irx`bgr& z#vNO@Q>VPYdD$dN;;Bc8cHZp}tsfJ0rv7I*w13}gb@A(qJ8s*bY})!~-nGO=gEKx? z)}MQ#_0CCn{={`p6fJ*pr7ymgbDM*y;?avY)_ECMZ?x^X_Kp!w~O7@9Wi@$ zKDHzGtlVya>$7ajRJWT{y>X7q_?n}*9>mJa_=?bwB=y;S-H~n2`XL&4ZqG6;liY5W z^~M{dY}y^Dxe#ThAZ6Jgb8jP?3pH&g!utN$_S~~ly9GeXP%M4%gWI{+DSErK^|J2g zao0;e*1W!T%-~qX#WQ8?mZ9taA7StkU|?ioV&ULsWn*DuVg}!W%fu`wASA4)Wat=J zRJ?H$i>QK;Q(|G`!HW+?f|4dKRCXx2^yq_uanm8^fZ*gwA6XZR%d{8mZx*y}#Zn!N#_Iji#50ec{iQ zlAe)@2M+yu;`1+t?NR<(JtNKq42vfHXSjdT%=n1>hIuUS;#-kmTYPIg2*XGOjjRfV z?bmZq{EgOwb6+_+=Igr(wl3dmc-22+xrm!wj^m6qYrD_P5B_uU)bV!3LyJ6wKg#*3 zY4lA`IM<>r!TiXa<=V~de_9Wny?f&Dran8vLLc+eo);(Frrq=3did6&hG!Tk^bPC0u>|f=)GI4dZrHe=NOv9@)z7;PN?QnVF`bqmh-)M~4_zTlKU+ zp5H4bRg^qkv~a>h^ELKwL+@{`_TDx%&pc*r-$D0n{~1cn^lr>J;LKf^^&|9wuo|C2Q#?)cSLJI-|!NPddY1RSS+%%l6~nUs46L!g!?*0t411gyc06J6 z?fPA_#dG{5LRsGVCajL#6tKwTN5w)BgZhxYfr`z$|6P-BlDTlrN8Oxj>v5^hL*D*p zZKtMwG`;_AgZ<8k{fS}f84Ery%hw7%7|Fi(#E+(GHAmx^ICG5>_wc{pGy-L=nJet; z)3@$All#>1;JgynL!FxTB^S+qXylb<&hfl>$Ng$_Z)>#loOcV_td?aIoJsf0?9z!@ zbKK&z)(Pfkb2(1B6vtFAf3|4S*Y*dk+1>w?+N_pk-?rab(01*;#5MNs+Tw4tj=UA& z$vl>udCD@ZPP_ZqQ^6$D^?6QlP{5jh!&>Dhiv+dWjXDK(_#W3UVv#HSv`EM3qRnfMpK5-ko@ZT?!}2Wlc}X+Z zdC6v8x9#@$scyH8>07s_-%nq|&zVKtVWE7P%QtNCl_)LnWtWfFJAbsW;?BZ}NRe_h z#q2-B)SExnCrD3`XxsmOqf^JTjXN3U%EY}rn)=vfqp{4j>8mb%jfz|CeKoUct$ygz zS6lXmQAtSq@VO|RKjGrNr<9Il$K7f1b?ta=cvD8*U3_<8B=7#GW#BLqSY&$MQc7+0 zwxu$0<)YJj1*3lb;4S%>9yP5}*V|^=(s|x@Ys7vipA|k>pDJ)itJ`c|$E&DmmD=o9 zp;G!Y&WN~K>}FhdYpc#DgodE=D(84(*G>2G4mz*8GJVpO%_p-WpDeQT&zg?^xxZ@8`ZVBtT_vC8p-;$ZBsdRZtaxiUzctE-nH)TMfc6t*&%9zEn=Mk zaSJoA*cIy#oTno)6evDxKS+#w=Wd!C&Nnm=p9$3?>0 z)0(wT3Ez$gQxV8J?Y`yEJuZ%t*F443CtcK%KF#^Y#A2d4^F*hv>!((p$k_Z5l)+jB z*HwQqQTle}?SF<<7JJ@lD{kwWXx618|6r9=w#z-O-Q})wXa25TeoKB^cX^ITwe`w1 ze;@lUHoL6o+uph8@DZ&u_e82gbRYWe{@yFre{UH$)h@bt^>_VC=Z<6HUV*x$!yyX7u()_S|>@Uf2G%^k(1 zD;gxF4^I!6)b`-nM8O-!Yt7!@GYPNzY6ebcf=Z_AC68Q*=qr|;aOUFC=Y^5HTS7Xw z1ipFy!LiMEjhcU|skTMwWYgH)i~7^*E+T9SzO~!yTmM_Gi?b{3&YbOT^4lTzaOsWP zQ|Bhy&w4h`748dAjET7Id^h)?<=d6F|1*Suyb)&tvg4{Z&rR08we?3`%fS9{wY%_l zOZ4u9#W(Ms{5BtC^qu)>X-5jz6e{!-{I=Q_^!}NnwR=?knbP3AzmHwZ-v_5Z+Oa1u zK*?%)7u~(hDtGRc z_k43Z0rk0av!Z%GwVhgZadzhPUx}p$vl1ppT~yFKF2r2)bt{)e=%q7%+Gdx(m(rEr zHrrnG4RcZZk?eS#MOixTmVGB&L1_=1oL$BAP2+BM-s$GkNC=MneH5CcxBPbgb}{d5 zQ-6A`>HR&KThhK7iR|6F?AY0wf;qzZrhF>Tul8M>R;wi~+y0*+EBF4MjCFrs<$y~D zMVIV(Z+bb7xytQLbUZ9Y2)W->(D<;Z3lj##)+YGxG`(~ZLr?nZB z2j0T-fNN940iX5S9=i|L2-F61n$GR}>{;xwE=5oKL4`(YiLzEsO-PTq+@9oPZL{ZG zRuV2`-&GQAT!n~{qmPR~mF=8f;Ztj+gSUa|wG+9EmiVq+v^MJOnN8s}FA5nLr>w{k z-Lh%b+q)tW7U8SQ41Ln4PrJ72iqVn7w>F!2&9As)m%W;^)ah$%!QSvVE?LDEyOKGj z51aPiF3jpWwKZOQisL8C-(emTTK*+VNxp3PDEiz$8B$ zMcgkQecrLA?Ut6yt$U6~u9;=j>xu8YR`sICafNK?q9<0aTl>#^`ujcgP23;v3ond0 z)^}bFuZ(?sw1#g@zv~ZGxjEKd-)~ReKBe$_g7)9Vq1BV~ZYrO%IKBJjw~pDzy4|7a zJy1+J>e~~|k225BBqPE(BkpVTzKUsS52yZnz3wOL%-(6|AIxb?&e3V!>dhuy_)EWV zUd{f({|vjQdkJs+wI;81{zm?X*3u6jh`u`}r)c=@+05>tlNFO>w9|0!YN#bpJ_r$<6gnF)qdI+yPk9QB+P0#HV?Q%ggF!r-qX|-fA9uI3qYNeb;{m){kb{4@#!FX2-tEeDB+qdVBJu z%PqDK7H`V67M2aUY{0$5>(ZoGPkygE#1(WPX9dJEL8-@z9lp*LD{3^r@qf?#$V>gk z)z)`SXGyGnwNG8Ba{`w%!~132d6%^GgnzRul*~#Ao+qtt_WRe1Jaf?xyRr@AKHE%p zy(wcU(3|#R{{5@&xxbvIZ)m*LX8K{5w~1r))%4^g`^k2S%naUP^nRU5(PYz^YgN-bURJF+lXUL+Po0O;KPg-6-)V4j`|e5C!)&j( zc7EYjs$6ZJoGZ{;R@Ud@!+dYS9_!uW{mN^bvLDpXI`d8E!(IDjrZdk@lIYNS*)jcZ zCd8Ay0_-Ml-L!ijt3VUE+^Mw^8`rFDQ`>y>kn^IepW;og7FA9;mw2L;p_Zv|=97($ z`;A|^m=!NAIm=s6e0N5G;{2IMPb4?*^St2st4J!vBes0~q@Rl~mQS)wl-k#=oW!ZN z!SqojEKH+(+8jd6yV}96)zinCQj5;zM@|oz^`q;D(~mAmhUp3pW#>X7+F!bbUgyy{ z;At+Yxvc!ld6f_KZ=a-TKQxM*BYr46VybMo=BZ!M=GLBTwyh2w&QV6Ebs=T9vuo03 z?rY`u9gi?c+PR~A3&u+bEA&>u-WhOhmt4mJ0tU62e<(y|q^HRR` zbw1hL{GTCT!N$aKn&Dih-1IObci||tOv~t^Gq0zf6M;mKTL)w2$!lxp_LM>^)h$=` zPnvR0POw#4Qc!g{DJ;46_B;QxpF#_A)jM5{GH3jK@+7it-s>sm)BddFSsStPp@qGw zpvw8Vy2-1*1vcydS7H(THVG=uwvgIOeox`kCH6G54{`l|=Q} zuc01O8YiAxiC8gf*h-izCuG_Ur~Kt zi2JROGtJpTPi{hjXs(+^}muV-`JC3$pY6yC+L~ zX-m|A)k`KTo|8Wkvb-g4Qp?YTISW&_9KW~V`?<_ZYfjx0f%G$?&9mAb zs+XogD#pw-V-v}5rnOI%?x;KupRwyp;iSrndG{;Q0^3htwf>oZ-Y?n#pWyBcGtG=S(Ic9jdtY@y;o-zxG2Sa z6!!y_vL|wv{+0_nzeSJ!j%oKS=-cHlD3jCpKyGe)*GplmATrmHRPPjK}wP>-?@q zA9uQLHd~R$w5#pW<>2hl4BHivUR6p}aEY%G8N9kf#@$JdJbbxf2VeA3vJ^-soD@+QiRM=R?=nW&8YGbwB-(?BvTyN{=);|LoAc z6;l0X^{IG}Ttv;a4L{c!Kil#**k|E~_KKA$CiXuI+`D?x6wig%T$}khD*bHceA6{{ znN5jX&;Dl!^r9yFz-LZrt%#-oL4!|Q~SDNM{f4|r~OaePH|N) zWxc$&^Zn+Ok4zys7CtB16f6VQ^UJCpShsb)h5D&$I`7~4$!CgunxpcdsO5~tg>5S) z^W{evErPW8Z_W;u(H6YK28q4pU!|nf7TT@(&yZU8PgUxfWa`h$v9rC_{AYL@rhC)* zUCWuLb^la<|Ckd0v~*VX=B#fMcRfvZd9{V-_4kkZ_m3<{ezRz0+^VyEB5$XO=K{97U*a!6 z>HR9hG?VdM2g`|*DuuhkS!H)5D?r^6P#kT#T01N(;kxpBALVPI8MB{3x{4kB2b352 zH*KrC1xXUqckFUnKk46mmC6mXLw26Lp{D(D+PW#>-7oUYE!O;GDoN}%TJv{pnC?y2 zRo~ikM5>ejGwho6#m!gtJ*qS6NqaFZgB_>Q%j=!m&xlN%_s2+iIsbbsKLjthlr2=Ir1Y?bFM^-OLHM zy#C!>HMWLUL2(<07`m`hrUUr_Fnc-A`2_+xo`p#4nI8+VqOM|F|QF7}^3$=!4BYfno%SU#!VbZKACzpTyQwnl7E<*hRGUZS}3=Cp

6<-RAvr-BB^sxycuIx=p^t`_NoCO5-H&j_bS5r1gJ!BOkcm``XffUh)GIrbYALF{f?Vz5@yKHx1+4{_Xm)1x-9uK@^Z~ESzW39jzri*f(z3Q`s)0m?> z64g%2b!Vo9vn~*8@Dbt_v2BaZ+T;7!6;{M7+Yvt9L*`3i<(;rNlWQ-_rY;kk5tW$P z3Z}$2X9jp5embe>lb>Z3m%@z0+&;PnTN^W5>3 zj)3S5_;P4X`IV^k%>tz+tX(20Mc27~bQ7WuY&rD8aNXRb#@`mo0&bgE&1u*LNu=#o zKJy(|dH*wH_jdfYPZ^53N(4eJp4tprr&Y+s&a8CBf!{;8Wp|ElYv zP+6yE>=rwd+u4<^tG+?Izu*7PZgUFV{0&;o?m2TxGwQi+@wA<%W1g-nx^9_0XQAYl z87=o6{fsBeIUq^pIc{s)wK?Tw+}3kQq96^6I3@SYlJ8xbJqIRra?f?!tE)aiiVWSE zTf@aBz4V7RqrCO2AMVymU8@c9US`y#txL1Lu1zI}8<-mSQPxrl(h1i7JLRKUWUx~0 z;=Ujwa9@OXOTv4uikg-WOD_MlWctr=QffoQzvg9ZHa8p#PBIEiPc~245x3ar8gEWQ z+Ts?MiDijB%MPuHjlF!nU*tN&%<9>N6({UgHzZ7dSiJjV_he62mj_>O{Cf7?Ut~JV zR-V!#t*a>?HAI&jNHHkfv5wPv!+Hfj7t4ZxU+4L4g>9m=n~M!jSFT<2#=GfQW$~=< z2^&mgx^|~VxIBCMT>XQZJO8?ZxtgnhueGo(G7ly&pBFQ_#*y;s8e&^)GBKPu)uoV?lU zL5-e_h2HlMNAB!9{GZ`!b+*cuQx>9P4ksUS^+m7oKi8xFzF1!`0hP4>zu|-+S5q z*H$x?VAS+DD%TQ^oU$QWd$PhVIj_?#>FKEj71#PY6t`~OoD?IFV7n%}HE)sEgLRB^ zz6A^0zMHOP`8spOdHu?Bp6&wT3u4XYltbGRx-+*%+voB>P%&Msxnk!;sfX%De@-4s z=vmZQe7a|u*vwB+=_fPit1hdVMkD4#p~TENZBY_xo@aJWxOLIoey4H6)q*q58r#yY z)E{Y6sSPvRkyPNzxVGcY+*QolP8I(dM5>n-FZ5R{{1<$_^MOrBSy~F8T&2A$Li^U?!_Dg^zqj^E*mw59NkId-4|={9 zVR>^V9J_vmVL^(wto3EXtrr;-h0i6<@Y#8`#L*ylqh(v7!HH&(n0ac~P0k2xUFfwn z^RCY8C}Ee=bMhE%PB4nZ1sr=mljZlUYe!h7c5S|^Ft>AU2dAGA>$^!zr4b)|CjZ>l zKjpIPS9wb%HL357Md?;&FHf5CYVQLPp3dauRe5H+6j*dZXS{4VJ?FX$L(3b##2K2) zTqG7fngh15{rAGqwNd4s*-ja2Vtg;1+Z@$$C$De<=N$v7ceR)DbUUB(Z2d8NYPQou zx2=iqy4&BcnJaZU|+lUE`(Dd5O86O)>zGIJ|#Pumws*(~iRZO%+)-TqfNFL#c0 z(;xXoQ&!!W=~>5r(Zcg=iIAi39>If#wSvd4A5f{8oX?lfI#J%WZ}o#Pj-+A>oxUTN z4U`<#ZcSQqjyL3_vFLL4wJLAQYNXa&5SsnNGRCT5CzZ%m%i zaOF*IbsB$>j9I8R%le~R*-w5=Y%~a4xQbCnIhA?AWpiJ%XT}Dx8tvjmZKozE{(gVL zCRirP^4=HsdcNJ;{+?&9sOSB3DBty=<+K{rd9OeHT-Ou$Xnu*?bN2<0)wR>#F5+ii z$9P(8SyIN%3A3*V9IJi&$Ljs4*-|Cs+3-q?Jm=iG+H1y-LV)o$s|-WqM43n{UA zrZm2ddu$N3X7=VEqIb8ytB^kYLzL^gY<2K+1FK%KI?z0u*qK|<-pc7?J+cp7Czl0Y zDt0+_qH1Eo`p8X_Hy3GJ=im8Y-mbKA@7lRvV%2RtE(TmQe~%~t-8y29Z%>4lfd}r1 zM4BftTGc#OITTgx)N$aWvUR`G=56zLKFH^D4SIcMqf%ZFJIh(m$hlK|e;ot zFzIgB`J>5`GAGJr9!jd|f~0^GOhp{CwpM-C^bPrl;;$pwu{w*gRNO7wm0Q5WZ+kKh z+!Kk|p0xR61$ zS!vKDN%J0!O?gLyk21Ih&HHuLO<~5VP5DZjApz5LYEu#a+Hf_~5Kh)mkZ-_=WCQOh zS;4FS8EU&k+)aB==p&*yVtdlcM@tI*<%`y2`O8f8vAX$FbnfQ8I|w|CZN6fn%roIjEYQZQUC`QFn`GMeMw%xTrz%fa#3y)T#>+6t;2E-J zDNn-d?(aN1`|!k&U7LSRERFif9<_=$p~0~=)snp`=Xv-VXeA^mxV~l+=W4er5cg$T z9Bz3wOI6EonWVtHn|-e3x!@GY5H>wXZOyJL%jdn~Gr89xG!-&X#MhDi@3)9XU$*pw zcu?0S^|5zP1DE=(V`T@oHD1cHIO!|unryujlm$V)^9s(KdfMvTD+^=q8T_6Hp)FVN z#BQsV@|>GJNhO8V+eIhb_WzMve2nA#tr@3J)VcU^sFfGJ-kpEv+1ZDuZNBd~<-C_c z`kTM+zvS*`?4b~c@OE66c# zD|p-r(qxtwxUR+HZrUrm8!`@)S+FW~_778Nc_GBSqX^R2QFh6WxqNHw-2QDHd$?A+ zMK(RAR%~n9CMZmOb0LY3?hTjfe9>PE6lpx;OJ|c3yJf zRiBcR44li`x@X)u*>cVyZjsmS-EJkRk-d^umlNYY9lNi>8|`px(T9$jS7wn@X19m< z&ugu;*!C~0^K9Eky90C5)jIj7^1hfR5^Ip+XHw|nJ?YBlX3p6$;lJV^8h=*f&vyPMdD}sH~R16iJ2HVp*yCv8ebals-iZyj&hAnH(T~(`C?VjtpMl^-reXWVqp%ubE)!DYW zyP9sAaB6PT>GvEb9!+_c+8MDsBdlw)pTN}15lQu`p|f9Zl!^Tmke2mUWY1ll%;S1` z-YzFCnTnV4?vygxc`Ep!jOvYhHM^$OO`cY?d3MvA7&VQI6A^x)Zzq=bTDrNHtMypa zdH!cOliyXHeW3fDy`r1yFO_@n{FjGPSa!Y3-*KkY!a0od*UI=tvSrn-lKWK8)$#0zfow?E z&7VBR9Cx1mUT%~pVlS{G{N^#`ziyB0bvw@-V!atQA}}C z9=Zx!nl42-f<(l?YIGI0Oq&qpXb@)M-E4g;xjwrt%ih{GMe^yj$8*)RO&)as2N=KP8Hvuw-#pJB8AGiY6|e;SqlXSMHt zhN+qRKdm+Y7rN{}!ztR!J~U(#(LA+4oOk8A755G-%w2k4t>E8V39dP+zL~{9)+ovDA)y?h9{j6-E?KyvHrtzm0Nh{WBzKK1(vs~}#o!!%m z-k~rzpbI9%o`#B5Ouh6e{^FC}kxv$FTDWzZ-oq3Bk1%KoFfuSPGBdF;GcqzVGB7YO zFfs@#3K%*Den>2AoVakK!o`md4;nZ?7br5?3tTpQy?Y+((w7IC+-Jnda@0RK`=E|T z>cWL9eLbp&KPcO%#;t!P(DqrO_G!)2>3z!YySnI*Tp-P?*IC7C ztlSW>?7hy(iNde!8Y2A`m#Tk!cQia$J=FCZQ;cNM(;wbDyf4?cp0@IC=EyoMCFt5C z^z`FmTYbN-;5(82cFf10-)X3e$=CA_43K>&&Yt+gLgKL3OyxvAMmt(tkp zgC`{8CHNUb)0kOQdAJ%x7*`hwJ1e~57rpzz_nT6zj?m#h^7oQ#KON1w)v8^1yhrb? z)da(Ubw`{x&2hK8+`*;yJ#NCi4Z%rWpJp7%$}YLpa8F$F(j0AnqgUT|eQE1CFS@Pi z!oI|$^1lg7UiZ4iPMtovO=h*FpNHjw>>Z4H=Gj-LP1*Zy#oncXITDY#cl#|dv;Ma+ zcWZDKb4A1tJ}Z$-pS#Y!w=P{|S}!wk^})yILPPdSEH~_5c`~Qk{%>#ZRI@E+L622b z8w1!v6RzA%U!Q$I+vbkbE3Tu7LX2}ZG`)HsE7@?UPJL!1_o@eWKd!{Qy5=7+TX>q% z_p5%juf$i!<~1^gTuHx?Gf_!RM$O!Es;2RmPS+sYFv&CD>J;@qDwfpRdw9)f+yAas z!03bmLk{CcCKiR(DfwGI{+g#cnp~3s2sb0~mI^xcpt>LkXUghZMQ?iLGx%D-}`AMhRe(t$o*Ah63 zA!;-4!3E_dHg*qw?D(~2|5_f#F3zM9kKamnT%SKac|=LAvB2&{^@JzuIX@lW;j(3-bA4HEWJUt-jQCz;}9Q(#+W(L?=kjFYbINTg>rj zXPuRR-Rt)YHOhZFCLN5+J?W>X+~;c;_L}ujQuGV0^-H#II6f=vhXTvv3s37Nmru&Q z8Y7vtP{EDcJk2D>Ir|>>PMVE#3`TF-q_53XM1&{uAGIH zyBp7}{@I7$F)%Q!5nuu5CUT<;1?qq9WhoH2~gVQsQ z9Nv2_^!*EuPFE%`lPvE8iQy!$+#ui3~sXJhiltO<-St{6T|>Uj~*{ZG>Q#I)cwUurWM zRz6UEAildWzM=X6LxTw8#)a$bvYo1xlViMDS=UKDIg@ltccPMNpRn}w370pOo;!H) zhtL|$md*7ZNv9p!8jaREJ`adBJQ$;WL#}L&M_?)ogx=Oqs?# z*=Sl$OLoy4tBG~2M;#W4iwbtU&Qg2Wli?XF@*wVfGXvKF1NrG^k5|5IS+(UMDB0zk zcjRB%x9mSd`0~`(a~CgSS!>ArVWm^>0v?u)O;g&{nzha|am+PqbbTQ+X(_`&p;Kut zk2;tfdk*ZF=BMpzv}I+~{Bqg<49i%Z8~6V_*1zPy+Yg+q>?ZkpqMSe8_b^C$t-G%2 z$?-M+j@2)o{5NO*o9DkQ|81+Vdse@3`rkAE8LI8?o&I+${+sb%p8pK>`oAUr9jjkC z{qM2)=f4=$FP#4({g>pwWB%Wa>NidXsVG+eZTW9o{brEF%zthE-;Dkzm#bvf_ z|F->S2%oCq|3}Vt#3Lm;_mFP8J z=Rah|v;5zlBN;iyN8BH)o5~cHudCYDdTgT1$53APek1Wy*B@z?Gh6g64B_;cyu$yn z`p3zWt?%D>Cb~`flDc`JyYs3Yd|^4(&nB-mlhRL(7oYil!9;}s9({T3ws5{dfDq@tC`KgxMa`e#)|LTl9%l)J-zcx&x}72SrJl?zsxD@ zd}T6OL%IL3LNd!wjrV_zo;~mHSm08)dEb-clFw$X6Hc2uE79nE@qF1YS3P-Jw{H#S z+FbowXvOJui>}_{o0(x|>~wn5iC3!^DK0v8V1?$x&Mo0{eL@*~e$KnKuPfY9^XKKJ z%hPAI=hdF_bW=F0>BbN%q1BgEJx8YQ-_vaKUnc%%us4r*DI&qV@4T0~688tYy_$S>R{p%&s@yv@ z4srKg@k#Gpyxx52kCUuN`d1_yOC~OHS^4SX&8kFEt;Kf?>rUMG#UsSPBQ0EPy*?)D zkcaQOH^)+M`&fKx{IToXfgaiK7Rl}>?!38cD&%IGGsX99LENz_hi~8NcO1@e*(fa$ zynUZ=`3J_JMFICFY*JvJ$eUzm$tD+v(8R1=D&t1H@a|f=&zTV#I9WUr8GZk?bq_J~xw!ZXUp=BhF{OjF)5 zQ7`q(Ck^wdANAtCEmmNHhJ@SWw+U#*aRg$WZFk4R2W zo+G-z;MznFbsb+h-Q3cJn+&$G%xkn|%390Pae05~p>#{NfNvYycQem9U67cuM|kbs z3fDE=0=ldVBp0;EBr)`z4s`gcce!8V*LUv@o>d8I3_OgNUG!A%2|j5KPTy+h{8ULn zFr?Gt?4p%LKN$8cJO9u?R#+|lN;Swxdm#DL z)UDTGe|V^_$&^HMp(F{hy-K@mqhzgC-rn_a>7?X`wSijCD<&S)d2wW|mJ;iWJ6>k? z56u+Z9$PA`_FF70@1cEfl1ws7h_&WU?w<{bZ$4+N56n;7=`7N1;-lVc=kJ`7vC4#@ zFMM8OPWJhn*}<2-oZfgY`CcM#%$x7e3f~r-TC_Cl=gjJfF$I^me?4bXBzQf&SI8pk zBg5w2t-HHttnKDhu{l1ijXRD1;Y7x(0-N7#n6qKVX~xsRdrDt_D!lV6>5&qbGAI>S zt!?9Mkd~IRULW&zuGEQ@8}HA2vsae?pox`%*L3Uq@eiNuT`zD}c~^6Er2L$JtP!VQ zitYPea3+0K{Fipk!+k#*V#N&E4btaLOZIC2c{07~#dVF;bFZ&2TqJvanZWWYqXkM9 zH;Z=)sJ)w_;&SX{+HaILa?JMUX5E@{qKdN46|yOC*0#c9{z#D*W-ZVeanwn{O)TC-_eTG6Jx*P@kq zcpWUYR<)ivop{W*Av9yrzochpJAVhIO5H82bL4m^#(KkctF!(;)|46+v-3P zoHa75Un(|FUF>*OJkYEpHPiM%!bhjWcc$&yZg+#%VOnzuqo9Cf*Bcv!8*KMds%!eTkXT{6XDcI2M0X5tQI7oL*tP z>Fc?d7k_&A&iBwa+h!Bvpl%mwlh~%&QMyp0x#`DLjx^p+Jw^g%OifZ9Nlbe+x7kSV zXVLk{tym>3P%80_eW`TSmE%8d*0QOr`&j?NHHV+u?&9L3i@$Mtc8S*T{hQ7xt)TR7 zJ^xB=E1BGWw&y3c=O3Ecp2HYlBtCD&eZiHkmF7Zq%d^%!42x$@l4xA2(<(IYx^A6! z;M1u=Nm{)9rYC2lXtlF>^sCLizWzf3*FAd<{aO10n2&XT?NRag?0!I%?@;rsPb{XU zI|L&w7Q15$|%WK)ld*e=F_X;__5C2w_=wD)B zD}3;5uYTK^Xm=%5r9Ab=!Id$uwg~2~`gE21QBU^e%ipUEAId#vaVb0=aQ)eqG{K1) zye*=!O{sg|&yI4w6!UIx@xy(y1m-(MI9+{Pk|xE$$`If+wcSnY@HDxHLF;A}>2JN` zUmLRVTb8xeQVVC^u8ads@kd@-PpO>{_MhQxyZW`?;j2zPchX^XOk64>_RgSrj#B3{ zqY3+V73wq{Us5A6xA%hFa=%5Q7U9P&`I@9y6i*lCiJYCH`boc4KI!S9h37Xk7lu62 z=`We`R(su?*PjgURF$wZ{ViJT@Qa1%o#9cXduyG_<92?$@2ly>VcaUaK2*SRxw+ik z_DzQc>IAg@BwwAm_{YEVr-eT%^iPR!ke}H6veaBZ{>Y(&F1No-h~`wdH|O8RzNJoY z%flVkvW7M2MwWDK%UQ-6abW%CJ&*2BZ8NXiz+q(Ep=#;7D6cQ;ibA~C+>+pIu@k)= zA9-UK*sea9U*I=OEB9CI`NK*xc=pba;KhyEPq$jek zuUMCre@*M)bp@fxoPP7w7j24@H^@4e?zU(i$Hs)t>i!_%O<&L5yvSy-Gi6O|%8TP( z`#P(G-)+pcy3_qyTWGP~F>?pon`sPpM5@G&E3_s_1vsu2dhH~<<@nL;TXBhT&aOYZ z4X-`B&7bheE+KP+YR!TbPUeSy3-#w8lRGtW5!0((iZhOGyP=b7v=;ko9j-G3)- zpStl&e%t!8_DheC{^82J%Eez6%~7%Vj?%a0w8eM3Efn)sJ92#xT<19b>UM|roTpWF z4M|sfUq{HBN6*gKnNU25({({oGlTSOua#eKac8vb_EY@JAck_ zUE6AVCGbN`bT|LR=Fd6LoxStUd^*&>!%6m;{}s`nD+@LqyKt%Muz$g~*G_st-z9RN z>J~)&Y0P|;* z%p~RX4I!^)=sr@$SLjTl=#_UL5qFQW)g2_sC7d<)yFZ zK3>GK;YgIxp{oa&Hm`hJ_B})1&D3M(zS*u*cdy*9rI2xQQ$y|Nk2(h`qTR1w5#_Mv z+b=3P)iU(6ylCTM-q=aM*ZAtL`?Pky-t>h!&TMw}izI_LMeR8=J#-`Qnm*B^#<|;9 zhe)X=NIq_;T9M`cpF!xelTQbGO=r}tuQRUAnO)Jd+`^*d=gy;jzn5Rxze3_;6yw|Z z9VPn|=4agdyfLmVd4<>Q?>g~LTmlb-j_S_78#mux@`d3(m9vat+xXV0cZ98Tm{k_H z^JBa(+XMl@aDBU=KmJ_nbJ)*BGFO;Xi+Eo*TfThDid}dAGj!(c&VD8N>HZZ_*OOUx zi7tm7nVCoELbt2;cnDPd`~Byrnq#`;lS)MeA*G0kpDlhx zyI9D#2~1~Or1M$sf_KY-$yKWN?!EVHthecTaCnX_=YBR{!wRNHGdN5-J-&F%^FI`+ zpSR<$*^lOVjBDenYhAlpmWdqpJ1i>KZWiZ1$Bq*L9Kc6Rnj#Z{@1Tr(UDrJsxDR0`xRWC)BqvR}vf)$&JQ^^atq zJ7Bj};?*(MX9u;TbCzCSk^4jP#H7zt{Y({Sy__u^r?o|4#kr@)t=FE(^P{nc)`9Z&m-Cc~!+=_iEy{soLAEc16pm6#kxZ=Yq>VnZ38gnTzzNC43JPW@A}l zq^aMsCx&N6-mcIKFQ#i6PB47c@UVID1&!S|0$=!W%$l<3&StqJ1&-Ipe2+^{)Qn75 z-Yd8Mae7L@%RdVp98L1N3%zaj@_L`&a44=>R+{TH=e={PbIQEtsrATN^vuuBV&q|a zyTq(_NdQ;GGFJ1lDZ1RotUILVsjyGIk@Gt|Tu0>TwYaaj*YyQdUntJ=V^6=hVyBU9 zTFP$}Mg-tIz6)xQAQHq@yAH-r8aqC0> z{(vL|otwBOc2=Ywdc8Vvc+DVDL-FH-JiGN?N)&>y~Dfq+}l~X zH*#XDP|%^{TPHd?8t!u{pg3KdU&iYxe_-tLy)$B`)SDw7wcxCh=+jcIZ4*`n8hadm@^R-hX`wUmI(7@{WTqZ}SsL-7J&*gqt2No1 zFFxUIZ^)8wyXulqsj}X1x;5|Z>Sw%67c>|hPrujm5!d zyZ9>PcTDWHW&1o^IP?B9wDBi#R%SR0MrF-x>wAM<9e)att%9X=*M z+ctMw(YKH7=KJRDIsI>zFVE=^F2P)n=NdfG(Dby^TOQJOR}$z zEcrU`eRhY~F13yyc09%`PZ#lo@6;0snO1c8`?sCHCaT+H|2r3FU$60Q+kT^HGf$<{ zfBeEZmwkTzCCvA4jiy7S#<$1=Gs}0Lu|8vZtndCLr3-6*7^ys~2;mGkqF?Z9j+)kK zqv-XnnyP&JZZ6!VSh?tccSDa*g9c|P?_8$^a&=+7U&@``2?DyrfFQdXf=vn2mC*`SFp0C)O zc4$i?v47vqj`z27*sIIZ>6`H;ZNeBbR4^6Yt|^H3}Q>D?0Ua6&bI#ycV`Qz#mPvz@6(ICGUMC7+6C?# z`eom;rd;^&d#>S|4~L#TDvot45{%53s!w^p!rH_9!3~d`y-T)LYwL;Z%d|I^eZEYX zf0y*m2-&G(kBfX3<~Z$Q=Db(+EcTc{TaX^x!?zh*CF;9X+i#{$WL(>GOP4L;eqIuP zqer{$#WR7+kD9nUPM>pq_m|A}nayvaD;$ERBzIWfmuxkw3N|qp40r4?SQEx%Iq%FY z$F8|<#}0Vh$=tb7U}CfkOZp%2?q3`2d*tQ{zgedEA*K4)hKtwMpPak;KZB~}yPBmg zu?_qVQx2KMz1O~{X<^dM`JShW|MZJFN+)y{X!3mR+0CubgAzUr^|D^nIX*MDvoy0tTLs(Jy<_Z2Jt-Q(jKJ z_*pS&t);7~-|6d%*t$!d9g5anlNz-kFCCZ@4jwtY<6IeEhr>4gJMR^PX6y*s zV&pxiFm|D+y2y6t_1eE|c)Z(X*p_`0u)Pu1cJJHHHCuY_bLg@#9r3B2@A6D{_cejF zH2YAk1qS8asi+Ntc^T}tzXwlVzETEMZTd2a7(&m)iiGemW1U08K% z?dq%T)lxC_`>fd=SmatRs^)9Fxggb=w{F``*(|~0F-T$dJi}wRx2%3M)jIm0bMv+jZJE;5Z}+HL z=2=eT*4r{A=~$zu9@n>p=Pvwbm|Xp~MzJHocR7#FcKf+{U!9WLWYd03zbmUbN&m^O zI>&zX+=qO8*AK~Dyy_SBpCP1TjsQafV*|&&*OsR~9ZzYwmD)J>;C}}7l=>4&JUSfl zr?leuX6=}7yLv zm0IUZl$qa=D{j7UT7194#A(W5j+-vBtA0AZao_Xe`kS}qK^ofQxYjA$pRsXg#fKio zr!OqSrziA$nmMs5Nur?ap7AdGH}Cv^Th0C6^Cthdb@sm_7p`xg&EHe7py0O1DUmsy zOnZ-o&K5Lxe$8TdyW-rL{|uW!I`V&O>0dbd!o_*Eu8gTQp^~&wcm?* zf_29`r_VR8eZ4&WnQO_B1#7qPruN+5^q=8f)7@KtjubE7KjYRf(SYJtI~3SWmsOrT z&3?&m&m*qC8zfg9RMS^aeyr70YF(s}9S!JXA~$PV};<;s{}2U~I81 zdHV28O`@u@&Uc;G9SidH%$kyfjrQ0`h#XNrb4)n-8nb5F(}Nx#HZ8te8NBK1s{;-{ za?7>W3S97(c(}f;z?Em`nIpU>!wx7csZ@F^tRMSA%5-V|0|1+e9ecxB;dhgdE+4Xyl zec!if(!Arx66C%glc_!5BX4_t&V9b`$8@|`Z?E{4eBZ!VJ(RaYA!jxc4gN6K8l4= zjJriHPdlSiX)R%Hz&9=PZq@UG7jsK2^e=Jz)Dq8OWfV=&7PEPIJJ)J0Ls@0rl+dOJ z?#ZW~Jr(=ZX@2C~M|WMrfLBbD4}ZIS#^R7^V4JBi|5Ht#_{lq-aL;(t5F&a}s?KBH zv@l+!n;s$iw+CHdSjNm$nzOmLVqVWc^5WJ+|d4=ufX+L;w+sR=G{)mSlaf49cp-ePCU@mM$zNLf}bs` zq^CJ|na{p??I!!yuqQRSA#!p_)2a{M&-x zM91Uldu)m?UYe@AyFEGL6PJhF)bz$py3TD{kqvhrrmKe8OlEqX|LXGdEosU{GPXB! z=IT#UoGZNR>lvFRQZ-K2tG|WLb7#2gdS~aJ>0c*sPI9sTX>@ekl*ntwZW)3in+`Q^ zb$#FPpF#0OtswKWt04VlZC(`B zzp1RrAmGYQJ*k%pd!}T4O_$y<^+M>C=}W>KdVXD5^==J&-nq21^{<;!r2F5#ILj29 zuk)w>mfoI+dH1G#QaEQ2yQc7kWBa_9ehmU^PV;O#mFOdWl>fl<^Dlam_Pv($n_3pP z*F&q`JD2I=6T_pWI?5MidSVXwK1*fT7<(gsh4|BVyR`)51omy_OAQUKYFfHy)oNWX ziJfPZwrV;x^;#zE5L@{#aC(bTsMCTiu{p^h#qoz5(_SrDw#K7M#J%Ri8=<4-QU(>_ z##&}B>ob=Yuro|FX5ck;SbIrGczJ}2E|1CK*Ed%lEAnA+V!rdik<*XyVPbBoVBY7j zH{oURp*I#N$#U+kEo+Ni*s;m{YTawDgNb2@UR(znIX1e#dm5ppxwdrTmQ8gso{NTPOBb=VBE)7jJpn<_WnKXU5iiP>3faxDy`>^US{8SZSmfy<0}&E zxNh#qyu7I3h~Gqy$*h((9@cB_9=9|2X!yd#An@DYEwM-%WCjXFT-08Z{R;F)z1y_-=0% zyS9OFgtMRb@db+7v&x#d<y^3W#l+Bpht3|;cbvM(w%5T&#c$(1-(5HMY+H5qQ1!`)&mD@^G09G|(YV0+G-991 zT5gA}uNI1gM>;Ryi;&&Qu&zw^S?o9QsbUu_4{X{J{xfQqcv;7~Q-_+kowp^29BN2d zy}CF+n^WcRrsmF^dna?lxVKI)JHe6X6PsHqH7?klPEb~ zuQ?_!@=Wx!rFU5mdKoV z^5oD2Ck;DC25<33{(dHfMP_P>uP#(fkeQowyz!F5z9(Gs*-n^Ai8HM>O-g`(^@|5shV_delY-hE*Q^S=EuftVyUmdeu zz?{b0*7S;V&C?ID7Z!5}=D!AZUG4R-j=fy^ zvE;yM7bh;k6TO`)GJ|I%mA_lLt^eV+yi{b_vo|+w z+*H=eJhIT+Xe-zh_34!Mi;_2gjW>RMWg+MNlEd`hSysncFUw`+uRciq8nH(1A}_Oi zpZy=Poo9A_+Tz~x_~RS5Gezv7<%_q^^jV-g@9M(Tud6RVN#$)Xan?AK(;jswY>^k+qIo+w%J_gGTzgFH2D|@tflX&5+gvLpK^geGX{S>Tiv-QGh&wX{)=Y3SS$;#Ziv2UWf zMdU(;7gA+%djn3No#7=cx%==hr#TzMIi`HFJG0=AQDA12#f;r=vif4R{hnX9I;Z-& z{*M3$`?fV%Ng2T@TF1WKt~j<^(Sv(k{LcAvf6w{Pz@h!Lq+*I zzl%kpwSN3V=0gd!Q)bJZ^8V^{blQ(yXDeMc-P;f?{y1nohsf;*+yA^=xJm#zO81{19yTQ7C*-MED99*5%Pb_x*Y`MEX+A=mS`~1x5bGuh|PHV53lWHdPUZRz8 zmt2UMs>S0ymrhm1&HcXKLC54q+S_-+Pxei?^6})w60v9=l?~rC-IoZwy#Bauot!M^ zk@vM#yXLq0sojzax$QqwaNFuz3Fk}Rh*j>q6_(?fxGVMYd`<7);j4BZiuSjDbH?V6 z>f>6I;_&+H~P7&3D4UX4eEHq6a%bjtB5irkqxtNh>2kMSOv7uhV#_USK) zRa0P2N^x=NOZ|J(cMZ$2ss~@}%1bz;zQ)fzm$>@J!{@G5>n?5Ko#!#fF+qi0b-mv9 znbB*eGBr0am`A!FF5EP6!8-M`$4g(f_gLBpHR!B*aC33ltm#qLuieYO^wjV2b%QIy zg4au1%I4WG5&8Ue)#k!w^L=M*yBK^Yub(rPW7n;-Z;}@-h~ez7pYxjk`1}*~I`urd zub;bS+_-Ff+0EwF)mV#lll!grzO9VX{c=OiuKZp4l#QYX*45l8`!}hytA!u66eU@aERrzy~pMT$2e}I^_tn-2Cob2Oocogr@ud&AMtXb{>=Noo?Crh ztsfS*ef1)R3rDiT*zQHId0Tq)$ZRo>xrxu?jnfZhPb=S-eSXI5Th`w@n@=5o9T;yf z`=4RC`<)+81Y&J2@B5e$(4)~Yqh`Bh+l{%?*N3q?nN8SWvf%1f?-;Qu`!y0bpRd~~ zzg!j1@jCQ?&<4q;dPg6`&pg+DQ^3pdPLHbA>CoIT$pecQo9tm?FuaDt4ZryUXmAiH0g+?c)4z>fOVa9)+w{ut4D^>e%H)~tC zG_$@pyDD8sVWwiQzWaZMYaFsKBP;mU7vyic<{Gol}8@H3be`L??*He6A( zx!s7{%2dBVN@LN))$i}Gpw1B^IhOSLm~gyjaOH!cAKq#MOG`ifh*#Z$-It3iE_Lte*R6q69hiJms@=J+|*U~mTK{B;Wn~*vO@YUpOf|q4tJhb$urVs zciQZB6qmbAT9^LyWujx=3Xjy;=M}iaIrY~ZsMAD+;@ZbwnOi4$Fg71N98W? zJxExzq2N$y+ANM<2JtwfMUMkMcW}@CQSjA!1>=RTcsr5cgF;4k@BS@kuxZPAvS|7A z<_AY5&*rv={k3RO{L{V3x`#8Y?9O2Wjzc$|=2fp3{QHkHId#%9lfs0p+u6Pb{QJdK zq{lZ?LPF1=qbo>!U(nvqJkA@-W~hESX7XE~|K04lZvrYfR(0>%aKl6UG;3GKPeoPR z0KV{v{dQ+ryvrM=*nU${*p+o;ZQG2>`~*%GWij2G#wlFP^NRx}yy}}}?73ydU6+Ts zC%ASeKMGQK__RSeX?}RL;T3Mhg|}mpE=U^g3(BcTd$Nq%V0%P^V^7F!C%dVZ4q|KS z)|}?vrPpxkh>cQi-R6$kq)SqtgA-r4KAOE&{CD?q1M`jNj~rw@Fn?;(zYTc@-IYpB zR>n2`e4Mk(T7KU|1Mj7E;VSz%82|Du@#*Q`D^_?{Et<>GMt-9F5vx=U^T_aH(diCG zv#+S7+zda{d)M&zIqqM##2NY&nt161&gBvo2h$JkefrVI_iohI&kWlx zzhc}f80jNn(rURh)?u5^h3%=#mXHvxBp$e{_nYK|1%^+2mGinT(-~t zUEv@5JNv)W}_=o?UC^!nF7D+bcynOT*IF_Er0t$pS7%~+WmL?AI_e=KUVCo(&gCAd9xy=UYfpK zcX4z4@yzS1eP-$2I=ierZPLpXH@8QYD0)V6g(gc0@SkCK|9cRp&3;Ay ztH-}){5!_~#qe)WeX;zVi`DXP>u!0C%?oUTqz-N=*N=MeMfqX1=BV6HB9*@cIH6k9}b>nA-u9oEJ|LD%uyxw z46_}QKOL<*V{nC|+m1`YxWQ7Hvhc8#{Ssfe||1+@3^+*0D{&RoL{Bf-+ zf2jO-C7-3*4Kvt2x-I#Cguz0PfsuugiG`J!nT45wk(H5w8N_2?6JQqoHd z3@esqM6565T3S7Ccf2I0VW7sBM|WC}o=?)N7OcO&(fG#9H?ANKZ}E-nc}HTtDLvmP zZrir=jp>mK^Z)$jExyqW5#4YdBQnCUM={^?pbewwkQ|hNm}XkeBIQZjjP(vPAv?`Sn~3N@p6ZA z^O>z`cwSyGUVh-*Trd+RS|aoELor0Oga@P+Nx=b#wx}19mmj42H_o+VD|sP#c>#ne zw8^ORlx?7B%2na1CD*U}$SgcM!O|hIeano)Q_gdr4=Gr+I&@wB&D7OtGr>2?PuuUh}MZ3@TBGm}f1)||<}RP|Xc!7X{uEX&qtY3Ov$ewFecHyw{GY7`Kg`m1Tx)fF2nmql5g);zKOdvUDk z11G;J@$a6k+H;MSx3%eWs@wGL*b^sDW#_~nDGPO!uzjz`neodS+Ji?bb7 zzS#LI++O{|y&Q>_NpDS;_OBG#=lj)CW4V>2>W5z`R@eQ@=3G8@B}k6pl@xR4OWnSh zvW97=u5LIN9p+Fw=i-s{mnnbtUbyrkVa=g{j85?nK@sWBHJqtEKj+xZWZQe$ds)fC zuUewczap(oMH#oenRZM^Puh1bPf=ueK(@Bp^<_&gO)|Odwyg1R<>p6x-u2$Irr{`n>+vB zsFt+<6Igz!_Mckz?3+^zj$}-`b?n+JrK6J%?W{BK=-M@D^U*bXtqR4$mn|$7ZneLu zZyL9G+sYj}9^cI;z4vRIx_sZ2j|E$6&Ep#1K3e~%dscZ=v`WX;muuFk28F52*dBga z-|Nz8m(V!Rz@Uk@+Dv;|r>h?ENxq}5d~?~3zC}sy+Y++hwzT(T&E$Euc4xE1gsw+v zGaJ=>zDZPVKmC&b<5TyeY}wH-O9Ub!wwZksJOA;_J>kbaD}D;BX*NA|@n%FHx52T> z6H991C!E>XEOyFxvDdY#uvx2Seo87>{(a^gw|`DPdw;)#__j;-O0@KkMempF{^PT? zIocxQG;eKWAd`Y@{Nczr*ITVQs|9Xd68zk?+3w>abuRl&asEpeOjkTMeO2&)!g8w9>i&Q7nO|w33hYt|i?#Z?g2l zuW5X9b6TeVn(995+CSAV8)xb7ef5if!CA=_?aQCrKEL*B9^b+QAyuDcP8)cdvUw9@ ztD`0^PUu)z-n(kg^;v&ybl{570cH~qZ5_w)Hn zKcBz!^ZCmfn1nud2?3BMbUCMC1X!=h8G?$32XwWL4m z^7E~kpKopX`8KQO+fuu5H^H~=vj46uKM$3-^0Rb#w$gQxLzfF0t&TF)X9qvuy7Kd_ zD?i_Q)m+_buv$vASIRO?l1b{VxPV6A^tY>Oz8$smx+I#O|K;t6E5?##pT?1|Iux!i7fx@~24%d%{rY}2Fu8|IW}?C z^(dDw!riha$J5zvUEaDZBfc+x)#82JFR#Qd___G^Zo9dae?m0xcD!G8Yx9No8*jGjOAxb5p>Q_aXzHEolR=lThdFoW3PaqHPxt)ySAy=!j; zoA%!hSJ!TOHsj5M+fm<^oVBi#N)+t>ekP`)>=ZN4%={HcgG#vnewNpae-_Jla_hNO zx-r+?^7oye{IxRuXie$GzjtSAee$c=_2k8>neR70z5Dsygp9ZRA3V1$*=!o4Qc-el z!EB=fd1F$ee2#mIct2gaog6nnS1T?*lu0lvFwWM zExTXa_lVcZ-@Uncf9mnN*ZbOksN9aJVSF~>J@c*859W)1zr}wHAhRbNh(mykRvcSClI{G*B6w!F2Bj@C+6jP?YYfW8=MBN zZ&mv$yF)rV?$PXk@{HfBrQYxQ>2o&kisxY$Pz-#IdA76qZc19u=A1~q9^ds}9;}U8 zD$?<^D^zfM^GDWg8{WOY-#T~0PjE)kdU9&UNu8@pq_g6#FAJ#w7kybu-dU0YQY zyVY!=PDzPD+RQfxKBqH^)>-Q~|_$9<2K=nGq=m3cQ{ z(nO^*x1ROc9@uGBB7A-BL9+{W3fr#!z5Dya=56wNbt1i6CbGSnxOG-nV1G*3WN6;D z&agkK{BkCT7(>9k8h3{&9`7#8&XLZJyE6Oow+p#ztGjX??=F77N+->Ii^9=sIz=v% zPZe3*vfsTu^|#XFr6=6=)uR06zfD=MeaHQgWnks9c~`a01uk2(Xt~MR*qECIVNSX1 zkGON=@7~`JDPw}nE>G&=R@_iEYm<*>|Aar#LJ}I?DU)5-gtwW_Ip&dkIX9|fIx9GR zZF!ry*BzF=uGyWQwR`tuPy*ZZ_tuUl{~0E|k6!-tR_js5EpvBFp5?W6CaYjd#_LH_ zPqXZ_UEz_M^_Cmm1)`Hu&!kMty5750#r>)KF}6bIt?jw&KbGXK`R(WaXYG;O;pIh; zfG^u}Z~nIC-0VA(LE&B-efQ+P{&#O~-rw~2V&LiXlin`;CunwkzJ%oB-J8v3e)QfW z-=Vv;y}a&^&f6kz*7vl@TPbB6=xx)@v|(o8$HqBL zzi(PzuJ+4%_x2(;#{J4KvpxUb+O|xDf%oGVtIsXITTk41kzAE=rRcV4uv$&hdyo$k z@7~|f^*iwsxWw_c&|xur74_3F-R1~1OCd7vsTW74>m>hH{-57%zUJfj;Nl*8Jh$$7@}WPQ$cd!ERJsfJMz zuUmzAj!Hkeexzm9f|L72W*_>|bUUKF{QaSOH;2C9ESEWJ(<-nm301XAN zHqOPLp18M4X%+EVo|O(gw{_<0=OQncSzDN0OOd*^*gS3BJdQUE{ws2q)Xs6*bNW$T z(TS32!gUY(7pd;GoViPM{=ah<);#^sApD{-*DqV)`sm>boOQ zShtj=yJ3r4v&q#@QU%wtGQ@aXies+&Wo}zA>xSvlqPL**2?{&zYEan8=c+A^YFuop z@>3{X0Me#{mes%bRSajWH(Pw=rD?aAn%h<5#3LyIp1U?qKg+c)T;KFpR>_&&dj6$# z^VY9(2|KfU-FiRQiEAHkp2b>vX8xDE#oEfRE8hQ>6Mb6ppJBrqlfSiHTKjJMoL>9v zTP?>9O?T#VRoJ|(^*$sJaS$8x91h(#$?;$d`0JA zW%aU$9+MM{_`|kj2e?+joRDy4qHLeU)Kja^mi=e=bKt;q<&WIn(c;oe;*LC5y0%S* z&HUtD;rTaB&du}i@eDF$Tb!C5!eH1@WYKb`)VPPm;+{G0J@I$KLY4x}mv8k>^L3aX zocZi@(y87rA=jMe9oL+@gyTzSU#xD9{yr(59j{N!4~G>dmpn{XNNox0cenHXGCuNQsR$6N%t3)t~Wr^>~J$>u?%c|wQf+-7Tl`klh zlv7_QxqMyFE%QHLbFEzLSMmM%nWVMov%{_POhpN%xeJ01{bwk0p0Z-HjZ{+pP1&Ua zC(ntjveu}%~Hp9lGa6zNRkC`nN-#WZCwfoh%WhryrGd06! zpZLCN=j|RB<2}l~OZ8P3X}npj8sZUgX zkWJes&62%=;j@aK%$cM&bC!0Pr|FU@oh-}F-+r-rgKEmQxsw>UE40p7$cwDwG+VQJ zdZF@V*U3xsm6TS>sdwHJD!RL_SX5N$m9$0IJZWpC$eCO|5A$;-PM7C%yOpQ;B$D^= z)kD&+;+`*m52-A#$uE-4xy@do?C|jDoqc{La^gNW_xHmL0`i7FSw%7RbLdb=zsF+c&e( zRy63`I@iEIi}M#f|M@Xi^wv2pw&;}YS(2~AGdLrTx~rSm1bF(nO<%M^?Q^*A!xHt+ z5tA$O%f)9m8?7s?-ea`=VS0{T%pXnZOu^o%I$>GaoY)L*BlR8_IZZEyccPX4Z@a+Rsvu@`(@eEQXFmQ@^jxMfY)zU!_*5gSkSWzTq; z^_<%pR1SIE_P%wpQ+ML*6$}A4MdN0Memn@OJ8rd1Y|dczntznz@UscaU!Q*Y=i+Ly zV+UV+@AjK}XV1aR7i;`~wr!KJ-yR^lDy((3noE{z)3Jr~mMYKN@7CV`$kJ!$+L}ia zK3RXKPr4X3>rk@b&P>IP2~*bHvz)?~D0CuIX~)`MCNHBORmeo0Nc;CV^Tqjphchp4 ze`j`V)B38t6HMR!;q&$*r<*O6(_uOBa)aiYBhRuf@_yAXz6@zxtg&`% z@jMd7BUPc}m&zjX@jrtkkHK>DY3~Fkzlrr&`AslCZK9vv=Rkah(_RmsY=vwu;;Ml?CYH|*XdOc!YS5(caWVmZzlk~ZzYj?+b?my0=PcAOk z;ad`aqhU`={2e#R;2Edc4F5CixmBU>mo%GaLg&-{7Lm7AQqLcnxNV}q@sOiw%ePO~ z^Io(L+T1c*q@cw5O!F&8^f%6R%Vy6~p7(scxj~-t#2$?f_0^oI!AdL7I8T#t3-5C_ z%lZ@&qg1(JPUn{^$2=ymh!_eu6<<}IyR2{~f5^?F@q1MtR5TQG*nvg({%Z= z<)$u9{~5f5*46L5&8Ky3P3yXq6VqA(I#jJMHWg{dtXNY$>sf@`?2AnEp^8UnQx5d&5Fqg+H+H9 z=F4ZlW_%OwPF6jx^U+sm`_*ad0u(nze_v*udad)Ph1{%2=e@SxZ_hk5oMl$6Vk*;l z(o${dgpI3uR&MJ}zIOV^ohvJ@t}ySfs`$6+fKsfh^5xK(_npE#BeYEwnldN;o3`)3 z)lD`Z`l9TzJSS&s*_`y#sjm8QK_Jfgp{GpQ$#T2u0$BUy=~;>8u2pLTknj&hDPGynVOM0{M}3!WwskPnib76ox5jtXwuyC6BcC0?(3S- z+VbM(8pWz?eoM29UJO6iS7}+lo2ezC&VSnSOKfG`?at1D}_O^&#vppkZ@QUC1Z{|uM>HlJQGG3jjJ;mymY zhV(@8&sCXtTlP1^*^=KbISQ-Y`IZ+(>76Y(UR9H$ zXSx_TO1!zGv%BNN!*gM-d*^diw`}CiOFobydfz9aid*c*R@L;Nh9_ws->A7heR6T1 zW44>8nA8H(%dwJ=s{}j#H}i<&5@RpUnm`BU z^V1r0ez>ihu<>j*pN#oNr5RVxt&=$ZC3AJi=C+Q%!Co8uHo0!NvA)QZ;aH1qlK6jy zU2$59GEG9edE6!Xl>g0D_SwYy+ILlMEsLqJU5jp2*osf9WF&R9POV#KJM&M2O_0Lr zu$09fntO`!-iJMG&huTjme=D6d!AU?;in*%&J#YFCYradN9pR0K5)X;__pDlR%Ket z%QdULt|soyjh&d)|Ge{)*H69)t%iJGEA3XzR=VotY$&FCL}%ABQ^!Br8+4u>_j7JE zy`ZwmS>-kBRGx+Pmz`arlZ%7r>ALs}-HZ@B=iTSOd>X^ko-Y-PPpMRDs_LxaOjk`2 zy18}5#VVo8K0In?>+ZltZImS(S7%DD((~7N*Vze6w>MLD3a2U8zh1ak)v-qJDc>Xg zd#`;w)`x1?zgTkV*sK+qE-GFfd+wH(MHkoK@Rm&NnK-{|=OIu_%}%Cw%hrpJWjB2K zxz5}8^M8iQKycyR_@9Ak)w9q*8>dzK+OzKYZn|usQ@LL)lUw4{Dy74d*tNv%@3G0u z*hbax;5}x*`>AIkIn!7(Rtf`y1!jH*Y3L21v}Yz0k^e< zbHGjNz_*RRD)owDp6onX7+5!dUs24H9aoEEx~BeGcSx$jQ#|~-^qatQt?IvJg%tPi zoS4-9+tW%UFXLy_t}QYb%B)^;~P!-_MhttHMkw2C?BK=!6b|YbIx}nx{=l zcZM{L+q3x|aHf>U+xUS0l`Qo}N_eaikkr$fIWL5Ylw>{0?G^yoU-^V?I zk9X88f+#S5zPL=sU8y)ais4N1((r{|`ad4`xvT$Ksc|;Phb3aweI9$a{|w(>+@06^ z^Tpk6Nr`#tcP^OdeBB^!U$bn1G~47gleG3)E{EA?Ucb{IappXi-BF#QY9GY&#L6~< zliJkXI>}~S_x`Rq-#OJ@Lh>lzHQCqM(SC-TY&?9r`r=o`=vxLH+`W#q$H>pu+V6BE z-{-}RI&OE|7OAWJN_7t3eV+AdMozL&w#nb%+-H^gJ#2nb2EJLQg2FE+N=%G-DOf6- z5@1lgC?Ld*CG4hWuRwp5$Mi1~oGq@lOqT4)a+#VRe_3sxRXVqIO3#iTMP>_JqXW{` zH!z3q@>&*BQve$3EKmA0Th^@lVsE%cccyMf!jHVdOFD|5YSu0-`^|GPzAY^H+k;%A zM3!$2a?(8??xe4r%^P^>+vE>#ie0`7rAjSdkajs`N*rVR;lg>+^S?@b3b;2fO#I_+ z{ogV^>)%ZHUbC~v@1ygFnCDH;nTy0KT8-C+Jvl8=G)J&K?0eJOL+7CVvlfOX=9Fct z_LfYyV)z7Vhc3O%yeY|{U&pDk%El%t!)wlzPe~!JuIg);n6KD$Ri`bE-ZFn{ZCl3t z!`fQb1!el>e13ZxV?spw%dVuZp7p}x$*u518_x>f@$B?vwa@G4XO0w5aoYH;aHjU9 zl$8&=Pp+0R;;Ii-=KgVa`rj_O@G^_)YcrfomejB6owv$NGtshgwY12dKOBmS7A;#F zSI&C7b?-ViaAPj|x_fbg-RGV2g~3If?HcRuXkTH0%J(a0My|-yl|P|At0jb?HTF@J zRG2_Bx9Ahkpf%!CtTmb&eHB+Osux+a;_bnke;-7ywVsszewysY-6wy9Sq5fBHLq-c zzjDW{XIbl2)^6NnZ1eN1<+GjVy2S*NK8K~OT+r6B#L;)-pIsN6`#G3o%TwBSYCVvA z^{FK4%!{ovFRm`UYItN{ishQgDMu5QnK`Risz66ngUlAZTW~f$hUs9*5@=a>;GN=x z^KN`>vv}N8x*gqYrYVash@>gAp65;J_{tL)rWw;V*X)>X#-5YnRdc?r4d8b9Cm9)d zyLqGaj?GcuRc@tcrG45b+IMxzr?>GZOurVqmVdyoK>JSX^nPWRtHpm)I{!0d1Zq2r zo^suK#o*PfwWWKHbQdi?9eY#p{govGOA>5j+f;V*v>U1_Pb|8)_Fwnsa3$59W)I_Y zAj6?fTNs;eR<2qf=Cliv6c*k(yoW<)bw(qf4jbdWHI*zdp2bQW96R4eLv# zbCYybH7(*J}$jFS1&xVjf?A#oh!H7%C@Ye>WckQ|Rx^w-pv+nk$7?@7|CZyfh#%g*&kNNECz&Mc7ypparFTUQq zq`ko~$;sWKsyoCg+~UGFaCa3nvLE#QMA!k{=f!qc!Na=OWRI`bbvcq$z3;Z|{oHx) zx1BG)oxc0+=DXi+zWbdsdF7g=tq0Xi?@XV?9vaozVe-9p9g@oEl%31wq#AzOb;>tV zWa3|_{#(!C`oGt1tKN58CvREgR%vC$(&F^04Ktv(do!r%Ss$z=j{npw|ZZS2M z(gEKc($=l&d(k)J2xQtJo9}G)yeHWua+4zts_d4V9NQTfJy&hFm2X)|*PEB3H!o># z)XFZ*(k{%(-n`_x{@t);+pT=dRDJVGX1($1dJ{G4%}djpmq2n!A>rvB>yE5kcXZ{t zbCYAcgQL3xqvtAtje@HRd+rH}uDMFPP47*KEOe`SzVUF;)}QdviC&+=BRSV!t$sFF zmLX=-;>XRP(TQ!1|J-xv z*MyTCibp-)DplU~I5TB!?VRh!iws<+q#eu*O!XJIa9S<;l8|Im?aYY&+@6(;%2K8? z%`^p7ivKe_n6&hy^-MXI=QI3+mh3#9-F4#gofqqjr^P%;pWnLSgNxJRlnDMqu34Y6 z^zJzQbBWyn>kdxdmoahc(>?FfZ^69Q@Xw9+O`5{&r#*&ZHcHy7_emD5J>qfn)6ZM~ zUJ9mYTut#dI;Z8%9A&^L7=AQU(Xw{`MaSo7v<3LX=VWZMyS8%Le});AMfZ1eH57So z{48`*ZnIQRn`BPa$`kq~$DXWs%zLb+@0v|T%CD5FlhyK3S91c+1#XSZKYu=5|Mj^^ zP3E_!`OmuXCd|kt-M!7Vv`BxkUT~fIlf_nrLIyfVF1SkztW?vM2w}8&sQm4b_$Q^e zht6HA0nGw0c(>s6y=S$nK$CT{8>};SDzsNIdq_%zDc|f{Gik|ZAq^EnnVYhD&X!vO z9&OXuBkd`DM%=Jz?F_jr<0F=fw`5reJKg7&>gsg~bgE9XIQ8r3SC7DR>m&7@3`*N7 z1D{WqztS2p^+K$u%(~eO%d7+^Ew^v`? zlI6mg2hB3}CTJ;6v$=m|m(O3H#pkb_vO%M~7oWef%jPeLIt5a4^iir@-Lk;vuTtgo zg*85ZHJWT6^wRjNQNMjKh}SRNIc4UrneN?3V5-&XmIXfgh%8_EYr6aVl_eE>yqAD1 z^ivCbw)vXFUWUg`CoY~fILlBnm3hn6>>1Uq&v||ae0NB>)e)u}Ht*G(twJ{3o~vy* zPJDX3TtlXFj`zZ()@@Bk{xir=|NG~2TS$+i-GyzzQ{Khwy4sa5lQE^_$c~8*JWLH% z&+6d0av`iVdqTE%(v{G&#wC37nkO3^*mn7;kVmtYl5(%qzL;&R539+{$m%Ev^PO}= zVaX92SGPH)!F@**&dXekTWqi@eP+iu8P>k9l9B#N-xkjbn4lkKyJef#oX@A~`ww<| zCpQW5xJb+TCmmZohe6EwLXv98gN<=NKK5(=i|~!K;0Y2l@ie*`B`JDYY4fp+3#s0& z0VkTZl$2)K)||`@Z}ZVK6csqIX13-$gOgnqeQq+Q#Sp9GpUwz1K6ONykLg%d&)Vf1 zlmpKybcpZl_lg$vt9)uJ|8%{}#$%oDt9aKf+n601qNviO%pUgOTING;Q`HTg+$XxW zY_nXp;1}0566X~z$v!ezvy8Rln8ecw{~09RQ%!u_43;W(tXI}m-zc~?NAq+2x+}g8GcUdQ zQ)d&FCHhUb0Dp7e?%330MrhnRSeq=s@Xx_ctM zVdJA%7gvXQs#lh{7%0ac&)T5gTBsT5!|{FM?FWJnqED6g91?vY`9S8xG_S5oq9pj6MR!;yE-@F*(!~$54Lg^JUoAG zl-8|YFOX<*@z>V2%$d2uRssTX{F-Kf}c())p|s9Q=w|W@azfS{GOww|u3j>Pp3Si|AYDL1SW2 zqqc%3KHRsq3!AWia!7jmykz1QF&CAVoK4Dt@7#Q4+_Z!Aez|Wk{$m?>i~EaXij!zi zw2ju2=cW_cQaxtIe`fBU z#QYPSEe|FhuK9KO+Rn9RzudR*Lrf39KJUaTfsK9D6FU0+cV>cy13)ui?f)509Nc=nY~pmM53a5UDp$CC znm*rt=af}XK9^szom2nt)w~Pw3+(c{f5q>5c;%ky=M@tD+&i1!rk#m-#Q(`S7ghmW zau?K^aqRw_woO{L;Aw<~w+?S>_~*g9u6j$)75R+LsoPlBPG}eGc|7$o4t`{;*Vy8R0r@3fX^3etVdn4H^3{IU+h^ z;;dQAd@TJZy{kkHsOz$~+_L05W(IAL)}Qm!i`Dvy*txZZk}u35Ug*0Jzi95&vlHIO z*{kw({caE6=WiiZ{K1;}`{p#gLgt@>+u$ShRV$5_8U>dYx}BQ76*?DxO?C_WCx_hP zyD4c4e{@dydNN&CdCiP}^Sm3Tn`(x*8Jb_RT{!>YtG)}dJ99n5ZU^u1I`QJC2G7nl zUMn?NE_eQAPpF=4SYLWh;Xi}2*&W!bBc;H73v$yIO-jBo5!5Yva83iX;J|(obN0N~ z%BK8Be46=OJ}+=cn7|rnms&FGyK+kIX_Nj8`5#q_w}`)JW%K!@&CsNL`l(rIN!O#F z?w98`{*d3g*bEve*Om1^LqK;Xx1Gq5Te5z#Sn(OiXi0xI-!0~!3dYl7zDG*de-u_Y zGnwT@#4}62Q0-^_(=A`Tw0_BV?)-6r3J$rW`6b zj@Q%5W#$n?Q=oOS*ftT~Oz+jYKT@~`$vbG|i&5?g=23AhwimR1;%Jt=qjvc)kI9Km{X88Gg737azMrh;vegx-tZ|!E(HGvjzF4N;)0p36 zUgBr}tM#7W+>58a3ys|o2`VA^+-{}S%(y&5@Rdb-Z$kJ)BT&nB>8->2CO7jL+op6^ zb=|bnEPA35mu_JnHsQ_V{|x*gWleueCrqkYBK%iJbHX)cQxUHdEFK@t9_D95X3usk zpO|FcAGRTHMb%z#*|PZ7;k_~?lath}gW}dsteNn)${AD|2H)b|Zof)(Qs5=?1K)S7 z3%AY_`?mQhXx?R##O#2hX-hXx)=NI{XE|ie+dE?$k0G$7!-24*88k1GSd*vQ4Ec5WK?_Hrk|v#t?Dw7<&>m+aA3@y z$r;}+KjLK**}wJN{_MGRm#zM;t8Th&doXw2gKg&vqMq;2-F!!E=R5A0@2u-ef5*K3 zw)6U1UO+ThL#yV#B$W zvr1G<7fhYC%c48%k);va=9M!_dEHi{|f`( zgfGciSw50-iT$3ceMMV?Cw)4rpg8Mjgv-pZZ1u2R#~2jjdz7!{^z)^po6X8CJ+zm- zHk&PCnuY45eXBUmc<-(-Q%+X)HLdkgoU?t}hBpz%j(<3EdFmNsQ8D5TYWUS5Xt{uC1ck$i{uqHD-9d&>RehjzlSMk;`_#Up{A{==O*L_q-rf=n-w^9 z)wHXumQoKq`hJ}MVE&Z;zB)ofbZ%yaByua=IjC*#E> zu^}FdXZ>iuKVN^JMAaNFf4RTf>F)XiLs_L-Z8KHGhq zF?HqwzBvoKm#+S~>)1(Ak>|Uvrq5WVu}Cn*{*XcDV@pAwv-Z5g|3c1r=z5%K|9Ern zQWxz8jnr4Y>e_x&Wv7^@tgtF>Ha=bKqg(EEde-_d`GrpkEw%R?*RGKcic)o1YPD{g zl+tSdyv#D5S=q0b?5LKRBHp_!sMyfv(axW_E22|bCr)Nx%W`qC*r%`49wyx85C4(8 zAT2h_{1zv}DQ}aNQM(a?|TynxWs8Gwdq4^YU`> za^G`v&8pu+y}(0IrH+e@p9jD^Np<_%(Lg_n_Gd@8ZSQsQc!XRuB`^DZN4#7 zf$zDw#!8pZO)6vMIe6u|kw$6jtfO5MBbk(!%d$0aGnh!5J)OwnZ@GF)>n67Q{pnC6bAx?d{L^+n?{d-@UG5 z#fn~ciHFp}F#y!)H{AW0_)?eh{vaih_XV3Z@ zxLioeQu3P0^N7jQs?vCSj-N8$xZ3!}*PU;w?tHV&tCreq+aEpe_}cTy*Pd^DU5t>B z+m0cTb>^FCS~Wr?JhIqyP*Kcv|K-0IGjFM7PStEJ-L-J}TUGy= zou~fH_|NeDPSz`BX2-gqsXbRK4!UKXJ+7~@;=($UWAdv|phevZ2&{SdYvJeiSMBHI z;X-vQK!U#(LL|UatD1eXdy-Cj?nr+can7^7dHR{QO=pB|&v!C|IWGE@`^fxAXSHAX#p-Zq^RDpO?1w7fwLhNmW3tPx z-S^`Em_OogEScqDCw{ubyDw+i+$TDl`8b=@XB=5`us!4aGuJ(G4h&AJ|u z@xJrql(nhrwp_kjx@C*X+e^KfUYEW|K4UD>UN`ZrP(jkQ$VM*am?+mRo2I(BEh={o z^PG6!ZDPKLw|=I^b*)Rfs?#@mUE6VOXOQh8KOR4yP1lxY&dPk(UFlTmBDhX9lT+ui zW?zfwTI)rZB6nwrtP{B&@vRdiw=!tQ@hzSQyPeXKc7(^8e2@E+XL2R~-p?SB8Chyc zuG|TWEPb+-C2LMti_JP~`)tPKt(r@YpEfd9zNJ3pKf~s0x{@a+WOtPt>Z;BSJX2*o zd(FYrsR3GB6yD7Kl`bq|oOUS4yyu= zGJ}>GGq)G>?48+?k>O?dSN>P;%u4q^aV3gg_%O^>V z<7mKvIp?3bJhDHl9sJDkmU&6H;f!f10V^)Nz2Mb5WveOg*L&UFtgoaQx$MnV zZ69%_B~fRsR~A?|Ge4u(mgSMHn=^w}MkysJ&s?Xo+jz29 z*y^UoO-t(C|1D073;fR@c%rvI$c(Qz_CEvvzx>>0)bMU3O+q$L%k*Z*tX-&FU5X=^VN{Fp++o0Do9OAHfDHqBhKLdw`9Ls;}(l$}qy&YI;VC*xc!B3t%bh4vQJ zztT~gQEf44e^yY>?j;eu z^uv6|YSl?OR^Uu5XyNRdZI*j1B&gscem2 z4UV>fMke#W?bE|ct1N%`Hu&C}HDv{xFi)DJeO7CRaL<06x|Hi_ z)Jui=hw1`vE!lK&rBBwb8)|R=o!>ms>(b0?+P^KIF7}C<(IZ^Lb9JT9ORuD(nL3xw zT(;skXj<*u!OD2=?#yX#|NXnaP};aCZ`!64T`#P5cbr>l{rb}OoYNL|Ks#HQKZKf!8KA!=VkmBpXZS@!5_x8{z$Nl2PeMM0XcAuEFk{0l@gEpB6~x%elT zX)tA9JAcSK3yi)1(**}YpznZ-!vhwUTojuQ9i^T_D<2rTL<+#@Cb2|H$ zuZgVOKLslHXVJOItnOQlmt4EVcV)%HkP~;^z&aq-?m9aCs(HlZ%~97yPfaaCKS$I6CtO$>%fV3ArIChS*m$nj}$7~t+VB*@!BGfO`oBb?_0U+==3>v zlTP()TG>5AbJ^SdPG)IO?#|fye)UU_nVuoMjF9liyRNUf_tN5~yq={|!%w zMMRCgv!rXmgIU>P!G+2`kDu30Xf+HuBe7xnk)soYMb^eBhum4e!Nw;-PEF@zwdu*V z*?tqZzKH#O*+g6_ZLM9PdB%*)1|`u8;yGtec1rH{oN5xLv+R+hBYS7`nyZW}W_cZ* za@b(zESKH+7kX#zG#8oVF8U-p@u*k%wLQ?H2;~2Dx29h+o%8-mk#g{N^IY#!+22y1 z#BM6iyQP0sU;A6y)AzYm;<;6ZE1%rmY_9fqW~F$RWjw^|ns*#Onq<{D=PG^M3td%io(b-<3|(6>i>h z=RQbj(5L;72uRcTeznr&l(!MvBDnd%)tkPxckU@MoU^KYmi$bq`8#)KJT(Ocz)j2T zx#G9ZFeGTCWJqkEedKHB&AC~79tWS?cPURlV*8ArdlN&m)@ASex#7gji#zxHSo86{ z$-M1vPM_EvvS&TSPnQ)<4$M0HQU9RTqSQOD@@`I_C|%ck>*-U|%SL+_thl($B2jE| zh_CkY+FwhbK-2zpkO3*r?rw=+73cbE*^}QV9hd3aDxKPUX8oGTO?{!pZ41J}J$+7Z zN}WBq?#!Owo9&mZDjpRG%~F_AH?@`lnQ8{$yX9bhzg(2WRBU#%Zi3 z-{a#>sIkU)Y+YV4Z(_FgUBfuhwZHx<9$RwkvwE92%i$T14AXU1&*k2h_5_?9+_k?! z()E6pjx#UbnQolxRet5mT7><(f|3@yM^1@6tzRH+!V_L7sV94keM(l{vzJ}pdbGkd zx1`rB@M6ta$7-^ryTm;4)I_6Q*S6m@x%QwiQ*6@gu8nzrmUfpv6U_2?zw(;SEGZv< z?YC;vW=;9p9rd5#hP+jnDwpZXU{{khCaIrI`ktw&%=a|*DYrDZ_N_~CZ^0)m6i1eo z&f5F6R#P}EQiK;N8osNk@@jkhJ#M+>?2aojj2e%<)`nhgo3Sl8;KGA|@8^!r+it$2 zQ`_**IejNxu2+T?f@x>(HNHC9QFCL{)SnyEPT#rG?X+u-9FM2xTs3|-J8Rpt^vd^s zr``omiCVcw-dtkyv|TSgt?`XYNs&GJ+vxAoTYcege+h0jS9`fq=Y?9Es#9rdi7Fz* zx;;}%!{ylQ0#z>Wy2BrSqGiIJk?v&(|0%!?w zY0W03T^wh;%C9elW~IwZHmC7w`IL1<{DqobP=DA2CN=EITJ6`auU)X& zt>LoJ%tsU73Z4DDT6PEo@&*(P}7FuX>O3!oWWoR`!W#*!! z>}7U=Si!Pn@~tlAF=Fkm`@}Q$$iT z{;Z#pzUZRUmUB}VMtqB0k)?fVqn6dAe)c;V6SEdBJo5P%gVPL=pU{eXX;4VGLw>Sl zp!I7%Z-_fW=!l zRO>ATnw2fBeBZAqTi)7xJp9hgsn=ZYZb~uadUTm(>1CE>+0$0cnY~j&%VlrpTs4XJ zMhOQ`8u=GnO3gj*#-ncd-fH?5)fwxHo1bitJZ=8B@wwa?uh-Ky{p3};JN2r8Poc)d zn_JCIDhyLi`abem+C77mSZ71uuPWlmRGF+GmAUj9sN-bzSGp(7;eA23Q00f!W)?3O zJY-;NU~UwiH0zne=kC1As{(62^{2;vR(!%!mSQftT(m!PR^r7F3lZBxJ+FSrIr0fz z3Vu3&Ztb0=YtEl|?Y((ca$}E)uc3LC=Pd3SJCm2}_|dey^7@^ES&KiPKBKT&$*!dK zeC8a}P(NOe)H$Y8o_c&sxvw3sqI7%1s~P?_9`ho5ixyQnC@<7X>!XisQvSmlehu%Q$yJldtd1&C~KOJ$l;N7`BQfVzZUxEE$fie`mayvV5M7 zu+OfvxMG)s&o6v#oqbB@{5+f25{&V)e^t*vt@*?)YfsegWe)t+;ZrZI+0-3nvg-9| z*Gqe62F|>|9P@R1ir1$DKX+8mJk`&(amuAVd(XHFKb;dfU&k^?EZ(j*De=?mbDT?k zjn*6K)@NkRSU=NEM>4i(tA$D8+BugsF3qrb@@lOfq#C`nQs>aZhWvzMA%4FuPlAT} zgoR-njg5CRukn!ivE#^r{KT|!*-WvTc;_`L%O0;yT=g>lx|C{V_02QudGxZyiuOLd z#9;31aOs#>n%1+ux0vtV{PJC-bcV}S^NDFS^WE2Iytnl_a`?`X=JTCBKBwLvdCl=K z25SAyGpp~1d}3&u@xQ)F6eX2oEn<3EH|m{=E~P|WM`f^A9%?v zP**j|CqtHV=l2q?Pv3RxuSk`Z7oV0`ejs$UZ)L)(&d9kA>zTMzOp>x@fBby@oT~3L z^|(TVON-AO?uB#~cdnVd(@HjlGt_I!$~D&^*(rbG8(!^28?$e`nQC<0wu(zI{r0p@ zA1SLB4-Vd3v}tDJ>NY{Ivp$;+ED`2PjC#6j&jM@V)IO_{n!RU@n(rn2JpO6rr*(4S zXT3z%YA4^p`sU3x9m&*mK}02VyWu6wT4SG5*~ZZ-^A*Z8NTjExbp6r^frUqx# zi-XUspA&8JPFv{G&UM=N?#?+Id+_Ra9u+Y&2!S>(rRFVoY`jeUkzCsHz?mDDj~t=+HY zIZAjcJzQ%7>D;>gwmNrRMze2jsp728k7cfhqnkZuOyOF6*~ix*WAUrkox6iXVoF|W zFW+Ai`K?-2hG|!isY6-nWuKUr=kH|d+~@v$@-b`rbm6BnKii&mi+j49XHnK!!>D&L zM$5Wfx)?iV?$S8uHMR593{{UAb}7EM=Q|`HJ<+!MNe1uHr(wot-&dvbrW#oHt&K0R z){eR?^ZK05Zl41Ze7j~mjhbkm7Nyy%^=e(mtVI`M<2BqFg*{(#T{yJV;95f1u1BXg z+iY3gYH%s@7^L>Q6Xto>O5BqvG-#<-wjQK8`)$frZJ#2auFN%&lgl<5E|m(Fk(zME z=gqY(Kb<3=mAt+$veIYPjK^a8w+O9^u-6MzisnD^!l(B7U(bTs7k?i7;P;`l+;aM} zg~yDh@UbNM+E2JVQHtwB+nQ5*Ux(kbldLbVUcaMl^7+Ocmws=u-@Z;bV`=r*#3@l_ z8X>2}Ws_qi1ida6W&}K5XRu}JGO6!Qn={vDAM7@YJNN5qX+l7E)Gd>h!BWqHU7^u) z=%QF_Ms}&-oE805oS&gycyM#-I*W(1eJ?EbZ<~5vHmYc8K|_?PtdGge$W@81*)3W- z*Xi%?oRqCJ+0*i0x%y;@6Hi{>dB5)}Z)oOWjVX~OmMIg{-4a={lG_&FJ9Osk^&R&` zrg?fXK9Rn0dE$x-)t{9zrJN(%c03NRuVGjaaK&jyP3XFMTgz)R9E40R_gN-Fo5O}P zL*E71xrj`1@5+QWa6T`cJ+s~G&;C2d&Yrotr^{dE{_~R-6O3%UhOq%~dD$HO~On-R&?`hp>DfKfR-iMm1t@57rlB0hoq};MUU@CK5eKD{4W$pQ= zV`Mw;s?GbX##CJF-uz-?TIU+?H_v94ax0Yz)$eJjxKPM)I?~#}kyn|!QwD_6a zR#_T_nf!0g-u&1T{zDerU%4Z@@@lMwhU`&=C6jhM4)v|--Uc4ssti40a-?W z%wEqJmwJ5Twimo)+jPEXe@`{*e93x=Z`}4G`+KTc=4n>B%y?j$#&p}+ z#-^V+zFDSiHc*?%dhL-`xVPNF^hZ|i8zMhW|Ja|f`LX_Exr6D);*Z%j`3wFy=<@fq z&BV*j&bwPQeMLQH`hByLHa5R-?f53|OvU!B6l=dJD^|kPL#^+42@{Btyf`&;$HF*+ zZ)@fR`|R7If5=zp#YEZx0l!JFTferJ|{mh8E`rCq-E%`LVn;gDmK&ve$OXQ#Iu zYc)J`*=;eeD&J+F3r;Pv{pnhabFO6eE!t(Au%PAKr;8-=%5 zmuz~S?X>$fe^xD9XZE7lU(e2d@9Ilt&pTG48uK)UIV!7IaYazylaud<5_{;C=; z7d1hL3jQ1)t^Aj7xu)Pn25v)FkKh zsh8DZlf#-DShdzI+WVS!{#D-jSA|ZUSQwezv_;EGbg9qmZ}YeGPM;}tf9DR#=b5@u ztX!*?GZZLp{lo);8*>wCNC@8vjePlL$SUe6s251ef~{|aR3(@B@Y zI&U33DYWCF#P;b&T$gP2y*-^#+{o1X1ayc-bY9ifOH2(eTU6^*FU?v9DdC<51qk{G ztn*yOxu^JFfbXOEYiwt7d8K`qil{cav~@PWy4+(k)i;qLS@T7%xGdSlHFL+(-~O7( zA$ykfPM%nH5P_iPR>#XsVWybcpY~LD1q!cxKS!H7Nq_|>N?#}r=`x*Q8rMxUr-jA$~ zF4?EN#%O)b2CvJZase{RA$z)^Lyue43VgZOnL!7{YUiwFxV)IRFjaqk#D;0J(z4jk z?Q`*nx+M}jGd8=|oHb(hcFWGq`hWey)}COS#&>3Rv3$<1>RSSkoqCH-_;S?MdS zuJpEZ=V@tmv$tjOCEMTHXEGX1Ssbxx`uR1H-p{Y;Jl(YUe$;-|L#l7Qo?9hd*5Wtg zI`eGD`s$J!JK3~%@IuQ<*dV*`CsrHmM?cF@#h3s$E^emmuIk_q&&#-~nv1I?2EmsoczZKcsn7!@A z^q+o@z4xy1cXr#p<81Th^wr+?rItQ9cJjpTq`abIC(rKw&+zK^(*4ojrhB)|oWuP5 zh0Ff!+l`B+h3^;moRoLxaKOLd9bdEVTriH%)SG#kf8y->jiR$Io=X?~!h4jXI{3wu z^Uq#%F-}McpDkVV_i}u~8IOzAK{GCtES@ZP>c^8mi*L%y%uNc5T$<6Bd@rH1W4grT z)9!x8%tv~IK0#ZZ2hQ6TUSgVJni|F>v}4_r3$dH^mCXK1Ke5lW*_t-nv9;jrd4`4D zChv+Z|7>A<^juuwXskohp_`HWPuUCQmy`=?YGf!K-?A%&`N^V zOfTqk)9lMyuM`+A#ME8795``bx`H>~y?x@lmp{kMyz`PmZo#-j}yAJM;1EHBq-J zp1l^^HiQ53*=wRvp~VGljh5lzM@|2(Oj{}zI@R}!*U=^aEO&v4gU+_Em&R(ZdoEU) zk-bQt$y5fK$v`EYuHmIFj@Rd)?Yy52e`nA3ow3EnJpzoUcV5%w7ervykFtp?&YCN! zEjCXzzqEBp+P^P5h4W2)pBy>xQmo^pV7K#;QWWf2F*h-FQR=PwrRVVe^|VcE|>MW zjhXr#uf^Jh*MQ3okOuWFMeKZEFGxkq-kS031#i$Selw|7u9*pepP}U;v(4Az9=sX_ zt64eYJ4==|L55B5r}=t%g-9i2cXQULdu*6~sHB^-LRRTe9J|Mc>5Z3kR3DUpWv1|* zZU~-Y;xkDqD#c3e{gtB=jb1t?VJHI`&=an3_J!2L)eA02b=>@_bSRz^Y7EF+I0tMS z$bt`Z6&Cn3WE_%GXSwpON8NaO*yl(wq3i#aLtTTUP|3;Ji*eeHOf{cdvBwrc+Oc@J-ApP`dC?CrVC8*G}AkY44ZwcOYfjtrJX@K`UEbms#E325t@&hF)-; z82Q3m=fHyG5Farel@D{3Z$9~O`jYKDf6hxNT%NrU8IYjTA&Bg0uo9(1{yck8guvq1 z2(amM!5ZMEfz=_4+0H{Z47VVlucA zS0)@NjJ?G_7gB)l{R?#~ZI1L(hU!DT=~>n05;P#>EaQ~go0kxTT7poLB^o2xxq&D z=1dFT8k!+Gl_yb6X5xzP=00It*V!uNU6^vV`g7-p@_mKfvx{9j5AB-rrE{Zk)MK$J z=K3DzgnAR17W)-kN>Y0p{OnkjqUaJuuk3TW)@~M|i9(5o_clyho2XTA`I;`5l-8$q zwXI3l8W*kcNeEn{Fzb72$Rrf)60Z!BvCpgxXV&sylhB&eX_NtfdmVP7h~g z&SG4eR5&Yb-NFR{$!U4Mw_PH;*BrE-k*(eF+SK|4Qvg%4ThkKLss9tn%Wgq(k}K zVvQ!Nk}fXmm{T33*q0d<{7f+2Wbss&ZncC%3w9joSv}Rub;^mCtV(X7$2xtKPNft% zx~Zm?s!D8{IrSvF#MG%KsuCM#%DDA#^a`Ham}uVGxHi+*^s1O}MTBSpv&F_4JTVee zXO^%=Y?{GyEret0%yW%lI?I?%1v(`si?t@5%S>IX)hU*9a4zeT$g>(x1GUA9eI!D} zR8rT4YDR3Dl^mgYOf|K5QIO8E)FMZZsZ*~^NjjAlt}@k4HPx5XO()G=m|a8Az};fQ z>_Z2xZCN(!smF%d%}lGkGS{uxIIHcMYFaFtj>05SlkV;~-K3(-0ITURo&-Qw=z54;+T|UJS*{M($O^sQiK&MkDS^S zwq9b}44$sNiJ?bhejMF%PkoJUcGSytJGRe0Y90}2xNe?du95v!^N5+k9v2@AP1c!o z)hCAQ>f?wBQ>P_HI+VT7!Hcs<3R@coD zVVZl8S8dAylSwo8zkOY~|KPi4_1pf0c{^U#IU_nP(fSx$wyiK*@2?rh+>Ni?b6ptj z+_7f;`)^!pM0~|9PA_)Kls%Ji(B!nK zp|Z>7qC7;YG;_+VtzO}b1?zUXcE&E3+_BT`&i&$l$F{$#Z@zo}Z|};=iAj1FTaKSO zCaStfSG&W?`W>w5y9FKfxPSHA*|IzLi%ZUIN)FL_cUU4Tba`3F+!;<=HQha`XFdBp z|H1d-@9L@3`V_-)Esa4D7pSzGGUH6mI{YLD^7##&r$G1&PxH*IzO_zXQos zrxYp6Su@x?p9PxewTHrM)|>18Gfc|g*}Hf$)6I*gcD^_**{q%4}8<^+Z8@n!BZnW=HpuJz7?SGxD&f7ej8*p#~qeQ7VD-5 zu9Hbi<@MI}xl+AkFK&G)oqo4$W!-H^1E=uA ziJo(PZ{MT`&HefkG8~`xZ~eAxP-?mLCCTri%d*Iq3aYEN7%82Z)@pG1G_U*4wUC6Z zb8?lMMdf3m8K%p6Ui-ZjO!IcooG;FC>r3zEcZg`-Z3Ui*oVDr1f(*zgTrVi!l>eDr zdnUKapnkb~T$Q#@rs&_f1?x)Ia0E+Tx1Tq+?%B?r`$2)vt!uEf_-FE6=HFJ2*Kdw1 zX@0OU=FsldeIdPd6Svh~&zk-gT5bD!raPKgKZ^3UTD%Z4glHZ*De78ljLRuq&R)ft z_tI)k+@Ad|;YUmMLXDqC{!SFz^>V?w88c>x2>V0{Uy(7X-1ltz>_2kv>YgrC;0`bA zIrn1*E7uFdrPW)GF3(u-U9WA1V`yFAY?Jjf#g?>n?bv%P|3uffC{`~E#;E+IkbDAJ zuGsS`c;8;APd{%ulhS1ua5ZY(_q9i(^ww}_^vX~1cGXIbFOCKkQ+KLc1Jm;V^JmT`c^X9VR(0IFW$ZYnTSJ(HgU#gUKTB&jIK1ko@UT73cvdPt1 z=}`%?N28ae7OmtB7cJRnZ6u=g1YY#0r3OtkHMl5s-M93}nX{Tv~YdV(E{UD2@aF4GiF_`UVs1EUvRGb zEOdI-ih#SZ2EB8cChal}X?#0#Yrx(6%b*<=tx1cG7npCH(Rn}MX*49K)j5anIJ)kQ z;(rFec=P!3-%|JTKUD9W|EB)WwyDm6Q{yKDNv+d{4l07?Et5ZS%ziM@!RUP{w7dFk zs>XZM%KFV~AtlSd!%+<7lCsBlLWX$fHL`INtf-;c&Li@xjJpQ^p!So(fPUe?v2GoykwZZ=nY=oy%i zl3nW9v!cI>U+A@>D%0XVR->f6Gq>hgSQ>om^_zsvW`@F!IX8XC9@YD-_RS@7&K*>V4}`pXNIe>YA-guQCjV= zo@3)%U9H9#udAi5++kgkk7r!wn)v3yq^m(uiH^_37%f{Jc=>Bm&J-&-tSw@m;!(ES z=&{rOy?rKYJhlErEZCX7#KU-(Ky}jw)~E{_(hWN_UJC0O>N>CsoYwj7s=ZUH{PNvt zn{AlAb}@U4%(^z?%*?zgJ^c2U)ZhB9IQsKdw-(3Lm_p5og*{ipqeCoMLZwVZ9{W7r zuDx^pB*UzzJTH57!EQ6pUQYj+3(co0rfh{jrMLAhla5PW-lAHxHfY{PqseEr`!qY;PM_($w{gqor1RSnf|`4c zkLmnojCvrt*3@P0#)Uqn`Q3I0I2X$aCg0jzAT^cQxyyRBo9dgjGAjd}h0NkovMmL* z^^aI|F1gOe>UGd<()0feNk)>Z?}|odwNFyp{cOpk8P~No+9@u!{TEexX|qp*RG8tG z8P>tsrP3WUrUf^0>&*HT*Wz_%*P?_wNng6w*E{jI797_)w))MXBDNm#pdV&J7&{-tXTemb|6+%MU?s?+PTNv+ahxz9^PQ)h+ntkjMY zR8QZeR(ek5ZQ06YQ!`IsC_+2V0l@$oRc%lh@A;mxBL9)L))@xO#1ms&Osvhk(!3 zx4#}anmN_KDDKK;5&zGgGhQSM91D+G=aa6dUN-s3F_Vis&*>~n6%`2H^e81mV$2$fp)VXx9Lu<(N~-I{y!JvPlg1Y#JM zbZhQ`5t0xVh9t;Lh-Q$PAfv!GpfPyvLM?u1?y+e`n$wC4tG#x(F$m2)*!{U}a#Z@R zU^~NU(*s$V??{DPB^I|W*EndbZ)Yu2{w?OjiNbdUH8YMZ>-}ICn=t$I8sW;)rhL5( z1_lOpu?h9(=kW0G|NPw8_@BY9z`*|bIrh_Qc=)GB$m?wY$%7T}PmfUh^qQIZU(JJr zng#Xe=kWjh+!?R8AwY43iFJ?V)n>_^8Lul@WAm5qTzt+b?^NQ{cV2EqEB?%KZMeYo zZS(R|wsSt#O?>mb`%8biP5hE-%SV5eZ=M%_DR-`7zsCj)k#EIEf4Og-r+*2DHZgW| zgD^#iHoBXk{_QX4uAggCL>`vqq%XhsFMRILvS**O|1%uDR=nSRpMZ?q1{t}HCU;lf z+TvA|RQY3mkH5X}$Ij=E>I`lE_#acB-}&Rn=a2Rh^Xk+e3!gvoX8@I`&04(XPh!n3 zr=KOQ-|hb&VK9Zef+4$+v7?fKgK~gVqKay8A$ac- zGx!put1p(gz5kVWPB6v(KZEybiPbWBcUHbV{!TMWO#4od#ropwx!jVDUf0(ieHAis zjo+u3_p0Y$gbs*}!~^qV-s`*vGtPYiNr9N3Aq>-VpOJNg%vrY2J84JuN|vP!>wKpQ z1~;t^U45|ZQD@s6hJPuiYNi*KT}&2Ayus+)bE5DVTUI1TwYrjt$Ic7q+DeQU&gFTL z?rbH|fW(nRmOKveRTJ}beg$g)K140fr853A(>Juwn=5Sgw{lgetNX(YvG@Y^-^c8qcV~!n_WXCD>~O^ z3pe@fXl4~(nYmKM*HvQ1(U@h+YMv|KJ7$vWW}FzkDR<NNn87O;aSs;dfsZ8gO(?*gvQ3M4lCLvB5NKuH8iPz(^k2z z?$l)$Pc7+G(mwDtQM4~F#AlLalXTIy4CTWLCzt-3ADpk1+CL{|NrN==&emQo8@CU9 z-<>wugo@T4n?2j(ippB^W&M@PGZhc>Fqkmtf84D+XJSXIsOp-V!2xMk_iwOV_Rn-D z_tV?6Rv$^rjOV>={LFm&kIyN=Cz+=vzkC_&m!vY+N3TNWr;yjC32)A-$UilDb^k<> z&1r#739crQ4KqH=_}Z>+Tco{clS}$b`8zoqKXlJZJ|DYPw|4IWTNCZ9$(Lo7JuXQF zOrA4YWn*u0;MyA>9aZ-%+tVPy?$);XJfB)t-tsH=&%1NkWF#wmI(|+sGhSnU$*;#* zueYnYbZ{s*FmwNwox;(bViJ-aSg~yFA9j;B4cqol&7RY`EopmbMbBAd6~>My6@AK! zR$ISGZH3!tyX1N)tfq@>cc6U%$N&1JeM;qT9>tM?cal^Bn^+p znC)?s3=Hl5tUaqwi!D9Sr2m~+#E;aodUJHdlcqLEcl_#Nf93gp`@CaYd)>CbxmxG( zY4dr$>l+%?q)&KhUVL&m;rKMW!eXyVm)~*c%2o%NBpeZ9v9?{cHpFX{WWh3n^QR@4 zH});6G`aH4x;ZoB8uzbbdm5q{6pr3DE^6EG^@NMbT3dtTm4dUfvMj|jAC$~kA}sE6 z_S7uRf@cOtLLSW#{gsrpw^E7yGGo)}TbpmJxDvb3TI2QE7^keVcvSrf(ub}BF$5S?HbX?oo>*BvX^e6MF^ET0OXZz)ZZ~WD3i8lL` z+CL}f(!4EK7%!}!%j-Co`_GFk9uSIu?i`Tr^q;{>s@Y2X=Y=bhAhiEn z>%S7Ho??g|?msU;N>kW;6RxVIt@`9XbzVi;o(@idj!%L9{aVs2HXA>8&k8=Je)D+B ze+GZ~&3%QxEB^kezj(Yh^hv+A&4;(OHi8THxLkVSx;bmRPR*ldlRtHCTef+6+{Yz# zkGGryp{)nAvJKaZuDH^;A+EFN*}FqJpIBm#XU;0Rm-F(J@~djg0xq-T@3dn;$U5d7 z7|khS+tHR@7?oZa^}O)b^TMd7u1{NLJ{HV>DkZ`$qP=3R@s72{JJuTScw6if={@h! zl1_G+M2jW!S3Q{;x^dtB%=z(KPVAa@MEmI1X$!W9TYR;hvVu>@9bz_wY_Cs=R8GEZ z8b8Tq&F910mS+l`J@v|?v7aMPF5>6WvzkwZbJPDc{*HLgd*azOXrCy2G%H7g_ zQZL(HAwKJ$(d9?n-!A-)VmI|ay8G?L%zr_~5nfdakJWE3e`=@O@gv*+9{)D>Su#FX z>l{99J}-8CqeF#Cp@vTK>5c=NK>;=8Q|GqLe;?jjwKwO<7a_O)f~NIpw_+=<@|*IX znx3opDSP^s)UC>L=gv-?)Y zPy2Iap9!1V9~I8}&tPCNW2f;cy<6drX6{=JPHWq)oKcG08|y8h&}51n?zcVv%u3wf zdHZF4mt2m;$O6sa);W`Jc?&riOT{gwD0INw7U~Ym(8C^%8Se)P?NMl%EtkS$CfU$OSicmVR7* zSMJ$zmRI&BRjUrG%LaeUyWfk22B`#df*-TrlQUSQd! z{bF|~L(<#Pt$%weHpa2PESz}Fw-_A2{u;Bh{h#oi4L7Y=cB^bcddQTVEAd*fTl!Dx zot(C>a@!?4a0vgMW9+WZ;c^yi;al&I3yx-4ii&;rTa#KcV|Qqb+?x51xf0S@xnyD> z-u1a@4DqkU%$3L7ZrYw(+NaE(fJ|NV5LvBeZCvMWO#| z)Bb6;Tk=0o2W9_^{pW1_k3l`V8{*maT)#uAv!`C!*u4w@vtIdhhlV-fa^;#BRNQY<{ioN9X&X-2AX-LgK$;(Kq;S?n*ip zb9rz3HMxjy7uRQ}OkKSyRA}Rgozg##_x?$Ga$aM1W_(xPuk|P7!YUu0ldKd`Z*Ujt zD^NkymA51RGbof@ny)g`;7zaOlRCKF6MyRXu7#xP+mWoF5=ET(CeHct=;Mp?{~4C% zuw8facysHWVEUl|j#*`ADk6jq?2$hh{CSr7Nxf`|ch4j*OSre5_;_NjT9lHsa=pbz zdug9f-P@-A*p|QQPvg_{d9v%5H<_4s{)(0VYWC@A|D2dh^8&%4u=u2j`{$NROFZM$ zj+ve>GTk~cNW`$+RQ%L*i?m6?mk!45w)b(dyjRTBwZ-m*;Rerxvpu&q^M-Qe#`5kv zT(&9xpUyS!<9DofDNk=yj&Yo}qS@!cjEKUXu70)IYxHH-E#{4!HozJW6}X z(J}wYTSdX(8@DXh`YBHJPUP;|$NT1mR>!T=KepLO?9WtYIC-G`^47L<=Udi2o{+sb z+;{>g&^l*DKf2xBISUerx4wx~8gzUL@?z<5&Hy*iK;?t|6F#?fGj;1f9KB@J`L1GD z)E>@%i9Hf;-Mats6`6{LJrCN^^}u=Uim4g@bk;9uWfZLY`Nd%QqE$C8pITime(6;> zZ-B7ii6^f9UrsJsKW)_`&x>dO9ADIyArPQ;8=OgOtmC{`DpV3pc1_f|)dS5VIS~u2 zCq8j;Th4N4)8gZPDtZgo2qk5?P5z<1+;iHx*R`{nKBnwUYU*mdJe98yR2Ee4JEU-C zVRhW~<;L~a6LO42^7kx=kn!WSuGn~IVoL2a<0VIm18h$m{4EpJwqbvxS6|5^`>oJo zDmtrNFlSEx!u9<^%3QEw`F_vDY&NA2TCdDzRyaSuBDDANi=zJb8c}&$>fVWPH2$4a zq0YZO=+-ZXUx@)%bSM6tcg-Z%ZM$ei^?H$zM>DfZCtEBzWvCLharMlVIT7w!jB$&y zCZ}&)pSAV+Rqd#{w9MI|)8zf)H}}iu=y0@sS1h@kX?yua(BdC^N?GDd^A-hEPJH{| z)Pv^#41auJ?a{9frt&_kH9V4`>;~!xfRgoGmg1wAYO1VWocyPoWhf|IZYlY0>9z*d z2QM}qHK+}ElWb%$Cw#{8t+Uph5V*-?>~MCS?}fF-AB>i`$0_qI*~C>4|Gw+Q6@&RP zAKKQ)t;%SfQ}K85L!+8>){kehX0CY3DN(r4eap(EHob+e*)03cX5aeq=b!!o-4hfXl!UzndN;Xk6^1r9w##;_7+&s7$%;^V)MK{F zYSn_?@}&BcT+aI9TUV`f@d&)6Q^sj_bDYlmqvCdY(+YQ{xv^kTPXWx~5BFDfw z$wi`9$fZ7M>$$8&OL*PQgcirfCvhu$b=|kN^|E!!cF!}xE(PM57ypI?af@2$vr2l1 zJ~D`nVE(`xQubRyY8q$lJjsMfb6la7f7z8M%4_EIFI?L%r0oNX>vJL5p{Lb;l}rtB zw>%%Gm(UUVz#t$(x{cjSu=bR!DQoA!2kTo{UF3ev`I{|nsG+Qq^>F#^s=C(9@LUzS z%5{2{g27F91KcfS5>i>NJI_16Il#?R^vGFV<;6)ov1i!??;dEq9Kd;e%C5kd@00@~ z*g1qa?{I)FR+y;w>{wA@)}lpPuy*kNwJa;v8NcjPIv{1Nv*!`0!t9>LtG!5YVgFLE ze_REbzkd9Ct@du-T2W5*uR=R&0)*-V-kknpRBC!=X{h?kFYZ>B>C;nA=0zIc+X3pz zZ!bNYQ0H-2C~e})4Jx63o^E;Q*!A36>(Jw~0=IX%wMoy|dM+S>x986gSbmC z*CoffePuWs{)AuK=EHNID^HZ)%()&A+N4r87v|2bXCkjnY82Apzr3^6C9=RI)I^VK z5yzo>fjiG8hn|y^2wOZeK80Itr<0|Tyy3&LlRppWyH)Aw-_A<4c{G_p)iBB=;!)s& zx!Qi$J^iz@lCD~c28OQcnw9nNb6~Rk8}WaYFTd$DFS)|yzmbPEN%U#LjP**EG94=% zHk^H0b63*WylcsShRZvXTqCNabY5+mr*a0;YUjKDwPC8{C0U;^A%>kspmwmo#_V4k z6fbZmn|zc}5uK7Lv{>WuE+ZjxhYO3k1O%AcJdS2hvv|(1DOPg#9nOnuyzX@62g|nJ zJ9lzwgVWw$tGHX%`Ip9NFK@iQNlxqSs+c3|Ldr6a7DaJSuNMAQk*XKam=eipcigY< zW~OM$G=r&oDP*7oJ^>^!h1Wk64Y3?j+xrr5Lc^{ zWOS764zwZrT7;{_>fE;8xNip6KBXS(>+edN7sHwrdep?DL-@ej-k3fI(?w2N%YNWL~J112qqa@+XKVUtSX zOrNVTr)CxH{ic_^<*SDhNz_m4;Ue7X5 zr_7dR_&aCCwN-1HERGk7WPW+IBve@BXtqGIqoKq-?QgHLLwe-BY>lH|tCr6HW)Uow zWvhN-x~%)l=e|4)D|?K%JOqErHb`7tuyVQAa={>#n@=|?aBhQkr*YW!y%&rtN5HEEc`BSW$0i#c?+6vcJYMIRoVTwT;%ZRMZQD&IOz|GcU8+N}Cpzs?B7Fk__cRg;nICmCk6UDz(VXz=sC?f!4}I3eI9jx z`{)AVh))d+OwT-F^ke6wk~I&0cT7k-_V?^mowyU5mI~i=XG>XM=6dOK--@Np%M}jC zr7zkl+*IiEXy#Gzw~uBXHSS6mIa-u15_{PcJnD0J+lnbc=L6QwTQ1^g76fe+n=3}J zY2P=x<~n(*^DEYQ3o5URl^&g2SYM!fKFUR8+s<`amPXeel)gN1Xu^rFk#h@^=0si> zt7=?Udi-zL?OB{cHea*lI(D8o_D^^1(WrA0&K|DMW`YzvkBNN6-m4wAXU)o;#rI#T zz0E9fvQ50caz^*Tsz>JoY`!v|+ws9DrL$_oYn_8BdWDbDz$vMq?25^wN$Or+Qy<64 zuBidneS2Q6+fpf0Ssbq!xN=T$--qWVvlf`T9J+o=#&qwN+6>`k$-2dNkA}wV3Gcrx zlw`PK+*LcJd_{h$Rw|y_zRB{54#)E; zhP}y0e!IALzWB!QD$nR~sPC^9Gx=QTAXDDTFOSov&q_Sw@f(_|OFt}m&XX+C@3zo2 zQlLw?`0`^$rg*_)x9sP#o7QhkPF+7QcHxo4sK>MC&2KBd{K&19q;6&h&xT4wf=8JxnRI*8DzH;nfy>ij~XQ@y2&zibp(cwO}Y3a6h#g`wh^7@jQ zRI7AY;ri*G%L2QSAI;ph3e?1^*mhOeE3&V5R?$plQ#E^N`(XRmdAnSUMHl_rbH{t; zi9G=dlQR`h-1kbgcWlbgnm+e8^SS0(`+p`sT|Y0j^`qK}kjv`59-^1cDnpLu+zY)C z=fiXLAGh(V&!NJ0>#e!dbsh)WPJLceA2?BAR=l`Mvm1w)PL22{wUC-Cd!~OD+P;yK zgX7M;S#DLyk0VW29+dm({P6rWtwoDo_IJtMS`Tg%+zz~K<{g%-wWHR?8X9y(nG&g0 zd#kPFA_7Chj5{4!-H$xeZ8GeVn4u_e-i`m#(RF@Cub)mTT_N)}{?Eiu*U#%6`Y}0e z;s{m!~kFlh@Cg(VGq>+~`Ta^8EQ#_4KRi32V9bL~7|f?G0$@c%W0ZrUsH1#jf3# z$+`6T%*jm2B50Yhr&n0?{q(xbynt&HK0dOHYqLz*>k-47^+|E5&_$-JJQ5RB{L~dR zzr4tv%HtdVXCvITKfb4boiMp|_4L9@1GD*=Vms#?n{JodZFwe|MOY-@Lvp1|Pm*&l zqvdk1jV8+$T-w$b+O=rgx>dFjR~JwHEVSM0#~Xor^`7zbChb>siMX?BX3+EcXK9(G zp{tT|SN)j?>e+5}`79K@SZ&VIFK0blphZ%?R@4nmUp<#kvU}B1JltojYWgbhdU}Je z!ltwKHCou1Uw-!efG$k}#Ee-sUd3#1cl1AUGd21_{g?-d*K5@78 zsl!?4q^K7i3nvshD19_Mt@z5RuE;j~&bcj3Q3kw$2Oq846Si^7#vOLvrmDfhO%9tn zZDlV^>HfO1`j*m?iq+fs->WKZW684&{(W{<^rM;3I-=}K$i1bj4NC3hU>SS+*ZvPn za=N?AN`=_%R=oK&d-JRH=U3U*D?W*qNXc9OFXCt3akI*bCy{Aw{|Z%i7QZ@jI8x?t z;5(DBGfmgC z)UGP{`O=B=ru>+wqQ6PoIcWY*O;)AS**v?>AC`-DGhQwl{nCHlG&iWKpD$BVDwaDN z_o|;ceb!C+*p|YHB4IDzZCJAPRQF5%c~gGAOi5p2=f19ZqPJ4azuf^0mk-obUEKI{ z6=aBGt8L?a-K1r6mVP-gnG-TM;eY$rac`SU))#-{4%@9woFBH~{OTL&S96SCZIk?( z&9iDkq*-HO9Oyp!xR=*mLnn%u2`g`(P#?DO{OXAGt8a{7U6cHp%_kWdR_$Dn!@;ng zi^J;p5joYZP1WjG*MMcCd46qetBr!1epc!Gw7_{kHypIl5{|kuYsDOyY1=qDI=^Pa zRIN{JovM|!*wb~nkU6K&Hb{QsyVW=Sit;kP)sGKvg$&J@*Ueh1eCK=F);K8I8&?KF zTjSg=f4$h%CbcT*L&$@rmrGl#=j~k^J{Jl7b?xFva%5KzKfY1Q;P2Y-zaTSUy5Vex zmF+RB9u)dalrTE7NIyIDd3NZd%&a|c!Kl+Uo}(iWHg>f3pT*6kZ_Z5$QihFyXe|!z zx*xqT?!vLJlZvl-o{!YFO_}p`(&lSkVCEW-sMnF{>QGV7^O5SdF>}67D!zL1e7L%8 z%o>nd6wyeq+9I$5ZHQWsxgfO*w!N5oc+z#HPcQm#+z5WqtQtNRg85 zsqTY<=aN2z7H$0_(ic+BQaz{dL67^#@6SWZ1W#Rk(Dn3*>p6Xw$9+YglonU@`DwUY ziVBLJPx}3z;d|F}%L-+=Ro%yDzCIebKF&4tSv{Y{`Ugj19)0;9|2o(F<63xo$tu-q z&Bc&E6=j>i6F6$u|1(JVo_OM)l3ig~(f@57f4|m;{|qN)oJ;lpRPJuU3bTx^N(8|tvhD=V&AJdp~ZKDyLSJWY+*gUYt=`og6#N% z7UG{~9z9>A^q=8te+bBQ(@^){;mki`K!yrh1`3PHU2#3OUgYt%**~tmU9|^XcCMZL zpW*s!t$jAu&>Wt<(B=ZiG3}VEAu)mb`(?6Y-}SxRX|c#~&&tE;rlG4kuG#K;v9@?t zXK(i9o5CMEuDTZ0raP#hpgj;LBhZ~0830(2d?oHO>siDzF_XtMq z>6m%BXgOZYX}@$H}Js`RPgQC;yEiiorK&Qt_holgQn`hrIOYjhPTPj#WmMN#?+N_tT7gRb9z_$xnd8# z8}sLGwl&_C{ygXWxtsInZcd!L+1@(sFc{T8woa=nJM-nww=;j<&ir{h`13Y(h`P>Y zIWRr*=FbD^v$syG)0?>gYQ{zox9n_N*_l6YXY+vc8h_rFW7Icg0>>kdLxPP-6Tge; zY&5b3&Dm}H@i|31a)Nlrsx`+m_{wg-oT6{iaW*u0s<@ATNY#=0UpxN3{#NjxAzuDV zR_Ez66D8ldD@O+}n;^4%a(0H*hT=7+Jtt2Mxw%|ziRH=jUo-gPI9F)#{XFGy;m)g{ zi-X-}%Y};wK6mSRlDYU4%d%B1*9?{SMmTu#Ow(?P)<~%8ePFS~cuvaZHg#<|VV=kC zz27#tt-6{h5vF`&+Xuml^uRfDe%!9$e%r6ian#K#K7#b^69Lz$IIGFJ7PR60)ST4>r5U_W8OBuU+H zj%@*tcSd~h5Sdoov8|xxtMsA7xgC!Ct_jPm4V>7TWZRv~DBh-gW6g8_)dxZ!_9;so zty;Q8G{i+QLQMN)TFP{juCw+*1!sc2ey!oNF%CHMwT$oKwz+MSo-a2Fb8>a{^|v`V ztC=UHW~cIVmH_uhi%l#pg?Bni7)}guZ5BEIT){!(?E@u;j&SXJ3O&4Tyk9?@dRpD4 z#@cak*DmQh>x!2i>^z|N*lqREy2bIQPj_s|eAl;eUA1Um^2>-7D}H`$%i8)z)!wQ};|HJqk~J67~6EqLcHVDxJ7q6J-9#k=P; z%#M`Zvq&?u=V|q0&&H$7GE+ZtK`cIIwM%+xXv8tz71!40sD_5RZInLI8ObeoY-iWi z+#I!4S^_@CqWv4?m(B=Dn>i;^=#C_3G`A^FXm`R_pY}krqUC*0D~ ziAiT3P4+z$e8y@0udPn2B6r(|FT%5E}FIVwku0lQZ@S8@qT#ioJ7H+CDMxKg0FSmKFMXzZO5a z`=3E>)=$U3{ZF1gcz=w0^7L-|0G>rNL_Ag>+c-P(d~X`8ZxF)Lys7xEpJE?ka4UW@vH zAKRQ84jxE+Tc0YYlFoQ5CZ%+83~byBR2$E~=Jp|S{__2LZ#K{V^5axWU)+YMnAIDj z6?eYgx8kS!e}?1B_J!1^E^WHj$kk`A5i&E}HYfS(^smb2*qnZBe3S+bdq_RC)!@W! zbIU6mk8m&tKAjGky@Ghb2kZr}X&gyGb5au{T2dl6KHC~2`_=vAZg2=4xfgQ4Daq!P zn_-u0yNPi774}#5vv^uRCLj3&u>jJESa(3#L1|J~hN?4oS<7axN|)OKHJN7p4>sHT z+a=!AQb_A`E97lT;+>dyyzRP)G$M?=+U2sX(gQa}EsiL7d`MiuKJd?l6#Z^UAZ^bL zg$9y|;6j^}OMm@WZl4bAqi$R9<5Bw8iO@*adGOcGs8zXr0b94$>wR;~O)K(O{__PV z(W&w`GvmK1C~Q3RNK`B|{I8Y0yM2(&1GpudbHm>%faZ1W*Ic>lztVbMpGd`pwvVvM z&MWd#KL0kEx1ITG`?LJ%EZ>q+kAzoW0=Xk@zD=G~_G|uA`Tq=ZGk-e%otA0oGOzV- z+@w!e&5D@+Go1bFA~dPM!r4!>+13A8=J~$7Td-L%2^A;hlkNsWiM-9=E?3Q>+-%X$ ziF=&RbJp-a?JAl{g9g3`yXGn*RtPK zv;7d4U|-ditS@V3cpi8qbgff2wWp=vmc%KuV;z-wPkCLO9NR8H=jsn{eRIIQ?VGUo zN};oRz~g@Fr}&;wlkB>_agB6f@54#Y*f@4AlavZmloZNPH$BJpYw=TAsLv)XV-mJJ zvm{w!eZ=HOi`sxki?l;Ulmuq6)P{o`kKbc-VDrM$xmwjgfI;i%c!l zpt<|jHx8AdsC#Ei06y!PI#H-Re3p{b(o_$i0MaX1U>=AFZ#js)UTaw9#ly5uwOg;2U z`2AIRjrZUZ^RH0$-Tb_#%2C%$rSAA#jq?Dl-nupON$KQ^UraAPI+8dO(nE5&dpP-r z_Wp~j?0jz?u9Ggf-Bxf;@~}lZ&%-&!Am*}t0rfAEC60vTot(8AJd$zi-yWePoAb+z zxfx<+CO;QfydfrhrLlSCtv%8jYLYklr%YT;2I4>yB5}onNMTwNl%KW+9}X!=+TmZX2{xpH9W<*gbF zt4@5*;kJD7Ijr)~ttW>?m_ENOo=~PM9cq*}MP|~D#j}^EJ3U%)edoTL@hKVp&v{EH zd~V`?)i zpSoDbXG#GtWs+W!Y_VR8qF1uX9*8(OdCA=fm54@RCle|C zO@$|Com!@}C-2g%T{;RP!4LdcI|3g3Ryir+sOg#qbCmDy;6eV4n`gPvQtYtd0f|h0cXOP*srgjET?>n{> zk0Q035{0i4PPbfF=`ek=VS~Z(;5e^`J6m*9Yj@uJ5pp@L>1g(oto^;GTpvb=h6}ba zU#YS@dEjltIj5}B;!^LJ-)g2)p47^A9i6!S6;FYUgIj2j)V^mHZv|Ieaq&Ij#*y{? zPSma=dqSLa7A-g)yquX=pna;IjiP2sMBPFsuGNXtKJnL4?^uw|#D8}m`Pis` zWe&};7AuJEns|#fSmt^|*YV8Ttlg??`xBD8tSs3th=;G~p0@RVaR0?4m-zl@WHXBi zFJhau{H1fT(X+*`PM91tUUnoc{L(MUC6hOrES{GxsFgW4BHT7Lvu($#h^;2dYpZ5n zyLvyU_ri$}0s%ovm#?S>%gxmGGJF=~zG9(h^pw?hldt>Atl6nml?|0Z$ zk`&!zO(#EAoW1Jx#dV%NTFy~MxAV4~kupX@pJ56krV z=Wcm&wO@bH=0{6*w6K|0%D%snR~l@!a&Fl3(4AKy%#d@d(nF0`&AjUKTCFs4E`s}N z8CWTbfb?7!u<{i%o)_x{xp+){oR+;ibY|j9$qfuY`JSd!C~{T5yBfA$#MRYLv$?wE zi?Z7nWx*G@N{2;{_?=BF%QoH|Id|5XH`mhBHd~ui-duAIUC?wU34)8yok=Up_-Ima zF)*Y*!{zy!=6!+p(jP~yD^hapyYbj<`7PCN5BEAxy}+{iKf{v!N^Q%WFK1mWoDctor89yI|pR|8wubhHR@VM^SxpS66I{`*HP)hv&QVF>yRx_38cp zBMkO}42;YS94ss>EUe5d%!~}k_q}XPoOtk}kV0Xj(xinCSr%P-WMJgrv`M(=gK|L9 zA>-hWkn3KU89D74)-&#{*um%g@AN9W_U#{h8{@JC;vT)vZv4&|_@le!zF_{s5c%2P zR`H+x9m@Xfw-*1|Zz1xt->k4Ve~U)n45-WBwBXPEtpRoUw^rDjqw_)Pk<9^__jgtE z=enzoKlhzN(6v+9pCOBb_z-*Rgf5z&xh=_JKc)Hd`VTyFnl^B^CV6)-Us*kiLGF;? z#s%h6|1$)wcUTahA;QJV)QE=>zIv|i^YSaKg)G{)5`I_qPJSxJ$@}%l>RFa^*mt^l z8U<`z#Je5D{LgSC_FVu2H-i|%T$LL!1`n)ewf6ptQ@79mEwSOAk>&5lfg2)Z%51rWxC}~W z@9pF2X)xSVEEeu27sI@E+sB6Z_6+;CvH=bAxZ9X6f7yLc{J`Nezh<=r_7p#`nxeqH z+DPNb-Wxp3?n3z%46YGc#$Vp8IHA(mrXgOj?8PDL1r93R6%sy1u@7e}noRJCnB~Bx zyRxk&(9HFtX(unsr;xl~Y61zflUWYsu(@c@VSF|tWyWmft0udTn!5?!4cz^rkwIen zMS~k2hgnn@c;^Ird^q~C=jn?Tcf5TPR^0vKtHcv6t7X8nt*igx$E?z47K~@6sEDZb z{EK66b6ejzQ~LNq|8^PP_Kn|61DW3lGkZHK>Fj2nRWhxC<49zvqJl+($`N0O9dnp= z`xjqOWC*J>PM%}%z+xfaQse&&pL9f1)*0I!m}+!c>CUvg7n`lurWb83(GI*3x_)hy z->C%7O=m*RUYm1Gp)c&K+qEtI(~@6uJ!`6)c(w4wGM}?ME&93=W^T>0UYzZa`Aqr6 z{*V6|Zu)m|?w7uDL^yrZ47J>?5{&Y{l8y`hy~ECX^`rXVW0y}YR9J2Q(Y#tlK-W@g zB}Zlfi)Y82RVCs~3`bTkIKQO4YpZq9GAXt5AqUK#*H75G&P>Qtz{0_7xyg?n4wXsy zY8S*0yXluZ&UBK}dsnP3v`OQjxO~b_ftQ2XIB zfuU2df06vD2bDKAGgKdXaB=609ba4zE_xvr|8kbfj_$PIr*@uKo*7orQoZ%?k~Ooh zW?Jj27X~*+)X$jYXrsf%q^sBY!{>nI2@XNmh8?@VEo6w%&M0`)$k5q2Cv4d}tz9ft z?v=hx4>&Dmie{BP&3ye#rB1H>;hgi|8IMf4aIKp`G$mZNi|eJWaD%Q|?yj5R;=*q; zZ)Uv+Zm`qwkYIU`tgLZ>|3AacAoCfkJ#PvyO3XNFBUGfweJk{w!l!w!z6(|HFNmp9 zZ*aNcaV3zYz-a5&a2x*}8@@!Euewwg<@abY1C##(hc2VkwdNCBJ}`1H^Xtu99b7JI z=IyBPNO;K>1~(z?zeLOwTN2QzM7TyXt`s70*}`&o?q@2s{`+5zW&dUeR|cEyGO$v`aRxjZir!27fM>|Ub=j( z3%j3L$FDalrB8ob753@376kuYHF3}Wtfl#iH-olMc(XG4GzcRrfGI?$|E`+4XWuCV z1{tEiX=%Rxt(DPW8(<2+66k{Msb2S-VXEO)sb2$W9g-WQ7aN4?Kh00|{uzBU z=wH}3f0h)7mmC%GZSI$QLnbf^D5UTy2Gs@Lv~zEn(V#nVg5+|C0^JXN_*{UE&o&2 zAgu1+eprmL<_Vj}f!rkvqzu$9f8C&!e4;Gm_~dFOIg19nMWQWnhMf201&kBSUE=ss zLCyg=HDvyjGBDmG<6y?!$*ctqgH?~fxq@K_lb_1bsJT0%s;*A_G(+h^=o8Mb$6sxh z4qft2Z1RrJ8P=7>FT!%~pPpQ`IB~js_MTdD}C=GMg zYVTb-y)gHz54^Y5Juz(Z*2?*xOrPC-eQK|5*5xZYw|9VY=&Q14YtDTICydY|9A7%_ z1v{uP`K1}I4&JWqAGOIeCCqKlxu+>;fyIPX-=j5zO2ZWw<`@KZ9~udes+ba@;bZkj=pTQL=-=hfQutYyIx5waq$l zb;%b$ro$<>Uy9xrs`cF)mYlU^?$OG*udbAdW?F}N6+Y|v6U%fa&!#am?}TT{`Gs#| zx1{q#A1UQHVbQO)_@5ih>4DR21Q;H7p3rVEe#w##O4OOJ!`v2&?y%r?V9R|RmYf(i zc?T>Uwoeat*euI=r)0bQ8g?-+@6Oekcdjo77Xkvxmy|1g z8FnyL%s2*h@lPMYuMaA;=P;)d24cft;q zIR={iXZX{1!a)14nh|1;>S&TD7B1h(kjURzM{ouRr`rNFSsUO0-8y>*f? z$o;9;*7W+aI2yQgZo2BG#K*Mf?#{^G)+oP6d(M5$vgX<{clDY73=`KI?pUvNaEotK zs7h6nm6pRJ{ic>^KFPDkRv(_dV}0;;)3oyS{-G;(cl96n;J{owA)F!ol5zzsZZmDg z5Rtp$J*dL+ytU`tvpvTiZEmfqy1FE9bKcjiyhq4R-O(sFf$h^Q#$rcxV~MLP-fj81 zS~<)$CHu@fNaR)ZeN6*JUii#A*Js=b`((?>HS^B%l1~pS(-aEuYqa`R%l*b?ditAw&(m$(HXbasu#wkS8bJTb@Fax zzH7l>RHc>YywhcZ+T|l>3sstPn+~mbexYf_iEoDI_9~a?Y>|&X>e8gAXtRUoY!qfd zNc?Srl=#8hL1n_qBNCmGBA?y|2TaQ0`k?!D&)=}*!~nyGW(T{EnNITVG&IotvNe#Y z;`W{e3=d-!*tO3qsu~`j@Gg|!yml)8v)^hKRgNKx{MZhkI9if$EJ=e< zHtkL;Wh~_ygP31&+zOR!?ATIkwY*}Mfmz=}dG1dZzhr98uoN<^Qm|FYnfKz<*@tFo zpF6*rB4z)8Nf$gU`ZpD*u4U+Pgj5-;l~$bIadgf*KVBJKTPD`$oi7wkObP`52s0=% zJ$mE6%#kBc&%E|je&@}3O%I>TiKkh0cM08*(fciux}81AHgH0Hs~e+5H~+<;YU>?u z8YcK2b6o~W@dd008VuD$k4S#$`xv%7FKpgc1EJfU!g3w%lP2&7F)f{ZK=_g<^WC?% zLKzn`J@Wmd#6IN@)90CQM5k2n#C~&P*uk%=z$YOreWRyO&QakW*S4O|@+o!9g%&b< z`KL8pVydM(&+upwOONmJ*GU5G6AmqvJ67fvmCmvI+GfTDli#c@KHHr5g6YubnMbY_ z^p%zR>wZyDKfi+gb#0KW0HfUXPH`QNC%X3jdgd~HtP#&7lwUX*$*5FcOYjgDu3xlN z=5a~-X*b3*5p@$fmC`QTe+ZopDlz6T7JD!oOK5i(zZBjR3UbQ&i3b;VG73FD5P9a% zVumzN^+ih*3N~lgtvq^fS?H2A46?7y_S}A9_o20SL)Gk`{0e;ROe$(WWEjna#W=S} zt`HY4kh#oV>&S7?{n(v;=PJ9!0u?`5`vhv;3wqa>RmtzU-eLH<{n~QD7l}ba7Pt5A z4|U^snD}bx@{Y|jrhIts`~=c4V_^8wc`snP?Se@aosAP9xn>>bKfwu`&Z+%pSY=dk ztFiC_=S|~77BhbT71fYyD({`L1uz%7CY{Oc7rF5s^|e`WQFkMi|fDt(tYd^g;3 z_3zqWv(2WcsQrjzxmnmJEf~1*_@P?<^_e zre7(QoowU$pF!B(`(Ni0IfdSfO)I(E6mF;eX9(?$0wuE$#WR@-ePRdCC~@*K9NcmA zxYkzdqAg!ncy~0uPg=tJK-cD-??*Yazk0v4(zY3h&2#cvT>Rq(`!%n{+3Y)Q&anPy zck|s)t0)xnnPIXT>p%Z@g}2ax`GrGecd-D8#YZ zIP+-&*W7cGK73A0i;MdlgtwmRF-Y@}cT^3Y@7D2aW=QIhH2*6coD0{AGbr%qtLaJq zT$dpqoh7g`#+LJ4r@C>LkRR7AC)-)pKMTs^m6>N16ihs^N}uzA$1hLrk1PcR?5`9J zY|>=R3__)6)qi5iuUcNf%sKJIiC)3Fb8mWV zQGPU$<*r#&=wUmvMI5t}^dECx`qrt$_wVt^^dlbMx9GoP_$<0DN>%E^rrUvRR|fMm8?HVFa z#4^c2B}KbLK5ymP@{Q~-5(7HF8g4SraQ-5elMPHcJGMFeR{Ig3Cy|iw zW@U}bOtHx_pIv^;QMjEZyQk^eE!*{Sj{<+hCcdccpVVW0g{j3vqH#|2SAT|?8+sVK zFU~MnaYj0gr`T8Jm&kqQLbd-4iB$sZeENi;|XOZ2iV_Om@ob-|0cR01H`+#HP5%p#l z*Jgprs>e^|nb*pIDj=C->1Cb*~n|!8b$Ee-u%V4-;V<>lFUhJc99d~Y5E$T8fNcbIH zIP1$-?cj1z0TlstVR1#N(Qm02w;ms1@!*q)uUX~EYs?azdhOST&rzQXUR7wv zJXU$<&#+E>&N=mSjO@%APK<)7(_)iU8qM!#F+_hj=J6-#H&0~k9&K=MesQux<10q> zJB#&;9p^M|gES#@%l3jQfGRPK2UN&6DTQT{8$4!YF|ZUwhT|1&t~iVIdaMb3CV$Vf(U5H8bxnj9df9rC_EBD%4XZ1UFx&P1wg~SY&DXHf&>>6&%o&`JYS_1b;|NjhLjoWwKROb+%d_e!W^;6sHkLP~c zz3Wf$zVPz+zVcJ=UVl9IGxo0aakuwR>+2+&Gu#|kRIKDZP#WJ|tjW7{Ez2>d!werl zDy!EYw|-h)`r~@dx_A3!-cP*?G0BMk(|#s{tqiHGxr#eVZNwj)mdpLHMVOf(;pU6| zQgCH|iuVQIt+TwZ`EI||OLLC`;Ui}|Dl%E;$bL7y^(VHuc-w13?T_mwPAHT+7XEnd zr+FY}`Ir894!7(PuV(JLOco(S1<4r?Gu$}#BtavWFJrqBTef!@q!_vr?dX=o`p)dY z_vXwEEX)!2pJwdfTWN!~Blmq?%(K z|H8R%A9!|uMcBT+lM;&`GI-CO&}sOly>-T%Sc!R({Dw_>8b35=aL=*ddLYUthA)vr zEv1l+7g9MbdDFA?z|DzFDMb*6|7ZBk{_kp>kG}Mr+nfR{kA>6(mOQ(6V(0oM_b!D4 zlXS!+w(V6e*E#nv%lgWT#W_B4o;w(4p1A>56jYp_5+lcG@L~(28pjdtgzHP{Cq}iM zi!@IRRO}H-x|DK&ncGj5%P;p$tGd}YmjedQiyVy{CnmCKozef9b>VGmUZMWhg&Pe^ zCgp6o12^%yWuK7FmP9t4iCnFBrb=dfu)1WSbM6_WC17yv=nNqTqvCx`CDXat9KJ$4 zahGAo*@ioQ3CsJZ?wx%6)V)*JXR0Ms&6)7gD(Q6HLhIw+EDrV@6I_=I=N^K2=IYAb zmS!$TAE@yq#xb|6?b>S_vz522{;BNb_EYyxUgGii&}fhn!?@QWciSY^o2<#v)xEuP zKjtxA)njQFmsV+TZD!0fyE(AP z*k*J7dtNoKiPd4p><0HEe|gR8cRYNd>%hRlz{tR8#ya!GR@qlq?#|h4tM#AZ{GZsk z`G4nhGDuGR9@6)#421nkb=5f(svEP)dk;NhPWaDIyq)Pv;-Y8W3|~aKS)bfsUuniF zvZYzbPV5(tzi^U~2g^L;Lq`|h=6V^!u!G4VBtu!IVEr=rYdc%+NXSN#~a~(QZXw0oMxU9%#;2E{EWR>r&F`;%^aEQoS728 zr5z=j1AlnSDs!{WnU-D*8C|nzGt)UUfoINpH%~Ff23yGZPMHzuWGDFVV*g4%l{sEZJ~TG4On%4OXvJ_s?$jjD9gfni zGhb}BzM>{`saAKZ70={CYgUg+xf9-r&x%;rH5Kg%U%oPY$C0Orw&H)IYGNKLE^j!} zbGGy7kw;nOze*}+pa1Rp)ck;9;ns8C4-{~H^?XzoC+_eoYJU3f*qhczT)+2TYtPZ% zeegfS#x?Gr>!-&5&A*ve4r$#5HeJlHXxOqn3*6*i|DRz?{o82ihY}SoT3YrKkg;0Z zbO!$xhnW_vGw&!0J*w?JwwUd4hq96(SLb|%5TBn+ycyVF*z=6dzs z=}&cg=e}uix$@1x-4oxexO?JTNZylbE%PGV2~ARWPYJO!A4)O{=YLxIbPr$d!wO%O zI1ZL31BF&lo`k8KD!o(gRPUYs6y}v5#JBF-xx4kCr@~3wYwKDKkNCMOAPlWpj$)Hf z>0{>ki_W|Y>1?`W!Pu82?SI04^S;)=UGB^nu9>o_cl#vWFU&&0H6}?9JFVsv+B>vJ z`&3PG1sA~~9@$A}P715fGJ2%L%;5UG2b5!`N8O87IU{joReG`3%sZ!+l^d;`dj`%q z6WnQ}s}?XJU|N!)+#FHO3<;*hVC$J7=bnWc@0{vWIo0R=jMZ!^5AQ5w7Vfm1x_Ti@ z=3Oe1GBC%@lVMVAz=X=?8DU%QR6hwdUVLR$@);0<*jzoQfMxPY&NAKuRUaXPbvKSB>(65uFRkPU_&;%~d6O)1TNLHey8mGfRry>Iw# zrR-<9R}((lUe(^0d^gDY8I-a6neA1@XVt61YmV<)T7IT_mGhbJA?tqjmwJE-y}qCQ zrCwkLvJw=v?K9u4j72tDE`8S$oAVHDFiEfjK&lXoJLgM2IWOW=IViW?%PbT;u;EpfS-p!zWp(XuM8~hCl#P~w3q#iiVUN`Y8*VH|S7VGgb%n;7gsPK+a%W0@PYGcv; z;8aDX~eteRxUKd;+IVm1^q!+T5B|gK=@rYK~XO5e~ zHlo^rnzU!he!I& zS`$oW=NinB6+bxDL#jx{t3c@`$d&}J0%gOjr5Q>eJpB0B1y)9JJQAFcX92avIArk* zHwIw~r=>ab6fGDGHBRa^N<2^uoYvOlsKoO7X=cdUXM)qV9lZWJV%ip=w2LLIF88OD zg|s-k98|je@{s6~9o)IIE0!_%eqabmJd(!f%ge67cB*&DGY3@@lLCPiw_@^Dn1Y?% z92`P?RqlCkFt9OzSoamww{Fmzp!3D8|I@0lqZc2^UR?ZV)zl?2HqN_D68gWba+^?~ zBDkwmLg|r>Vvs>kCyTNhUqVBuvJ6{_$TQ{{x}0xHVpW%yd^5{$x@a?b#^af~ns0KR zM49<5E<7`B(n<+-CbpATQs#JOYQFuFDff-l?UL`4EkX?p2|8SzOJ={}aFAeix#!5O z*9!8Elj9~);gkZ|2`nqm0m|E4XIu$awm znrSw9<{JaQ=?_>xA6MwvF;gSrbnS=te{9Utf#*7V@#|(GUxnS)thoJ4O_}|H6L01@oVaDFz-U~~ESh=+Qr*F7=GE;#xi(3MTzLL~ zv++NJB8SDVWo;9D9A`=hA3r(a_AfhYd1dFhN{15M)RwjFy3IJTc4|u5YV-PutHE8m zEpw-|Z{g-sh*SdCnpqG3IjvPr$;-T>*(lCwwz`9*O2q$=hmq37Cns|37eC(k$;U&C zbIa8ZZas1D@~#VJtIhKu#nQs$2IU(Jbqg6(n39FXXMvj?AkS^-oz|Xr z#YbY-Z_xt`wo?uorJSD(4j4Pn2FDFYPd!rf@idzm<@eg*)*4uQpW%RtADceM;TsGJ zkHtLBfU3&nYh&zBgf7Y1B6~>rc#SQ=Z`;MPD^i#pX;f9*wuH>r*+b$`5IL^A{-4@fz^U&^PDA=# zZY5=olN`d@D8{&5Z$erq>c46(miZcD$oxQ>5R$v2#;M#R8vLi$$EX zmGr*-s)V{w!OG=K?E56;<6^0XTNZIIxhq~Bv1RVH#;uXNi^IURcn)}RfM*7DqG!|V z&}T{&0>K`)Z`|3^J9$~}S>;3dEE94px`nwB>#>mdJhUwtjz{c`jMjR90^)w{Fg1Nj!Snl768!YEpQhZYL_l7Gl z9MW6wAGFoHF!X}*C2Po}*-cRUD6(|T0kKJIvg^e9SK2K*HA{w%m4oTYg$q721mWrE z!owOtvxSN#CMjC>qKXV3Sl)$RefbL1pb?7VWEXkh9mOcoEYY=9_SEHT+bp&VF>ZPK zLV#uRG>xVgTe?|3e0~B>Lgy|$(qj--=iYFZNzT(=z;3!I z`-G`NfF+4F%|WHQnWb9ZvZ1~A=AA1p-8u)e@*XW>+0tkM9t~!xVscl~TB0z?lZ8q5 z$?QiNN21Tf2Y!_hN$5VNeA+<2!KbiMO;k}&#kX|>>-GtTj28bHRyeROT&I?FK_NP8 zqY!^b6sLsfk9HSkp_kk53I1q~QG3~tuzXS)qsoj4q1JOB&F(y8@iFA+f$k-* z%n_IN?BMIzuqs|ciD6RmtYx}QzDtgNYz*0F;Lz>amo@zm%Ykh=h72BCy;mGLs4k=A zb1>u?_Zo)TYJORBrp>NVncOIO=){{jK4SMg&zn4cI5}m;zu=}yh94xFod4%L!6r+=iEh7-xeGpBxN+m7Zk8jHbDKOv0{sr@i?z*J-myC1#;Rn-&1~vGK8aL}^5e~njp2dCsgrX2G_SIo5igE~rEA{-Alnf+0-TIeA#H8 z)WuGN6=xMCX0r!1OWAVToOBS%nV@_k;V=J+Fq=55KCa{29$)yN;mF3}{;+mlJM)E~ z4suGKk9?QRx$t9=y2PpF6}fCldGmd)+RmF_noywRyUe3g^PZ-0NvY;Pd7g*HhDTC1 zFcfiqel%N2$>+Xlu*;;pGrv~(i*)RIUF)?}N1Z>WL*Y<)=X2?X-&0;}k?h9`;HoN|ZZf$-zQVy++3e)@gbx#P%*(=6|W8=8t&F4)1r zE}(BX<+1nzjtRMEqWGD37oV!wz2H^AoC&ksMK33I#bBW*_7FE)=kF zdez^lOCs1(?g&n|IQhnchkb&?>__JfH-4+`-mU+R%{E^?s)<7ZI}+tk@%(D`mQlW~ z`_ + wave.amount_tickets > 0 && + wave.opening_date <= today && + wave.closing_date >= today + ) + }, + selectedTicketWave() { + return ( + this.activeTicketWaves.find( + wave => wave.id === this.formDialog.data.ticket_wave_id + ) || + this.activeTicketWaves[0] || + null + ) + }, + showTicketWaveSelector() { + return this.activeTicketWaves.length > 1 + }, allowFiatCheckout() { - return Boolean(this.event?.allow_fiat) + return Boolean(this.selectedTicketWave?.allow_fiat) }, fiatCheckoutLabel() { if (!this.allowFiatCheckout) return 'Fiat' const unit = ['sat', 'sats'].includes( - (this.event?.currency || '').toLowerCase() + (this.selectedTicketWave?.currency || '').toLowerCase() ) - ? this.event?.fiat_currency - : this.event?.currency + ? this.selectedTicketWave?.fiat_currency + : this.selectedTicketWave?.currency return `Fiat (${(unit || 'GBP').toUpperCase()})` }, allowEmailNotifications() { @@ -66,6 +88,16 @@ window.PageEventsDisplay = { 'GET', `/events/api/v1/events/${this.eventId}` ) + const activeWaves = (data.extra?.ticket_waves || []).filter(wave => { + const today = new Date().toISOString().slice(0, 10) + return ( + wave.amount_tickets > 0 && + wave.opening_date <= today && + wave.closing_date >= today + ) + }) + this.formDialog.data.ticket_wave_id = + activeWaves.length === 1 ? activeWaves[0].id : null return data } catch (error) { this.eventErrorLabel = 'Event unavailable.' @@ -78,6 +110,10 @@ window.PageEventsDisplay = { this.formDialog.data.email = '' this.formDialog.data.refund = '' this.formDialog.data.nostr_identifier = '' + this.formDialog.data.ticket_wave_id = + this.activeTicketWaves.length === 1 + ? this.activeTicketWaves[0].id + : null this.formDialog.data.payment_method = 'lightning' }, @@ -112,6 +148,10 @@ window.PageEventsDisplay = { this.formDialog.data.email = '' this.formDialog.data.refund = '' this.formDialog.data.nostr_identifier = '' + this.formDialog.data.ticket_wave_id = + this.activeTicketWaves.length === 1 + ? this.activeTicketWaves[0].id + : null this.formDialog.data.payment_method = 'lightning' Quasar.Notify.create({ type: 'positive', @@ -141,10 +181,13 @@ window.PageEventsDisplay = { { name: this.formDialog.data.name, email: this.formDialog.data.email, + ticket_wave_id: this.formDialog.data.ticket_wave_id || null, promo_code: this.formDialog.data.promo_code || null, refund_address: this.formDialog.data.refund || null, nostr_identifier: this.formDialog.data.nostr_identifier || null, - payment_method: this.formDialog.data.payment_method + payment_method: this.allowFiatCheckout + ? this.formDialog.data.payment_method + : 'lightning' } ) const isFiat = Boolean(data.is_fiat) @@ -178,7 +221,7 @@ window.PageEventsDisplay = { const url = new URL(window.location) url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:' - url.pathname = `/api/v1/ws/${paymentHash}` + url.pathname = `/events/api/v1/tickets/ws/${paymentHash}` url.search = '' url.hash = '' @@ -187,7 +230,7 @@ window.PageEventsDisplay = { ws.onmessage = event => { const data = JSON.parse(event.data) - if (data.pending === false) { + if (data.paid === true) { this.paymentSuccess(paymentHash) ws.close() } diff --git a/static/js/display.vue b/static/js/display.vue index f5ac5fb..9051fbf 100644 --- a/static/js/display.vue +++ b/static/js/display.vue @@ -49,7 +49,7 @@ filled dense v-model.trim="formDialog.data.nostr_identifier" - label="(optional) Nostr NIP-05 or npub" + label="(optional) Nostr NIP-05" hint="If provided, we'll DM your ticket link after payment." > @@ -64,6 +64,21 @@ lazy-rules :hint="`If minimum tickets (${event.extra?.min_tickets}) are not met, refund will be sent.`" > +

this.shortenId(row.id) + }, {name: 'name', align: 'left', label: 'Name', field: 'name'}, { name: 'event_start_date', @@ -22,12 +30,6 @@ window.PageEvents = { label: 'End date', field: 'event_end_date' }, - { - name: 'closing_date', - align: 'left', - label: 'Ticket close', - field: 'closing_date' - }, { name: 'canceled', align: 'left', @@ -38,43 +40,21 @@ window.PageEvents = { } return 'No' } - }, - { - name: 'price_per_ticket', - align: 'left', - label: 'Price', - field: row => { - if (this.isFiatCurrency(row.currency)) { - return LNbits.utils.formatCurrency( - row.price_per_ticket.toFixed(2), - row.currency - ) - } - return row.price_per_ticket - } - }, - { - name: 'amount_tickets', - align: 'left', - label: 'No tickets', - field: 'amount_tickets' - }, - { - name: 'sold', - align: 'left', - label: 'Sold', - field: 'sold' - }, - {name: 'info', align: 'left', label: 'Info', field: 'info'}, - {name: 'banner', align: 'left', label: 'Banner', field: 'banner'} + } ], pagination: { rowsPerPage: 10 } }, ticketsTable: { + loading: false, columns: [ - {name: 'event', align: 'left', label: 'Event', field: 'event'}, + { + name: 'event', + align: 'left', + label: 'Event', + field: row => this.shortenId(row.event) + }, {name: 'name', align: 'left', label: 'Name', field: 'name'}, {name: 'email', align: 'left', label: 'Email', field: 'email'}, { @@ -83,6 +63,12 @@ window.PageEvents = { label: 'Registered', field: 'registered' }, + { + name: 'nostr', + align: 'left', + label: 'Nostr', + field: row => row.extra?.nostr_identifier || '' + }, { name: 'promo_code', align: 'left', @@ -92,7 +78,11 @@ window.PageEvents = { {name: 'id', align: 'left', label: 'ID', field: 'id'} ], pagination: { - rowsPerPage: 10 + sortBy: 'time', + descending: true, + page: 1, + rowsPerPage: 10, + rowsNumber: 10 } }, formDialog: { @@ -102,28 +92,198 @@ window.PageEvents = { allow_fiat: false, fiat_currency: 'GBP', extra: { + ticket_waves: [], promo_codes: [], notification_subject: '', notification_body: '' } } + }, + ticketWaveDialog: { + show: false, + eventId: null, + wallet: null, + editingWaveId: null, + data: { + id: null, + title: '', + opening_date: '', + closing_date: '', + currency: 'sats', + use_ticket_image: false, + ticket_image_id: null, + allow_fiat: false, + fiat_currency: 'GBP', + amount_tickets: 0, + price_per_ticket: 0 + } + }, + promoCodesDialog: { + show: false, + data: { + id: null, + wallet: null, + name: '', + extra: { + promo_codes: [] + } + } } } }, methods: { + shortenId(value) { + if (!value) return '' + return value.length > 4 ? `${value.slice(0, 4)}...` : value + }, + primaryTicketWave(data = this.formDialog.data) { + if (!data.extra) data.extra = {} + if (!data.extra.ticket_waves || data.extra.ticket_waves.length === 0) { + data.extra.ticket_waves = [ + { + id: 'primary', + title: 'Primary wave', + opening_date: data.closing_date || '', + closing_date: data.closing_date || '', + currency: data.currency || 'sats', + use_ticket_image: false, + ticket_image_id: null, + allow_fiat: Boolean(data.allow_fiat), + fiat_currency: data.fiat_currency || 'GBP', + amount_tickets: data.amount_tickets || 0, + price_per_ticket: data.price_per_ticket || 0 + } + ] + } + return data.extra.ticket_waves[0] + }, + syncPrimaryWaveFromForm(data = this.formDialog.data) { + const primaryWave = this.primaryTicketWave(data) + primaryWave.title = primaryWave.title || 'Primary wave' + primaryWave.opening_date = primaryWave.opening_date || '' + primaryWave.closing_date = data.closing_date || '' + primaryWave.currency = data.currency || 'sats' + primaryWave.use_ticket_image = Boolean(primaryWave.use_ticket_image) + primaryWave.ticket_image_id = primaryWave.ticket_image_id || null + primaryWave.allow_fiat = Boolean(data.allow_fiat) + primaryWave.fiat_currency = data.fiat_currency || 'GBP' + primaryWave.amount_tickets = Number(data.amount_tickets || 0) + primaryWave.price_per_ticket = Number(data.price_per_ticket || 0) + return primaryWave + }, + hydrateEventForm(data) { + const formData = { + ...data, + extra: { + ...(data.extra || {}), + ticket_waves: [...((data.extra && data.extra.ticket_waves) || [])] + } + } + const primaryWave = this.primaryTicketWave(formData) + formData.currency = primaryWave.currency || formData.currency || 'sats' + formData.allow_fiat = Boolean(primaryWave.allow_fiat) + formData.fiat_currency = primaryWave.fiat_currency || 'GBP' + formData.amount_tickets = primaryWave.amount_tickets + formData.price_per_ticket = primaryWave.price_per_ticket + formData.closing_date = + primaryWave.closing_date || formData.closing_date || '' + return formData + }, isFiatCurrency(currency) { return !['sat', 'sats'].includes((currency || '').toLowerCase()) }, - getTickets() { - LNbits.api - .request( + normalizePromoCodes(promoCodes = []) { + return promoCodes + .filter(code => code.code?.trim() !== '') + .map(code => ({ + ...code, + code: code.code.trim().toUpperCase() + })) + }, + templateDownloadUrl() { + return '/events/static/image/ticket.jpg' + }, + async uploadAssetFile(file) { + const form = new FormData() + form.append('file', file) + form.append('public_asset', 'true') + const {data} = await LNbits.api.request( + 'POST', + '/api/v1/assets?public_asset=true', + null, + form + ) + return data.id + }, + triggerTicketImageUpload(target) { + this.ticketImageUploadTarget = target + this.$refs.ticketImageUpload.value = null + this.$refs.ticketImageUpload.click() + }, + async handleTicketImageSelected(event) { + const file = event.target.files?.[0] + if (!file || !this.ticketImageUploadTarget) return + + this.isUploadingTicketTemplate = true + try { + const assetId = await this.uploadAssetFile(file) + if (this.ticketImageUploadTarget === 'primary') { + const wave = this.primaryTicketWave() + wave.use_ticket_image = true + wave.ticket_image_id = assetId + } else if (this.ticketImageUploadTarget === 'dialog') { + this.ticketWaveDialog.data.use_ticket_image = true + this.ticketWaveDialog.data.ticket_image_id = assetId + } + Quasar.Notify.create({ + type: 'positive', + message: 'Ticket template uploaded.', + icon: null + }) + } catch (error) { + LNbits.utils.notifyApiError(error) + } finally { + this.isUploadingTicketTemplate = false + this.ticketImageUploadTarget = null + } + }, + soldTicketsForWave(eventId, waveId) { + return this.allPaidTickets.filter( + ticket => + ticket.event === eventId && + ticket.paid && + (ticket.extra?.ticket_wave_id === waveId || + (!ticket.extra?.ticket_wave_id && waveId === 'primary')) + ).length + }, + async getAllTickets() { + try { + const {data} = await LNbits.api.request( 'GET', '/events/api/v1/tickets?all_wallets=true', this.g.user.wallets[0].adminkey ) - .then(response => { - this.tickets = response.data.filter(e => e.paid) - }) + this.allPaidTickets = data.filter(ticket => ticket.paid) + } catch (error) { + LNbits.utils.notifyApiError(error) + } + }, + async getTickets(props) { + try { + this.ticketsTable.loading = true + const params = LNbits.utils.prepareFilterQuery(this.ticketsTable, props) + const {data} = await LNbits.api.request( + 'GET', + `/events/api/v1/tickets/paginated?all_wallets=true&${params}`, + this.g.user.wallets[0].adminkey + ) + this.tickets = data.data + this.ticketsTable.pagination.rowsNumber = data.total + } catch (error) { + LNbits.utils.notifyApiError(error) + } finally { + this.ticketsTable.loading = false + } }, deleteTicket(ticketId) { const tickets = _.findWhere(this.tickets, {id: ticketId}) @@ -138,10 +298,9 @@ window.PageEvents = { '/events/api/v1/tickets/' + ticketId, wallet.adminkey ) - .then(response => { - this.tickets = _.reject(this.tickets, function (obj) { - return obj.id == ticketId - }) + .then(async () => { + await this.getTickets() + await this.getAllTickets() }) .catch(LNbits.utils.notifyApiError) }) @@ -159,14 +318,30 @@ window.PageEvents = { wallet.adminkey ) .then(response => { + const result = response.data this.tickets = this.tickets.map(obj => - obj.id === ticket.id ? response.data : obj + obj.id === ticket.id ? result.ticket : obj ) - Quasar.Notify.create({ - type: 'positive', - message: 'Ticket email resent.', - icon: null - }) + + if (result.email?.attempted) { + Quasar.Notify.create({ + type: result.email.sent ? 'positive' : 'negative', + message: result.email.sent + ? 'Ticket email resent.' + : `Ticket email failed: ${result.email.error || 'Unknown error.'}`, + icon: null + }) + } + + if (result.nostr?.attempted) { + Quasar.Notify.create({ + type: result.nostr.sent ? 'positive' : 'negative', + message: result.nostr.sent + ? 'Ticket Nostr DM resent.' + : `Ticket Nostr DM failed: ${result.nostr.error || 'Unknown error.'}`, + icon: null + }) + } }) .catch(LNbits.utils.notifyApiError) .finally(() => { @@ -176,7 +351,7 @@ window.PageEvents = { }) }, exportticketsCSV() { - LNbits.utils.exportCSV(this.ticketsTable.columns, this.tickets) + LNbits.utils.exportCSV(this.ticketsTable.columns, this.allPaidTickets) }, getEvents() { LNbits.api @@ -195,19 +370,18 @@ window.PageEvents = { id: this.formDialog.data.wallet }) const data = this.formDialog.data + this.syncPrimaryWaveFromForm(data) if (data.extra?.promo_codes) { - data.extra.promo_codes = data.extra.promo_codes - .filter(code => code.code?.trim() !== '') - .map(code => ({ - ...code, - code: code.code.trim().toUpperCase() - })) + data.extra.promo_codes = this.normalizePromoCodes( + data.extra.promo_codes + ) } if (!this.isFiatCurrency(data.currency)) { if (!data.allow_fiat) { data.fiat_currency = 'GBP' } } + this.syncPrimaryWaveFromForm(data) if (data.id) { this.updateEvent(wallet, data) @@ -218,7 +392,7 @@ window.PageEvents = { openEventDialog(data = false) { if (data && data.id) { - this.formDialog.data = {...data} + this.formDialog.data = this.hydrateEventForm(data) } else { this.formDialog.data = { currency: 'sats', @@ -229,6 +403,21 @@ window.PageEvents = { min_tickets: 1, email_notifications: false, nostr_notifications: false, + ticket_waves: [ + { + id: 'primary', + title: 'Primary wave', + opening_date: '', + closing_date: '', + currency: 'sats', + use_ticket_image: false, + ticket_image_id: null, + allow_fiat: false, + fiat_currency: 'GBP', + amount_tickets: 0, + price_per_ticket: 0 + } + ], promo_codes: [], notification_subject: '', notification_body: '' @@ -244,8 +433,25 @@ window.PageEvents = { allow_fiat: false, fiat_currency: 'GBP', extra: { + conditional: false, + min_tickets: 1, email_notifications: false, nostr_notifications: false, + ticket_waves: [ + { + id: 'primary', + title: 'Primary wave', + opening_date: '', + closing_date: '', + currency: 'sats', + use_ticket_image: false, + ticket_image_id: null, + allow_fiat: false, + fiat_currency: 'GBP', + amount_tickets: 0, + price_per_ticket: 0 + } + ], promo_codes: [], notification_subject: '', notification_body: '' @@ -266,6 +472,177 @@ window.PageEvents = { const link = _.findWhere(this.events, {id: formId}) this.openEventDialog(link) }, + openTicketWaveDialog(event, wave = null) { + const primaryWave = (event.extra?.ticket_waves || [])[0] || {} + const isEditing = Boolean(wave) + this.ticketWaveDialog = { + show: true, + eventId: event.id, + wallet: event.wallet, + editingWaveId: wave?.id || null, + data: { + id: wave?.id || null, + title: wave?.title || '', + opening_date: wave?.opening_date || '', + closing_date: wave?.closing_date || '', + currency: + wave?.currency || primaryWave.currency || event.currency || 'sats', + use_ticket_image: Boolean(wave?.use_ticket_image), + ticket_image_id: wave?.ticket_image_id || null, + allow_fiat: isEditing + ? Boolean(wave?.allow_fiat) + : Boolean(primaryWave.allow_fiat ?? event.allow_fiat), + fiat_currency: + wave?.fiat_currency || + primaryWave.fiat_currency || + event.fiat_currency || + 'GBP', + amount_tickets: wave?.amount_tickets || 0, + price_per_ticket: + wave?.price_per_ticket || + primaryWave.price_per_ticket || + event.price_per_ticket || + 0 + } + } + }, + resetTicketWaveDialog() { + this.ticketWaveDialog = { + show: false, + eventId: null, + wallet: null, + editingWaveId: null, + data: { + id: null, + title: '', + opening_date: '', + closing_date: '', + currency: 'sats', + use_ticket_image: false, + ticket_image_id: null, + allow_fiat: false, + fiat_currency: 'GBP', + amount_tickets: 0, + price_per_ticket: 0 + } + } + }, + saveTicketWave() { + const event = _.findWhere(this.events, { + id: this.ticketWaveDialog.eventId + }) + const wallet = _.findWhere(this.g.user.wallets, { + id: this.ticketWaveDialog.wallet + }) + if (!event || !wallet) return + + const payload = { + ...event, + extra: { + ...event.extra, + ticket_waves: (event.extra?.ticket_waves || []).map(existingWave => + existingWave.id === this.ticketWaveDialog.editingWaveId + ? {...this.ticketWaveDialog.data} + : existingWave + ) + } + } + + if (!this.ticketWaveDialog.editingWaveId) { + payload.extra.ticket_waves.push({...this.ticketWaveDialog.data}) + } + + if (payload.extra?.promo_codes) { + payload.extra.promo_codes = this.normalizePromoCodes( + payload.extra.promo_codes + ) + } + + LNbits.api + .request( + 'PUT', + '/events/api/v1/events/' + payload.id, + wallet.adminkey, + payload + ) + .then(response => { + this.events = this.events.map(item => + item.id === payload.id ? response.data : item + ) + Quasar.Notify.create({ + type: 'positive', + message: this.ticketWaveDialog.editingWaveId + ? 'Ticket wave updated.' + : 'Ticket wave added.', + icon: null + }) + this.resetTicketWaveDialog() + }) + .catch(LNbits.utils.notifyApiError) + }, + openPromoCodesDialog(event) { + this.promoCodesDialog.data = { + ...event, + extra: { + ...event.extra, + promo_codes: [...(event.extra?.promo_codes || [])] + } + } + this.promoCodesDialog.show = true + }, + resetPromoCodesDialog() { + this.promoCodesDialog.show = false + this.promoCodesDialog.data = { + id: null, + wallet: null, + name: '', + extra: { + promo_codes: [] + } + } + }, + addPromoCodeToDialog() { + this.promoCodesDialog.data.extra.promo_codes.push({ + code: '', + discount_percent: 0, + active: true + }) + }, + savePromoCodes() { + const data = this.promoCodesDialog.data + const wallet = _.findWhere(this.g.user.wallets, { + id: data.wallet + }) + if (!wallet) return + + const payload = { + ...data, + extra: { + ...data.extra, + promo_codes: this.normalizePromoCodes(data.extra?.promo_codes || []) + } + } + + LNbits.api + .request( + 'PUT', + '/events/api/v1/events/' + data.id, + wallet.adminkey, + payload + ) + .then(response => { + this.events = this.events.map(event => + event.id === data.id ? response.data : event + ) + Quasar.Notify.create({ + type: 'positive', + message: 'Promo codes updated.', + icon: null + }) + this.resetPromoCodesDialog() + }) + .catch(LNbits.utils.notifyApiError) + }, updateEvent(wallet, data) { LNbits.api .request( @@ -332,6 +709,7 @@ window.PageEvents = { async created() { if (this.g.user.wallets.length) { this.getTickets() + this.getAllTickets() this.getEvents() if (this.g.allowedCurrencies && this.g.allowedCurrencies.length > 0) { this.currencies = ['sats', ...this.g.allowedCurrencies] diff --git a/static/js/index.vue b/static/js/index.vue index fa39a4e..b90e922 100644 --- a/static/js/index.vue +++ b/static/js/index.vue @@ -100,45 +100,102 @@
-
Promo codes
+
+
Ticket waves
+ +
+
+
+
+ + + +
+
+
+ +
+
Promo codes
+ +
- No promo codes for this event. + No active promo codes for this event.
-
-
+
+
- - +
-
- Discount: - % -
-
- Status: - -
@@ -165,9 +222,11 @@ dense flat :rows="tickets" + :loading="ticketsTable.loading" row-key="id" :columns="ticketsTable.columns" v-model:pagination="ticketsTable.pagination" + @request="getTickets" > diff --git a/static/js/ticket.js b/static/js/ticket.js index 82fbd6d..6ff55b6 100644 --- a/static/js/ticket.js +++ b/static/js/ticket.js @@ -3,16 +3,36 @@ window.PageEventsTicket = { data() { return { ticketId: null, - ticket: null + ticket: null, + printMode: false, + qrSrc: '' } }, methods: { - printWindow() { - window.print() + async printWindow() { + this.printMode = true + await this.$nextTick() + await this.waitForPrintAssets() + setTimeout(() => window.print(), 50) + }, + async waitForPrintAssets() { + await this.$nextTick() + const img = document.querySelector('.ticket-print-qr') + if (!img) return + if (img.complete && img.naturalWidth > 0) return + await new Promise(resolve => { + const done = () => resolve() + img.addEventListener('load', done, {once: true}) + img.addEventListener('error', done, {once: true}) + setTimeout(done, 500) + }) } }, async created() { this.ticketId = this.$route.params.id + this.qrSrc = `/api/v1/qrcode?data=${encodeURIComponent( + `ticket://${this.ticketId}` + )}` try { const {data} = await LNbits.api.request( 'GET', @@ -22,5 +42,8 @@ window.PageEventsTicket = { } catch (error) { LNbits.utils.notifyApiError(error) } + window.addEventListener('afterprint', () => { + this.printMode = false + }) } } diff --git a/static/js/ticket.vue b/static/js/ticket.vue index 3c932e1..23a8dc4 100644 --- a/static/js/ticket.vue +++ b/static/js/ticket.vue @@ -36,4 +36,53 @@
+ + +
+ Ticket QR +
+
+ + diff --git a/views_api.py b/views_api.py index abdc1ef..49b8ec9 100644 --- a/views_api.py +++ b/views_api.py @@ -1,8 +1,11 @@ import asyncio from datetime import datetime, timezone from http import HTTPStatus +from io import BytesIO +from pathlib import Path from typing import Any +import pyqrcode # type: ignore[import-untyped] from fastapi import ( APIRouter, Depends, @@ -12,22 +15,27 @@ from fastapi import ( WebSocket, WebSocketDisconnect, ) +from fastapi.responses import StreamingResponse from lnbits.core.crud import get_user +from lnbits.core.crud.assets import get_public_asset from lnbits.core.crud.wallets import get_wallet from lnbits.core.models import WalletTypeInfo from lnbits.core.models.payments import CreateInvoice from lnbits.core.services import create_payment_request +from lnbits.db import Filters, Page from lnbits.decorators import ( + parse_filters, require_admin_key, require_invoice_key, ) +from lnbits.helpers import generate_filter_params_openapi from lnbits.settings import settings from lnbits.utils.exchange_rates import ( fiat_amount_as_satoshis, get_fiat_rate_satoshis, satoshis_amount_as_fiat, ) -from lnbits.utils.nostr import normalize_public_key +from PIL import Image, ImageDraw from .crud import ( create_event, @@ -39,6 +47,7 @@ from .crud import ( get_events, get_ticket, get_tickets, + get_tickets_paginated, purge_unpaid_tickets, update_event, update_ticket, @@ -50,19 +59,53 @@ from .models import ( PublicEvent, PublicTicket, Ticket, + TicketFilters, TicketPaymentRequest, + TicketResendResult, + ensure_ticket_waves, + get_active_ticket_waves, ) from .services import refund_tickets, resend_ticket_email_notification from .tasks import deregister_payment_listener, register_payment_listener events_api_router = APIRouter(prefix="/api/v1/events") tickets_api_router = APIRouter(prefix="/api/v1/tickets") +qr_api_router = APIRouter(prefix="/api/v1") +tickets_filters = parse_filters(TicketFilters) def _is_fiat_currency(currency: str | None) -> bool: return str(currency or "").lower() not in {"sat", "sats"} +def make_qr_png(data: str, size: int = 235, border: int = 4) -> Image.Image: + qr = pyqrcode.create(data) + matrix = qr.code + modules = len(matrix) + + total_modules = modules + border * 2 + box_size = max(1, size // total_modules) + img_size = total_modules * box_size + + img = Image.new("RGBA", (img_size, img_size), "white") + draw = ImageDraw.Draw(img) + + for y, row in enumerate(matrix): + for x, cell in enumerate(row): + if cell: + x0 = (x + border) * box_size + y0 = (y + border) * box_size + draw.rectangle( + [x0, y0, x0 + box_size - 1, y0 + box_size - 1], + fill="black", + ) + + if img_size != size: + img = img.resize((size, size), Image.Resampling.NEAREST) + + return img + + @events_api_router.get("") async def api_events( all_wallets: bool = Query(False), @@ -86,24 +129,29 @@ async def api_get_event(event_id: str) -> Event: ) await purge_unpaid_tickets(event_id) - is_window_open = datetime.now(timezone.utc) < datetime.strptime( - event.closing_date, "%Y-%m-%d" - ).replace(tzinfo=timezone.utc) + today = datetime.now(timezone.utc).date() + active_waves = get_active_ticket_waves(event, today) + is_sales_closed = today > datetime.strptime(event.closing_date, "%Y-%m-%d").date() is_min_tickets_met = ( event.sold >= event.extra.min_tickets if event.extra.conditional else True ) if event.amount_tickets < 1: raise HTTPException(status_code=HTTPStatus.GONE, detail="Event is sold out.") - if event.extra.conditional and not is_min_tickets_met and not is_window_open: + if event.extra.conditional and not is_min_tickets_met and is_sales_closed: event.canceled = True await update_event(event) await refund_tickets(event_id) raise HTTPException(status_code=HTTPStatus.GONE, detail="Event canceled.") - if not is_window_open: + if not active_waves: raise HTTPException( - status_code=HTTPStatus.GONE, detail="Ticket closing date has passed." + status_code=HTTPStatus.GONE, + detail=( + "Ticket closing date has passed." + if is_sales_closed + else "No ticket wave is currently open." + ), ) return event @@ -127,8 +175,17 @@ async def api_event_create( raise HTTPException( status_code=HTTPStatus.FORBIDDEN, detail="Not your event." ) - for k, v in data.dict().items(): - setattr(event, k, v) + event = Event( + **{ + **event.dict(), + **data.dict(), + "id": event.id, + "wallet": event.wallet, + "time": event.time, + "sold": event.sold, + "canceled": event.canceled, + } + ) event = await update_event(event) else: event = await create_event(data) @@ -187,6 +244,31 @@ async def api_tickets( return await get_tickets(wallet_ids) +@tickets_api_router.get( + "/paginated", + summary="Get paginated list of tickets", + openapi_extra=generate_filter_params_openapi(TicketFilters), + response_model=Page[Ticket], +) +async def api_tickets_paginated( + all_wallets: bool = Query(False), + filters: Filters = Depends(tickets_filters), + key_info: WalletTypeInfo = Depends(require_admin_key), +) -> Page[Ticket]: + wallet_ids = [key_info.wallet.id] + + if all_wallets: + user = await get_user(key_info.wallet.user) + wallet_ids = user.wallet_ids if user else [] + + if not filters.sortby: + filters.sortby = "time" + if not filters.direction: + filters.direction = "desc" + + return await get_tickets_paginated(wallet_ids, filters) + + @tickets_api_router.get("/{ticket_id}", response_model=PublicTicket) async def api_get_ticket(ticket_id: str) -> Ticket: ticket = await get_ticket(ticket_id) @@ -202,6 +284,71 @@ async def api_get_ticket(ticket_id: str) -> Ticket: return ticket +@qr_api_router.get("/qr/{ticket_id}", response_class=StreamingResponse) +async def api_ticket_qr(ticket_id: str): + ticket = await get_ticket(ticket_id) + if not ticket: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Ticket does not exist." + ) + + event = await get_event(ticket.event) + if not event: + raise HTTPException( + status_code=HTTPStatus.NOT_FOUND, detail="Event does not exist." + ) + + waves = ensure_ticket_waves(event) + wave = next( + (wave for wave in waves if wave.id == ticket.extra.ticket_wave_id), + waves[0], + ) + + qr_img = make_qr_png(f"ticket://{ticket_id}", size=157) + output = BytesIO() + + if not wave.use_ticket_image: + qr_img.save(output, format="PNG") + output.seek(0) + return StreamingResponse( + output, + media_type="image/png", + headers={ + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", + }, + ) + + background_bytes = None + if wave.ticket_image_id: + asset = await get_public_asset(wave.ticket_image_id) + if asset: + background_bytes = asset.data + + if background_bytes: + ticket_image = Image.open(BytesIO(background_bytes)).convert("RGBA") + else: + default_template = ( + Path(__file__).resolve().parent / "static" / "image" / "ticket.jpg" + ) + ticket_image = Image.open(default_template).convert("RGBA") + + ticket_image.paste(qr_img, (122, 505)) + ticket_image.save(output, format="PNG") + output.seek(0) + + return StreamingResponse( + output, + media_type="image/png", + headers={ + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + "Expires": "0", + }, + ) + + @tickets_api_router.post("/{event_id}") async def api_ticket_create( event_id: str, data: CreateTicket, request: Request @@ -215,7 +362,7 @@ async def api_ticket_create( if event.canceled: raise HTTPException(status_code=HTTPStatus.GONE, detail="Event is canceled.") - if event.amount_tickets > 0 and event.sold >= event.amount_tickets: + if event.amount_tickets < 1: raise HTTPException(status_code=HTTPStatus.GONE, detail="Event is sold out.") name = data.name @@ -230,14 +377,36 @@ async def api_ticket_create( detail="Unsupported payment method.", ) if nostr_identifier and "@" not in nostr_identifier: - try: - nostr_identifier = normalize_public_key(nostr_identifier) - except Exception as exc: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="Only NIP-05 Nostr identifiers are supported.", + ) + active_waves = get_active_ticket_waves(event) + if not active_waves: + raise HTTPException( + status_code=HTTPStatus.GONE, detail="No ticket wave is currently open." + ) + + selected_wave = None + if data.ticket_wave_id: + selected_wave = next( + (wave for wave in active_waves if wave.id == data.ticket_wave_id), + None, + ) + if not selected_wave: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, - detail="Invalid Nostr identifier.", - ) from exc - price = event.price_per_ticket + detail="Invalid ticket wave selected.", + ) + elif len(active_waves) == 1: + selected_wave = active_waves[0] + else: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail="Please select a ticket wave.", + ) + + price = selected_wave.price_per_ticket extra: dict[str, Any] = {"tag": "events", "name": name, "email": email} if promo_code: @@ -249,31 +418,31 @@ async def api_ticket_create( # get the promocode promo = next(pc for pc in event.extra.promo_codes if pc.code == promo_code) extra["promo_code"] = promo.code - price = event.price_per_ticket * (1 - promo.discount_percent / 100) + price = selected_wave.price_per_ticket * (1 - promo.discount_percent / 100) - if payment_method == "fiat" and not event.allow_fiat: + if payment_method == "fiat" and not selected_wave.allow_fiat: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail="Fiat payments are not enabled for this event.", ) - if _is_fiat_currency(event.currency): + if _is_fiat_currency(selected_wave.currency): extra["fiat"] = True - extra["currency"] = event.currency + extra["currency"] = selected_wave.currency extra["fiatAmount"] = price - extra["rate"] = await get_fiat_rate_satoshis(event.currency) + extra["rate"] = await get_fiat_rate_satoshis(selected_wave.currency) if payment_method != "fiat": - price = await fiat_amount_as_satoshis(price, event.currency) + price = await fiat_amount_as_satoshis(price, selected_wave.currency) - invoice_unit = event.currency + invoice_unit = selected_wave.currency fiat_amount = price fiat_provider = None if payment_method == "fiat": - if _is_fiat_currency(event.currency): - invoice_unit = event.currency + if _is_fiat_currency(selected_wave.currency): + invoice_unit = selected_wave.currency else: - invoice_unit = event.fiat_currency + invoice_unit = selected_wave.fiat_currency fiat_amount = await satoshis_amount_as_fiat(price, invoice_unit) extra["fiat"] = True extra["currency"] = invoice_unit @@ -314,6 +483,8 @@ async def api_ticket_create( email=email, extra={ "applied_promo_code": promo_code, + "ticket_wave_id": selected_wave.id, + "ticket_wave_title": selected_wave.title, "refund_address": refund_address, "nostr_identifier": nostr_identifier, "ticket_base_url": str(request.base_url).rstrip("/"), @@ -388,10 +559,12 @@ async def api_ticket_delete( await delete_ticket(ticket_id) -@tickets_api_router.post("/{ticket_id}/resend-email") +@tickets_api_router.post("/{ticket_id}/resend-email", response_model=TicketResendResult) async def api_ticket_resend_email( - ticket_id: str, wallet: WalletTypeInfo = Depends(require_admin_key) -) -> Ticket: + ticket_id: str, + request: Request, + wallet: WalletTypeInfo = Depends(require_admin_key), +) -> TicketResendResult: ticket = await get_ticket(ticket_id) if not ticket: raise HTTPException( @@ -408,16 +581,13 @@ async def api_ticket_resend_email( ) try: - return await resend_ticket_email_notification(ticket) + return await resend_ticket_email_notification( + ticket, str(request.base_url).rstrip("/") + ) except ValueError as exc: raise HTTPException( status_code=HTTPStatus.BAD_REQUEST, detail=str(exc) ) from exc - except Exception as exc: - raise HTTPException( - status_code=HTTPStatus.INTERNAL_SERVER_ERROR, - detail="Failed to resend ticket email.", - ) from exc @tickets_api_router.put("/register/{ticket_id}")