From 72d0e81342ecfe39ac14c56958b8c60d92e5bf9d Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Sat, 10 Aug 2024 20:58:41 +0200 Subject: [PATCH] [ENH] generate a single report per subject for each action (#231) * generate single report * add test * fix type * update requirements.txt * use union * rm type annotation --- .gitignore | 1 - .pre-commit-config.yaml | 2 +- bidsmreye/_cli.py | 2 +- bidsmreye/bids_utils.py | 2 +- bidsmreye/bidsmreye.py | 2 +- bidsmreye/config/config_bidsname.json | 2 +- bidsmreye/configuration.py | 2 +- bidsmreye/download.py | 2 +- bidsmreye/generalize.py | 2 +- bidsmreye/{logging.py => logger.py} | 0 bidsmreye/methods.py | 32 +++---- bidsmreye/prepare_data.py | 6 +- bidsmreye/quality_control.py | 18 ++-- bidsmreye/report.py | 80 ++++++++++++++++++ .../{CITATION.mustache => CITATION.jinja} | 15 ++-- bidsmreye/templates/report/base.html | 59 +++++++++++++ bidsmreye/templates/report/footer.html | 27 ++++++ bidsmreye/templates/report/header.html | 51 +++++++++++ bidsmreye/utils.py | 7 +- bidsmreye/visualize.py | 2 +- .../source/images/bidsMReye_logo_on_white.png | Bin 0 -> 53051 bytes pyproject.toml | 48 +++-------- requirements.txt | 3 +- tests/test_report.py | 16 ++++ 24 files changed, 300 insertions(+), 81 deletions(-) rename bidsmreye/{logging.py => logger.py} (100%) create mode 100644 bidsmreye/report.py rename bidsmreye/templates/{CITATION.mustache => CITATION.jinja} (92%) create mode 100644 bidsmreye/templates/report/base.html create mode 100644 bidsmreye/templates/report/footer.html create mode 100644 bidsmreye/templates/report/header.html create mode 100644 docs/source/images/bidsMReye_logo_on_white.png create mode 100644 tests/test_report.py diff --git a/.gitignore b/.gitignore index b862b72..101a4c6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ logs # files *.nii *.h5 -*.html *desc-bidsmreye_eyetrack.tsv pybids_db tmp.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b690145..221cea6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,7 @@ repos: rev: v1.11.1 hooks: - id: mypy - additional_dependencies: [pydantic, numpy] + additional_dependencies: [pydantic, numpy, types-Jinja2] files: bidsmreye args: [--config-file, pyproject.toml] diff --git a/bidsmreye/_cli.py b/bidsmreye/_cli.py index 919a739..fd893e5 100644 --- a/bidsmreye/_cli.py +++ b/bidsmreye/_cli.py @@ -11,7 +11,7 @@ from bidsmreye.bidsmreye import bidsmreye from bidsmreye.defaults import default_log_level, log_levels from bidsmreye.download import download -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log log = bidsmreye_log(name="bidsmreye") diff --git a/bidsmreye/bids_utils.py b/bidsmreye/bids_utils.py index 434e011..cdb890a 100644 --- a/bidsmreye/bids_utils.py +++ b/bidsmreye/bids_utils.py @@ -15,7 +15,7 @@ get_bidsname_config, get_pybids_config, ) -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log from bidsmreye.methods import methods from bidsmreye.utils import copy_license, create_dir_if_absent, return_regex diff --git a/bidsmreye/bidsmreye.py b/bidsmreye/bidsmreye.py index 76c0a9e..ca85362 100755 --- a/bidsmreye/bidsmreye.py +++ b/bidsmreye/bidsmreye.py @@ -9,7 +9,7 @@ from bidsmreye._version import __version__ from bidsmreye.configuration import Config from bidsmreye.defaults import default_log_level -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log log = bidsmreye_log(name="bidsmreye") diff --git a/bidsmreye/config/config_bidsname.json b/bidsmreye/config/config_bidsname.json index 8eae1f9..4fb5fc9 100644 --- a/bidsmreye/config/config_bidsname.json +++ b/bidsmreye/config/config_bidsname.json @@ -1,6 +1,6 @@ { "mask": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_mask.p", - "report": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_report.html", + "report": "sub-{subject}/[ses-{session}]/figures/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_report.html", "no_label": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-nolabel_bidsmreye.npz", "confounds_tsv": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}]_desc-bidsmreye_eyetrack.tsv", "confounds_json": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}]_desc-bidsmreye_eyetrack.json", diff --git a/bidsmreye/configuration.py b/bidsmreye/configuration.py index f856731..35e4b5e 100644 --- a/bidsmreye/configuration.py +++ b/bidsmreye/configuration.py @@ -9,7 +9,7 @@ from attrs import asdict, converters, define, field from bids import BIDSLayout # type: ignore -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log log = bidsmreye_log(name="bidsmreye") diff --git a/bidsmreye/download.py b/bidsmreye/download.py index 9f9dd06..51e5a40 100644 --- a/bidsmreye/download.py +++ b/bidsmreye/download.py @@ -10,7 +10,7 @@ import bidsmreye from bidsmreye.defaults import available_models, default_model -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log log = bidsmreye_log(name="bidsmreye") diff --git a/bidsmreye/generalize.py b/bidsmreye/generalize.py index 45f7b00..cb57fc5 100644 --- a/bidsmreye/generalize.py +++ b/bidsmreye/generalize.py @@ -22,7 +22,7 @@ list_subjects, ) from bidsmreye.configuration import Config -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log from bidsmreye.quality_control import quality_control_output from bidsmreye.utils import ( add_sidecar_in_root, diff --git a/bidsmreye/logging.py b/bidsmreye/logger.py similarity index 100% rename from bidsmreye/logging.py rename to bidsmreye/logger.py diff --git a/bidsmreye/methods.py b/bidsmreye/methods.py index 49973b3..fe9c7cb 100644 --- a/bidsmreye/methods.py +++ b/bidsmreye/methods.py @@ -6,10 +6,9 @@ import warnings from pathlib import Path -import chevron - from bidsmreye._version import __version__ from bidsmreye.defaults import available_models, default_model +from bidsmreye.report import TEMPLATES_DIR, return_jinja_env from bidsmreye.utils import create_dir_for_file @@ -44,29 +43,26 @@ def methods( if not model: model = default_model() - is_known_models = False + is_known_model = False is_default_model = False if model in available_models(): - is_known_models = True + is_known_model = True if model == default_model(): is_default_model = True - if not is_known_models: + if not is_known_model: warnings.warn(f"{model} is not a known model name.", stacklevel=3) - template_file = str(Path(__file__).parent / "templates" / "CITATION.mustache") - with open(template_file) as template: - output = chevron.render( - template=template, - data={ - "version": __version__, - "model": model, - "is_default_model": is_default_model, - "is_known_models": is_known_models, - "qc_only": qc_only, - }, - warn=True, - ) + env = return_jinja_env(searchpath=TEMPLATES_DIR) + template = env.get_template("CITATION.jinja") + + output = template.render( + version=__version__, + model=model, + is_default_model=is_default_model, + is_known_model=is_known_model, + qc_only=qc_only, + ) output_file.write_text(output) diff --git a/bidsmreye/prepare_data.py b/bidsmreye/prepare_data.py index 3cba8ca..3568b31 100644 --- a/bidsmreye/prepare_data.py +++ b/bidsmreye/prepare_data.py @@ -20,7 +20,8 @@ save_sampling_frequency_to_json, ) from bidsmreye.configuration import Config -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log +from bidsmreye.report import generate_report from bidsmreye.utils import ( check_if_file_found, get_deepmreye_filename, @@ -213,4 +214,7 @@ def prepare_data(cfg: Config) -> None: ) for subject_label in subjects: process_subject(cfg, layout_in, layout_out, subject_label) + generate_report( + output_dir=cfg.output_dir, subject_label=subject_label, action="prepare" + ) progress.update(subject_loop, advance=1) diff --git a/bidsmreye/quality_control.py b/bidsmreye/quality_control.py index e8df3ea..8c09a03 100644 --- a/bidsmreye/quality_control.py +++ b/bidsmreye/quality_control.py @@ -3,7 +3,6 @@ from __future__ import annotations import json -import logging import math from pathlib import Path @@ -20,7 +19,8 @@ list_subjects, ) from bidsmreye.configuration import Config -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log +from bidsmreye.report import generate_report from bidsmreye.utils import ( check_if_file_found, create_dir_for_file, @@ -142,9 +142,7 @@ def perform_quality_control( add_qc_to_sidecar(confounds, sidecar_name) fig = visualize_eye_gaze_data(confounds) - fig.update_layout(title=Path(confounds_tsv).name) - if log.isEnabledFor(logging.DEBUG): - fig.show() + fig.update_layout(showlegend=False, height=800) create_dir_for_file(visualization_html_file) fig.write_html(visualization_html_file) @@ -184,6 +182,11 @@ def quality_control_output(cfg: Config) -> None: ) for subject_label in subjects: qc_subject(cfg, layout_out, subject_label) + generate_report( + output_dir=cfg.output_dir, + subject_label=subject_label, + action="generalize", + ) progress.update(subject_loop, advance=1) @@ -203,6 +206,11 @@ def quality_control_input(cfg: Config) -> None: ) for subject_label in subjects: qc_subject(cfg, layout_in, subject_label, layout_out) + generate_report( + output_dir=cfg.output_dir, + subject_label=subject_label, + action="generalize", + ) progress.update(subject_loop, advance=1) diff --git a/bidsmreye/report.py b/bidsmreye/report.py new file mode 100644 index 0000000..30576cf --- /dev/null +++ b/bidsmreye/report.py @@ -0,0 +1,80 @@ +"""Compile outputs from all tasks, spaces, runs into a single HTML.""" + +import datetime +from pathlib import Path + +from jinja2 import Environment, FileSystemLoader, select_autoescape + +from bidsmreye._version import __version__ +from bidsmreye.logger import bidsmreye_log + +log = bidsmreye_log(name="bidsmreye") + +TEMPLATES_DIR = Path(__file__).parent / "templates" + + +def return_jinja_env(searchpath=None) -> Environment: + if searchpath is None: + searchpath = TEMPLATES_DIR / "report" + return Environment( + loader=FileSystemLoader(searchpath), + autoescape=select_autoescape(), + lstrip_blocks=True, + trim_blocks=True, + ) + + +def generate_report(output_dir: Path, subject_label: str, action: str) -> None: + + env = return_jinja_env() + template = env.get_template("base.html") + + if action == "prepare": + input_files = sorted(output_dir.glob(f"sub-{subject_label}/**/*report.html")) + elif action == "generalize": + input_files = sorted(output_dir.glob(f"sub-{subject_label}/**/*eyetrack.html")) + + files = [] + for html_report in input_files: + with open(html_report) as f: + content = f.read() + + name: str = html_report.stem + if action == "prepare": + name = name.replace("_desc-eye_report", "_desc-preproc_bold") + + files.append({"name": name, "content": content, "path": html_report}) + + date = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat() + + report = template.render( + action=action, + files=files, + subject_label=subject_label, + date=date, + version=__version__, + ) + + report_filename = ( + output_dir / f"sub-{subject_label}" / f"sub-{subject_label}_{action}.html" + ) + + with open(report_filename, "w") as f: + f.write(report) + + log.info(f"Report saved at: '{report_filename}'.") + + +if __name__ == "__main__": + + cwd = Path("/home/remi/github/cpp-lln-lab/bidsMReye") + + output_dir = cwd / "outputs" / "moae_fmriprep" / "derivatives" / "bidsmreye" + subject = "01" + + output_dir = Path("/home/remi/gin/CPP/yin/bidsmreye") + subject_label = "02" + + action = "prepare" + + generate_report(output_dir, subject_label, action) diff --git a/bidsmreye/templates/CITATION.mustache b/bidsmreye/templates/CITATION.jinja similarity index 92% rename from bidsmreye/templates/CITATION.mustache rename to bidsmreye/templates/CITATION.jinja index 78f46bd..c3f71d9 100644 --- a/bidsmreye/templates/CITATION.mustache +++ b/bidsmreye/templates/CITATION.jinja @@ -6,7 +6,7 @@ performed using [*bidsMReye*](https://github.com/cpp-lln-lab/bidsMReye) (version a BIDS app relying on [deepMReye](https://github.com/DeepMReye/DeepMReye) (@deepmreye) to decode eye motion from fMRI time series data. -{{^qc_only}} +{% if not qc_only %} ### data extraction The data of each BOLD runs underwent co-registration conducted @@ -28,19 +28,18 @@ across voxels (spatial normalization). Voxels time series were used as inputs for generalization decoding using a -{{#is_known_models}} + {% if is_known_model %} pre-trained model {{ model }} from deepMReye from [OSF](https://osf.io/23t5v). - {{#is_default_model}} + {% if is_default_model %} This model was trained on the following datasets: guided fixations (@alexander_open_2017), smooth pursuit (@nau_real-motion_2018, @polti_rapid_2022, @nau_hexadirectional_2018), free viewing (@julian_human_2018). - {{/is_default_model}} -{{/is_known_models}} -{{^is_known_models}} + {% endif %} + {% else %} model trained on calibration data from the current study. -{{/is_known_models}} -{{/qc_only}} + {% endif %} +{% endif %} ### quality control diff --git a/bidsmreye/templates/report/base.html b/bidsmreye/templates/report/base.html new file mode 100644 index 0000000..809e922 --- /dev/null +++ b/bidsmreye/templates/report/base.html @@ -0,0 +1,59 @@ + + + + + + + bidsmreye report - sub-{{ subject_label }} - {{ action }} + + + + + + + + + + +
{% include "header.html" %}
+ +
+ +

subject {{ subject_label }}: {{ action }}

+ + {% for file in files %} +
+
+

{{ file.name }}

+
+
{{ file.content|safe }}
+
+
+
+ {% endfor %} + +
+

About

+

+ Report generated +

    +
  • on: {{ date }}
  • +
  • with bidsmreye {{ version }}
  • +
+

+
+ +
+ + + + + diff --git a/bidsmreye/templates/report/footer.html b/bidsmreye/templates/report/footer.html new file mode 100644 index 0000000..7b420cb --- /dev/null +++ b/bidsmreye/templates/report/footer.html @@ -0,0 +1,27 @@ +
+
+
+

bidsmreye

+

+ documentation +

+

+ issue tracker +

+

+ github +

+

pypi

+

+ dockerhub +

+

+ FAQ +

+
+
+
diff --git a/bidsmreye/templates/report/header.html b/bidsmreye/templates/report/header.html new file mode 100644 index 0000000..b0c17c3 --- /dev/null +++ b/bidsmreye/templates/report/header.html @@ -0,0 +1,51 @@ + diff --git a/bidsmreye/utils.py b/bidsmreye/utils.py index 4f66250..e9f29f8 100644 --- a/bidsmreye/utils.py +++ b/bidsmreye/utils.py @@ -20,11 +20,14 @@ ) from bidsmreye.configuration import Config -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log log = bidsmreye_log(name="bidsmreye") +TEMPLATES_DIR = Path(__file__).parent / "templates" + + def progress_bar(text: str, color: str = "green") -> Progress: return Progress( TextColumn(f"[{color}]{text}"), @@ -43,7 +46,7 @@ def copy_license(output_dir: Path) -> Path: :param output_dir: :type output_dir: Path """ - input_file = str(Path(__file__).parent / "templates" / "CCO") + input_file = str(TEMPLATES_DIR / "CCO") output_file = output_dir / "LICENSE" create_dir_if_absent(output_dir) if not (output_dir / "LICENSE").is_file(): diff --git a/bidsmreye/visualize.py b/bidsmreye/visualize.py index 580b7d1..fc1c565 100644 --- a/bidsmreye/visualize.py +++ b/bidsmreye/visualize.py @@ -13,7 +13,7 @@ from bidsmreye._version import __version__ from bidsmreye.bids_utils import get_dataset_layout, list_subjects from bidsmreye.configuration import Config -from bidsmreye.logging import bidsmreye_log +from bidsmreye.logger import bidsmreye_log from bidsmreye.utils import check_if_file_found, set_this_filter LINE_WIDTH = 3 diff --git a/docs/source/images/bidsMReye_logo_on_white.png b/docs/source/images/bidsMReye_logo_on_white.png new file mode 100644 index 0000000000000000000000000000000000000000..4d99bd4807006b8a5a72eaee384411cdcd9fe911 GIT binary patch literal 53051 zcmeFZbySw$*FH#hiZnA1P?8}qQ= zpN}`}w-3JULet(4p4UY?OE>tGN-tkb?gVQH#;h$|yeGT8CP3(zd(SjAJ%f6&gYWr= z|NHe&aJy{+2PL|J-`irpx3s;ZsA~r>8|;klJ`ri8XO-ysaJ%tXf$g9nHeKHWTjP|A)5~q|$n1zJeV5R6J*4!fa!ibxjC-@X z8;LvPrm4#?jQ0gTeMe7|p{G)Vx={@MYd>wj?kLWyx&c%9g93q~TOWqCrH1>hm5mHho3Iv?sB^HJrH)bKCW_7ug3dj7t97%T;uv zA3T#kCKnlMN4V}uBG0uw=IF{Q8}5ksj_ba~ymei&xe-w0NthVuQ{=s>n;dOQm{&O7 zwrX7TTsU&`9-Y_JdAz>Ctifxq!FykL`Tb77ieLAjQ4a1~_-iw2h8B{3_ctiCyTuv5 z@EoHDGaR?Q^644aq}4`TmB07v3sTIZuQ=|k)8q{eG#~$Y^0`IT$J!_AL5g(_kMl-sr338{o>Fp+&eBexWs6Uy0JNMcf(WiTeAC zRYaL}qui!iQrBL8fM>Us&mHM#o9XY$_DSvis@M}=`rr}rk^DY3s1){EQ`@ViX|XV; zsR`I4GR1{2#NtbG%bYrG&V(XO6*=A)u+S{VS*BLozQ`MpqM`%ub~LnJq?SXtBfAzS zE)FeAe2OphtI8+x1yK0?IjHytw~J^hzYA{Rm!Olzn8Tlie6gX#z9L z)=egN_(clytPgXwcS;|$K(lRAu-`nAmk5Si)`VfwSZQA}Vaaw`>!Lg%YpuDH!eB;% z1bIqYW3M@1xp)%&M3SeSLzZ06c~mdqoRML$*3lJ;>Dg!tD!=n6Qx1V9#J92>F1Akh zpxyyP!f6EImrh(LUF@rG&RCu&sU|+ znX#^$qRHjP#q^`!7WMntl7!-9(Z9EIq@c+!dEtDQpn!*$quPYktBd|ZLq!R_m5;E( zwy8Do9UUI`fD!GZUTV$G>&Sqq(g269&;;nwYn`1QsMy(6MKyO?k=Yk)h1ctsy;AHnU^ZV2e?4?t0RE9%x18=BCwZ zoDXu?(Kc9Ne|L^@k@GP&Q|66WK?3wJ!YZndPLrcnnTma?EsVTHijUqPf4uZ~IdvUb zTKP7w(LTb236p3)T2oWxNTu<;vVrbVBT~nweSf`WN;rSr)Mu@CU?EXL~ z#~Q0={TI{O*pKg2ar~#=6baqyw2(3%3bTtU%;j4e2iVfml*4+nRT2g*<2bagw+gcE z^tj02d-D0`aco}_kzhEI@hc7J$*8Pec?x5!w!e}8xE6<*TU^<7Qqm1oD!qw%=y-?a z{v+)-yd*7aOp~{9RTBPHwwh3%e;zRWskMQ zYL4|?`DK^{oXv%AWsp?Apx43-&#AKEV|zr;sb>uHg#;|EDQCZ}o@L_>!HP=d>)D`M zwj^L1f9P&!tZOPSo(MN0H5OVsiLkU&A(%yLG>|fJ%apXgn@5F#MvVHr$AM$<6)uE# zc0cBW4nh;*QruwvwkpKF7FL!<(maCfTc|X$#>&KNY@J+J0<9NroVFM{+6#5-hR$v7 zvH{2^pRIL+XW-QH%Tx2&VQGYeEJx$U_mpL0sKaSXVdONiM5P4WEZK)!ei0n9ytNIu zj;b~&YQ?I-kC&5TWQ#0C9IE2Ej5zVHY`flzJ%CR+nt<1nOlGP>O5nvW)}U>kWqLD< z!+}(v1<$?KG#XT_j!^8(R1Jq3Fqp?M5h!IIzwQ}=L0JlMQx_{L+J4s;FWnR2fmD!- zGG#p1Js-7=GC|YiX61ACLeB;2L1p7S>}RLkFy1Y3=V8aK1(<+q17NtVB>5*sgia<6kCOOq(q8PI&jDQP}5{G5nv}$XaNF=p;dp zN(^IktNQ9Q3eG*y&uA`+lwprB4RXJyu_0iq2ph9?zp$-jdrceSyABDmjA|unp=GP3 z%5jDEheN0n*nQ)T+r)Qwe41cE3d_Smwn$H~h%r(rhpO6Nzok`u+BIfb;OYsFY^K1$ z1=ASU-ctNYk~xo4r+LU=9*3*!O0Qa5q0GRTJ{vVC*_^lpl_fGL0vAd5b7cDQ)|VjY}8DxLe~D7e;A^7H1DMar0%6hGnM3LJzLaba%^bn6ipb|7PSbA^Thh+J;yR zj^z~{<5-LTB6CcS+%I@kKJsVS)zPhX((s7r;uId{8OK>Q9Z8d=uB&v%t2ffnlsZ~Y zVooL>PV>&8xP%2UM2%MUhzqpGDa_kbp;VtmHx>+aDpdQ7>$W@ysENUMm(Yy4YL>#F zFK1Yb?72q;xd$1NQ+CmRk=eD_K;ZO(D-*OYy5!?o(!qML7O_Rz{T77ZTn*)(b6NqF zs3NVSB=N4%i2a22>P2fM?2c-WYl-Wid8RFuoj8(p;!jH2z%O(#h6oi(DFZi>z68j6DbI(0)a6P9ny1R7at9G#_NEgE zo`xLw=O|;}3M8iWi4y)^){&sypgOaQc$3ccRuuL2yH&Q35A5YPzT)ycC@RzJ=+;Ty zoLr4HgK!FqoHSzlxiu&CO&JV}m|Y18B-8=up7ikPYYq(6TB;=W#jp&N8YQ$o&rvgW zD*~fmQmRuC@o=yOCECUV*DHqg(&uY_-)IMaSE7KD+LTU0jmF4OEePhMBqNp%kLm25;_S8rHw zxeHw>S=1sOJPb{I6>JTs(q+IjQMNTdCpNPspd zWh)QnesMfd7*wu(HxS<#!~<6nZB1l|AAoSU(eEE1tLLe6$1Nez3@L>flVWfUpr4N! zW%^E@gwM2QgAX6a&Wpsifz@9uPrjW(19{_NJJ@8chijrwg|hcb7$pvWOs8D_WkdoS zHuWV^dOvxjj8^Bf)A8VFAN+fW@kKaCuZ=ju_(U00h+?7Jp0gHNisC-sT*AW=mIA-x zHaaQx2N%LhMRUn*c~kDHpvi)B#5ksI)UjVieicq=C2t(#Nkw9Eg>Kz0yOkm^QJcjB=ci1oA$g)e2tV8QP=kC0_49 z;|8*tFXl#mwAkXKYl|+6&2p`VF1XiXT-AkMPdoEwRNJ?athkA3O+GG^!B<)Vb8^ z)AL>n-^LDG|1_9Ix;`ks?clu7OjD#kVxf_(W4#ID{`Oot%06A8Ea1&`^H-b+a>+CQ)?OF0~5{Eix7F#nA3TE`-uS6zuALu|iv zS{e@tXEo;Y&q3j`gn3cpZl6sf=tM-Upp8!7Bl6-Ef2CL#ov1+p7Nwak7#aDAPv!}- zRFq?^@XFK5hzX9JcZvjK%Z|k46ct)4bZW_w65%mO4jW1OHle)rocvQ*=x~WU*`~%= z;~vtO5q-B<$TNZ{iSKW+S)W~3NpD)b#|i(97FN$s~|s zHyDp|53b`2s^$uJdfRN~dseEvB1A+gas;GHsGz49}a-wBJs zskFJ8#R&}(6+V+86_R61tS}6O7GWF|oj*OK^Hve7X>T#L;eRze==%4cV~clK)9{Ix zS!3ZG?2MzRzi=3DcR178OKFnaWTHcB5r44=&vW5BjGf z+g=$e0finp;RXVnFSoG1&G>l0uFB3>lWP?f>j2T}V$3S$FAIEi%0Z8a?z(WK?dQ%= zDLJt036gT?IakG0Xcd0lE>raSTzg#Sp>2Un_4RB8?P3{O zqjb_iY;sA*qq@pjkP$g=q^w0vg+EcKFOIzl8`0gow!0S>`5G~j@P(y#SwcUod0|z^ z`>&@W?Z4MPxg#zW;?{*pD!>(F;wL#RyXRQL>4~;0RdkNEv=)6sS48FIv@97!X<%MI zMIt~DK48_g`jS-0)iEwK7NQX~kqwugbg!(0?w$?dSB^x~UUeSx{Faykht@gOF3NO`XW7;S0%1Dt+KpLQRGdh-o^y_iiLS8t9r?b6c$CN<-FanuB@hp* zU*f#db>&1mNuTKM^eJ1Dym{WGg!B?vpjRu)EJ!J{;uS(6s`{q#m?v6|5@zDrETHKO z6Ou#Q-m|dBoIspUgA9OiM#EU_u)$-JW7QdY*A$H))L72~rx zGJ@{GIiN;dMxa|eg4KLyjvUPs`--!rZYC$xB)rn%#the_o&&mc>sAG=r@jQ>JC znL)2LNbgU%b{t?~?P`Lh#*GYCorA(&lf6Tm7bVWoX)IW&Lqn_JF^!y|;7M36#Bh+l zEvj9vB%UUp#;)J0I`Uj4mm2FD6K@dzu28fpD?r7krwhOJF5IFXZ!)a71@Zx=2(#uj z?@z0s)orsF5xzHdTw}%E@z<38VdSvn<@#mn(!z{Cw;JmteW|bo5&Es?1n!l&zYxtg z5_>-z!_l+8RLSqzQbW>%VU)K`GEnudF~DSi}I~frTrf)t9BGu(Kh4RgYV6QW&wL-XUOE$g<@z-rjA*o&~SEt0! zYfY-UFX6H;EXqQIvp?wSF+TH;$A%s;IU`;khesb6sg3hb#C)TxkLN2K2!kcF#c*VG z4DG;mq)A$_-`RAhe=+qi_5ZbqW%d z?O>UKBk9CzN!xEfSZv{IFoTrR(`JcFQn_W64!IFRND6Vgp(45C5})U3Vks6t1gqUJ z-zXFHq%{O)ppB%TPS{T+Iu>=)O7AMXA();WZKR~MxV^(3+UPaxU)krt-OF4* zkLQ%MnSb;Tp80wFJ~r*!IbVdwn@s$iiB(V=9ja5(3FRz^MxJ~`BIt;i*;91?&B&o5 z^V=qi>!-&E25B~@m7zQbYG4@QVzk)AvnsrV zwfavqITZC+VlPUZ^^nlW(9n1lRL zx~S{YNiJtO9N?S{TbC|dweLjd$-;~cIhCB$p5WKfnx1BVLx;+_#(DIHX^JL_CvL>f zKi`7hs8#M@4%#ro9e=Mse=?&OnelNv)R=2m8DVM@Qp{OQheQYaI$6(JjAN;BIL1^Q zZlnpT(f(8)>Fr|D#=@b^u8AmgoM|7U3KbT5kEkW~f_k0BMs_yw&A8##y=O~I__rj7;L2hXX2^4t+UJ6-y+1okVYSp3Y#!(iWjIr5m4 zz8kjv=%eY+eAKh`0iN~bJSNBRFGUa!s^P_TZb{w}p4;qByNql2%m1`U%sG-k%~E@z zfEh*(K0x5CHjCPt&PI6S-ut7hN3Eh=&9q&J@ES6!x%m)+5EM-RoSN$yyj!rJ^Ph8>}r&s)A!_1 zm(ZQlhKlE7W*-pw9LSXXsA%`nL~EbhC^x$J*L^C}3rW&^nLtMhcYffn8x{?RNfkwW z_>)^4>URk;lX60UAI6*9mHr#uIa#~dsgKA;uMWbmM%<@kEB5q+ zi4rI;3+h=IBaE%(Nlt9!-l@=0&WGzYS02~-qrbV%VZ5CD;JU5);ZuEkelLZGYc{-A zcNxsye1*d7U}^h9SZ>W1EEjBX=;uSz!2z8o~p) zj@lOanXu{i^ECLgOR0*rN2%A0UBB1n=Djj?;QEwTa1%^A2)%b_fDv;vk<8#<8H6uW zL()L!h~=k}px?7UO^8}m@pCB@W7^>1L$q1$0>_9#EwWp&8g6x>tlQB}v(1S?0w*J#?&<5#xckE+OQ2W>oButnHvzlVS z>H@k(9VV!cn;g3|omT5~?a0Y3q3Ah=2W0QPw>w2#uT~@sF6{gp#QR&b(8MY!_ao zsjHnA#p=(V3e8FS9v?C_PBXdwBQJ#J1A!Y>j`@1{uEpyd3vRe_dt8PYF2WZ738)u8 zs0AIO;nZNEV~mjC5Ou$BlSZJ;sSEEy7sF#Ej}*4&?PNP6CaCnC$`+a#SvUn_TF z#@{lNExrFdM*2Jx%UP9ED}=2<(YsFNR-p(Jl}nSxlsws8DD*Q+XG-9H-Jx|J6>qnX zS+q3VM)EIF&Iz5YEAjgGuYw{b1})oEkLP5fi^c7K=3I8|kd%+qtKm<-><=ARhj`17 z*IybTp{G?Ok^~5l%7_M&D}FJXTkhh)FBLQ6uD{7=G)spl;Ip73H_x^dsU}Uso4sY> zSe-+sr_c60sFx2he0R*&uNZG`M=2M6kp6O@doL8_C-&jUbK??Ls&`-4A`?@e zyzF0CBRXX(-j(Yuge)2=UUOPZj8M<60{FwaUO%T5OJlhhKh}G$7Wo5*tzk&YB_xgd z{qhTB$}1jg8AjDaM>R3Cvu<|}VV1ybR8uJaO0M7IkLKg~b}JaxOs;utiThej3060x zrF|IVJxk_F^!W>`km9?pnZOIn&zj4vb)w!3JqIAgvMAa7#P4;P-)zohjisG?8w6v`#^ zXo%ZjpVd=>M{Gf;9QueAb%AMi@#k#(_a5ROpPa{ge3SF0Ax5Eb&0YjCLkYpKI=S zUefH$U}_mY?us;?F>zr z+->Z^cRrw?_=Vi<4UDWzoXHJM%q(mLC{Mn2P?B313s7ouDzGTniMCe8-r?l#u8P7rqi%D?)BfZv~PGgFfP)x_CKfKp3AnOw}y z(S)3XiGzuSQNrEAm5ovmg`D5f*c75FF8TK^z+VEC=FZOc5N2jKH#a6Xb|yPVGiFv^ zUS4JvHfA<9M$m%M$-~y!z@5?7iR!6~zx5$*;$-A#Vef2VXG{Lnr-7lJi?aYFC3sH$ zkNMfyD=7T4dt0Z!TLIXE+1wgR>C8MDH&o`b-U}j-s z|JPf<*#FVe*~0W+#`=%BJw5qrIsZNp@cKXd{g2lFSo>d%K`R9Xh`61R%hU8^#04my z#)lZ&8Ce)Z{`%F#l!u*%)5M68hueUck%Nt!laZH&lY^1dn8TRGfQQwX!^q&@x{|SV zayGCvGI{C>bk1Y}`r$D);ACUrU}I!s=P+U9U}tAzdY2G6r37vltulnzHjSniw0gGjf=48ZsJi8nQ8(nD7{xaKn~j~Fg^Qh)8?1qahxcENG)x?wfG0ln$;!gS&hyukr)`0N#Q?(^ zJozc;;II2&Ef6tB69Z>EM|C?pYXQoqDUm>p$*W{4ZR=kcHET$AFiU zk=uyNfRV$9$C%N;h}V#jiyyEl|NEE7YhudE%W1;S$YsC^mdL@u z&d6iL$--!AY|O^VV`^#$!skCH{r_@_9E>bHjQ{cp?!WuQ-F{Wp#Py8fR*{!8}#A9MYWx&BKQ_%Aj7 zAMg4fbN!br@Ly{DKi>8KnYmE@RWX{_0;1;z>Ood)6i!e}BHK%AJ3&D)Q9b=YyB7$# zf}4oWG71uiyGSTlsJsJnmY^IZhmsK&QFos|TyoP>n|TyE#kvk-K&KSpLZT&)2;{*+ zq$|*<$kR9|t<4Rr(9yklk=SxWH_}3fu^8wd2t(QbTqMNbARbP1`y}T6_xVOToVbiA zQq>MwR$mc*e^fK~jmv2BPHRkrGA%VU6qK{Nu{RtPls`H65XJ)k(19EY+{Fq4Q$rF3 zA8-`lBM=LGh#>v%*zAALRsBEQzyIGXe)UngTMBASv`bQ&`dMgq$^Cn4uCMLwpKl-Q z>+2ct${HGyhE2Jx4cJoA5b_EOL%P>KN#_p^%C0}Qdfh~S)Bhw*4Frd0;rXSdmiqeH zg@ux@;?dF3uP!g0!R6voU-#DqI;*dD0hhCLU44CcR9ae^j*bol0_pK9Eh~$crcRW! zUxi9567?>C&Zanmg9%Wk-MsiE?*DUYiiO}~X=&-;;9x{V#QFKTQkLM>)>dM2vJ(#p zSNibAnWdFgxE{5tf*|elh=zuS&!0bo-jkA&z-LHENO*X7YARu(?9pO#?UyfK+Mh1L zVA1jW_xt<%VKPr)94=0Y3`P|0+_|u_v;^}B&&ZqHlaP=A3ew)*9s^$Fuxag_^{aT9 zqRE9lp?J}p-Gc)h1xZp;Qba^VdU|?x_S%t5K4L;bLZYW@|KPyN!XkFS7#A0pjZjG* z07Q2F5r2TNvYMJ$-M*}xTx3Lqp!eYX6O?>8tnw21drjojg7h8@{KP!oCao+-d3RPMUMz;KVb%ZNc&> z-BpH%qYx&p`{mC6PFq{sz`#IJQBg-{{$giTPF5D|)!2NzZrON$zj>!Ww0PMUu#UpQ zLQhZ6ehGl3{cx<+1z=CBt68|Yxdj9SUcGw7%#1lu#G@by3;O{Y7UA-EX<>SLdU0{F zr$<~&Ozh1Y+~36jif?7bLwAalvd%9qYHMrXx4L8F<7=v_25zc7=y7mzaOUGv7TW##^=prt62_4GdOcpu>d$C$6TT50!b zYi&I|JPh+y2TuCt%^Po?r_mnQG}YBF|NOq++}xCkd9k&k^F1vv=SV9nI{==sw77V9bOb_mTpmoG79yB6YWSt5rsmnRXHV`2 zmeAED>di1F`y8craM1E{cYJwyd1Pb+_>z_kY!(_hl8DNwJ3qpQ*w3F|fJnf@yFOmx zbM_7V2wdg<_7;syuoZZphK9!c{QNEtaF;rhKW{WOivUDONj=`I_<4AE^b36=CW@N= z>UQGq4+U_9kB{%|+qXXyz>NBlzkFO-XtblEqOx0T8n2#!2&ZSOmQ+?&ZhVirI6bXy zY?Pve3B)p%Aje`!{qgbR$Nv6)dd*5>V`K71;qhnV0|ORaLC63NuCK4fvF*JXfDK=7 zlBExi{P^)PGV-hUFGn36qB|jxGn_AGqo3nnDFAS(sju&aLleg8(p&=reUM~c6bUaIIeBMy_lM%GkWo0SAaYsS z=OAK$e+YS;Z$A9GXl`zHhnpQMWnp0%o0*xJn3xzJX9Xd`$$2zU5Sf#c)86h|q?ifd zn3xg(6S(4$c)?TP{D!OF@j1oAPF|@bgpTgu7z}_ZF<48BQ!)d5qyvCQM;8(KbXi$h z%~^9r_Li2Ee4|!$0G0-3Bog#)ij0&4eY2D!h=h^DJqs6KcWefD`ee;Yz3_;L{S<|$ zke?=eF8jvj=CgTHF(72Q^D}`b0c8FO0&3iX#M)+gbQ&uaz2X-z?w8HgMd6cm8|U{clDtihrVPEP3%m;G#!57yS!Vw+{v81Ufs2a^0|R5}s~a~fD@B@*_$mNw_;5%Gi8)MYw*dRp%x_yR zHwSPEPGIUA8VXfQz;JDzSC01f)z#I&gC!*;#qEkF;FdKJse$DxE18&?i~dSCC(CVe zGBU3j84D)&NH?#;L~~No(;qJ!6BLHt>@`#K-}A*uuXPW*1d(s zDzNXt!4e=b@wpwBmz71%#%plY%pHR43{o?8TglnknKMLynORBOOD!`a!*nEr*Kt!C zWJ-P!aV*jM?^})ll>p!=e&$b2ODpAQd)gCIQ&SMyK6jU2>g$iDD~JK z@V|(S1d$~zBNHAON&ot_O1q($I^9*eU3)5rMOC>l#goLcD zDvnY7*Uy1#4=D-#=J39?cXDzvB%%dS=Ku%-!iTf{pJkEw%**XQU)yd1TfTlxPfy2u z!9P1ZJPg8XYO2n_9hKIU7A3d~V0UgVW$qUOQqs3zjEszOlHXYDraw+^ZrmIlCx8#G zNn!bgZeZy*N8_aB+Frg^6h~Z^s1egNKj5v$@&o z`x`<>CyOi^D_ej72PglTf3CYbPpwF?*f4?|F1T~!3ihg)q9kkU z>q+_DtBZPGB*li5eu;ho#%^l5{e6D}c5C#rz|iopO@~*s_1(6C^~_BJ*q z3*_+m1;LJ`ui5j=&CT5ac=xb|oJXTgJ zIyyQE3JO4pGF>;me*HR{%5FAQtODY!&lwfK`Wqh~A3zj-mT5lz+R3`+49b+uABd*2^5g7yGd zdCHMQKQS8e>=wu+10psPZJT>^bOe&p?}vxGn-jagWzkdESX0XMVI~HKA3uJ0Umt1% zlu%Vwz4x3_$Qv(INar%>3VKTEkx?Lj=9f1#TmxPOP@~E7%F-)b7#}~m-RC}fboJ)V316R3uTfA2y&{V z4seV?Io(G-%}1TYuYPB02>1=)YKDgEZC*EE`SU}y_9($@VC{R8MK=dCF|>|B2Lypw zI=BHlz?Oc0TGyqrzkc<4{PSDtBf)r?CNnB(Bu1Pc|BINu4FJ{H5@i<`7vX!G0A-ya znPDx{WPFCd-UH+T_6O`vS63Gm6_qH;)Y8(@&`?xe_04)wMNjr5*ihijch^S?fT15z zV!wVaD=ggkk&wN$we|Ay5-Su*^p*|G75FE}&c6X)z~zSlZ~{0pJ{Fd6-D869VTLgI z>X|#JQi_Xrfhz;9j{Mgp|4NN6^`;&>Mu1P|h=oSK5NH9$2gy=4!w*Cw2#M}c%&K>7Ek5_I^78UtRr9hr%78Y1k0BGX zUllepGlPYNEs*a4jtB@D;OT&A3sF*v#$xsbA>#mCnX7s)l)xx~1rt8H<9BPFBFe|-nRhfc&%3~&*Ihibc2 zI7~oxT^$E6?=RpNN}u^vX7>x9qnO-X?y;MV9R4iB-zKF*;-dv+o2shg!E}Xy*EQ`n ztl#_hE38J~d#Qqp%ggVYPjq=nFftYW&enSYq;+?9>&E*5Pyz&fa%P4bpdc85o4ZK0 z1eBs6=EdPa7=VilF2|i95{@E$_|^ASke{F9kt?&qazKv=}s;ou3M-lf~vd^ONDe zz8#Rq33(m!+uGVdh4_pNKDWn2Uj7I0*N!@55R)Lk0GtLWezLnOoj2a%dQ_U1H>u%< z2CA*0EnqI3N#19mE{%&*Ra5KWm=WOTHw6g|ByW>USlP>O)YQ~^UtJkN)`XTyonKf0 z?CdE!g0X0YW60GN!7^oJWI+B^shd0FAhOvV%fX>hygWT+VP#c0_E6-1!7L;M!0#Sx z#-H5nCXmu9Dvri-#6STB5FQu`_hRHN?Q?+9TRS^X)s3Vi=p3LzjMHh5tp?Vm@~+&^Y>EJzzON-!h%UZ^LtEmiRUZRf|zmvi@U81`dRb(&lA`Dbi zuhUf#L&MzlR#ueY^NkoGkZ~FT;%F@)i_E@bCHNQ?7S{3j=N?1>%6Zb?cxsV+>iXVZ z$s())(aP2PudX(Li86YaO6H9t004G9)B^{mCFlpCphLZULvaURsBX9HI12qQ- z5J*FyvQx{@kp@UsM0020PF|l;ft7}#Uk`fMNCvwP_fV@%D%~pkHU6RHJ|(8#Q1p%6CXe2qV^hRJeacF~NAg_d>~AU0_Z=j0&IVy)Sz*~Zw?(r=KEfR6UTdlE==Oz+!C zu&}bp9d=Jnz<553qZCLB1n~oCsHnso=AS5?Q{+=v)__t1N>b7z+JwYJQXc#4U&Ppe zPkCJ*0&XF$Ka?&4q&bl8?b?GCa+Cu%|jS0 zn#$u~2wnh{IU+v_8o~yM5e}k?U#J|cyCgI=R@GYNAUSiayp-nWe7HR`lHx-q6Y%5! z$xkAzyF{%B6t=Dz-`0@28d^o7qoOj|Od=@jU_ceSz$(R$0v8Cb)z#JQs-dA|X29GJ zc;33!T!WneF~P^n%eitJ9TRh6zT^x>1_2JX!1jVCU$w;V_m!rD11rKfIg&}RViDj2 zRl|tAaBy&X9lmYdt{?217iVX+goHY_5r;!UL)|`=oSpYvLV+(zVh*Wz3P z8@;N}mI?~eH$gm?7Z+X+w+?_Sk7&W=aQvK^>Feq7c2&+Tk9r|+H&Gx*+MHJfxXog- z)39?Wz)Qg8-0CnOyk(V@f==5&ERtg%K0_zw8r!?XxLpHk6Hp&O*KIZ%pG0%Y-+p~! zMdn15NClIqK#Z*Mx;bvJK>`dgC?w=bq@^MQ&J6 z41Fckl%2my@Q^6#=`B{i{R*_ZpBg9My1R|QODn$jpx`^`kBkQab~O8C1C;p+OOJof zhnRs-+gMkZFdBY#w$T?96a+>B32AeC`x!BD64LHmtr-}{>vDH(ch}s=C=ZBK30rZ1 zS3CkH4u}v(ea&DPHdO=25l`icHX|s#@4!pIFm~OjNWr4ob>`y$X+gsHGFwHvi0a)y zp{1@~XS2}2b+`gNa=yVvH#9Bh=hPQzSy@Zdeo9tXJ;BMAU*5j1uGdM7x`0%+)c;6f zH39`!TxzQJ`}bAeQlWht5|Wb91gyCwc}8}2e)rc$(fkQt>gq1Lf>D7;V&nep=JN8; z{6bep)aCcxRTHuKw(ldDR>z;a10dW1E2X3T)~VM227R*477*Vt}dvG$z@O^8#@|{(uTZ#A+CnnfVq-pK?jC@ht$c z0ow)Y+``-(h-+lj6&$P}h1|)#zGzb2CVK;*9;UYjfRF(Nfe(mg&>v|l{fE8{0iQeP zgsS#EnJi1!5ZHrOVG#L#|N-yKnvyMN*h`lnCTo>%*zlx&a@vj>?J;2s5D&tCORjGJwvvN}FKemYaBw@d_J8QLZvmcN=vuZhR}j=@*v zWvn!)jGqhxh|1P<<>2t}+jegcU*Gn+cZ^(I(;xDn@6lbOHm|uC&_#)@@`o zX%GWnsPqs6WCd`$Qn!(bkx}7~R~D#%0^v~?mzIb_mSB^IfM@U5m<&-Du7Z;V0K$8R zhYVMz`n7XFLI*oC0Ko0X4+dJ=;_`9;@IcT2{Q)G&>REOKYQkD~=ne33;2}W9=<&9M z03rDElXHSo7a(?+y6*4py5F7}fD&hmFZw0T%+^1?3*Zd#0T=)%*E7F6kkvqxR{>JO zvnE5Ws-p75>J&40C?2ON!o|}7z*Tkpu>?zkP7gs6jZ>!mF*L;IxY-ZtTmrO^jGN<) zjau{ZTtI2pb;i!0XRw(J0#V4?#-^Zs64aU?hrMrc-i!Q<{uMB>g@$3krq^+SAp8ml zRNp=zf+Miu#giC9c2nS9PpIGqlv(8XKYaM$@COHAji=`wSS&cxdinW* zZR_~B9q>_5Q$}Cw73R4`IW{i>(Flm8S$uAaBO8W*!-0X@O^1n?nX53C`KSZ?HkR6b zygH}*1D*p8j#Q_i;z=FZ%05%MuAE^r?r?ixggTr||SK_Jji%P)a+W^@`#(70R zNN7M8(@zR`V-JutUPebhB?q8dfEX|MIti3hkQJVo(^fnTG7zsoe1f9v_o0ZoiV7x~iMTGRy&GX&X17Epme z2DXiij0`0>E-~@tDE%9t;(@~uP~(Dwza?x_dzT*aO?w`o4+DX)&0!;i|3)rv-1qm@ z46rOZ2@jS}j)Ko`7+6>^rCz@}`?W3*=5hk?&3eZmtH+S?UE}(d<>Y*^{7Faif6#R1 zVLA2P+rMwK5K z;K*Y-&Kzoei`tQ+N1KF7E`6=^Nk!S6ZJ%`x^!xR#G988`!6GTu%*LkuLw4_ZkR2$C3Wm=`>wM&&zUNZ2)K9eUb<0E?4{iPvAz37Es#SW zw5F*&Dl@xw=Hz_jL4-t5H(=!JnbYhOsV`vR&WaXwI3Fk(9KgR-XdSbz4=h z7#`xsH>{qfK~jJV=r1aLIdrJe%79T8(`mJ$%+4G?E|=Z~aR7b5U$iA;zb$+#YGHRS z=i?lowr4px3!ln1Qz(#gJ?D)5dQTg5u({%fes%vZpFf`_e4^9T=m;?$D0{^E6VjhP zebQRhv`}ssSrnn*&c|IVlI$wZjxYMSg^0=9HFwL02aq|j^S_|<=;-K(4gK3#af4Fd za^HDanUGh1{MdSJUg;`$Xi#l;XKRUA(DFk6!-pTi;Eq&Z<+^6g zz71oGKeV=!Xa|dkh`GmkdwU-~cyPwdnFWqn9BAqX%By%!DTRW{uTnO0(Tl?|rOtnv z-^1rk)P3{(dF$1VAUM6lId{DOB(?4I_4Qr5c5U6fCho`P&70}6=&a@E@9z4WmXx#^ zPR88XN}eQwzCLc@A!ipCnAoCt-yrUo=lB1X2$Y!WN9$d&S>7s2O3q*NEqD4WkCxeH z*rYBYD~l*Fse3epkiMQ?Vbe6~)AnuKMz|k0AtK8haaZu_q9z><5|Dm6`ZI`66=S1OKK~4ks zlSrJeWSl%AwVxaImbOmqim!4c4|LMXTmG+BMwuy%(Q^I%`}e8R4NBr7H&Rn`CpLn5 zo`g?}5AVMWl9QMo^5^j+8m#54dWt)rIjsaxtsv-gA*uctHp z1J8c?^M5r8#;4TmZa>S;{&)CmF<5y0qtYQW0)!S^Ap9EhB?tD|4;BeC*u-xnUC&73uZ`BrZ1b$>pn8jXSlqEZ-b@RO3h7GR|Hz?HXL(Tk2g-eXAT4+5YiQVS}5SeX0pVKrY3bU%3Dz>pbVY`-FO zeJ+kT9T_=)SCbvpv`^H26?OIb9@V*Y549_1zo@L74P9AMGE~(&ZBF3BoE(LtAwuDy z%Uicwd8CM+*yl#d^Pd~r+IaYb2M^|f3!12v)vFihXGE49J95M>#nJE3A-z3&?&tc7 zrd;;(ym~V{#S1Oc8ro^vawUujYg}9^lO3{tZ&k!uLr+QqPPV&aQ9H=HCVvQ$(ekS+ z)>OH*VuI2;HuhU>?e?!P5{@4qHq-9UsKNP=4M?HFvAplS3v#5OxcJ>Iw@Pw%-K|HQ zl1a8VA0zz@$v#y??z*u)xJ|qK)vG_?{gVs7)l+)Ed(9t05hlw|=r@c_^|2 z9VSF{!ONFv_Cp6QyCNq(pbxpe9Zrv1Uw1=)*RDU10q$8{lBxst)_<#|yUer7xK!#) zc1^o}{dkTyQcm@ll@@=(O%#Dx2mSnTi_G`@{-B`1Fy>!87GpWp4ar$6tgJW`LPkr- zP*zb9bGTqg6^&VLYj1xa&;faS^LymZ+Q$I7qtkn411KOm&uO}x+J9{tiI&uJHZ*j? z*1nt$5T_uOgoR0D&X}}O>TgHK>{+wM&fMw*gxmVM20?x6wrxZuZ1VTtvkR@f_Cq-f z#x3CqRPNu8pgTZRlxjI~y}r+I$IOGCZc9b{Qqc=5h8!PWdS}~)0MnZ#M|~mNhqySL zoZ1#v;B;YzhwkQL4TRO+{Y8};-dflgo0^8r z(A+{%#}KmBv-g~_$*64ck$c4=XAAm=qndLo9q98Z-`2dozGmuF!CEr7-}p(Bz8#z2 z?d+-PI3#%e`p}_cv`jkZ!s%&^ji8qn#Bz8LvNdg;QS6Bakh{jq#BOi)mhgu+c^;!v6Xml8yq#D_?@CHbMfzDTg7V3CX#&obE6`1)gYwS45}ZFLtfU8-(u#4kyG_HKTi zvM}cSq#e26!#SNCiJjWd(%(500t(VyE07vT&uk2Wg+`_*zh-0MFf{o9Fcm0s;F% zMYx}}c@tVxCm7#+*2wTB(VGIiv$ofl5m2$73oo9T7_e`j&Xz4(banSXerJIKgK%*J z;20XwE{}EvR>QdPhN2~{Xw~@k&dDY5$t%;Bd^O%Met^RGL-{-fi+PqC2sOU-H2fZY zN83FcOHtr}>HEZdLD+>@S@-uc!L|x!qwLVmiQm*Oo2EJ^TJ22QO1QryfYo_rz84IAJSF4*js_?%mZ_e=bH9Met2b zOcb=`ZJ(FeG&fN@Nla9gdLXU$=!VKw2aZ3g$;-{{)4#v5-MuAW(TZfGr8{F)OZGh; zCwRihp=~mHt5>g{793v=0SJ%U43o3+M-e(Td<(#{xc{20yuI%^Tx95K6;W2zE1x3ZZ=GqDTd%Pw`GDShsBNR>i0|RHlgV%iH zBB5Eq7R$~BI#y(q%S?5yDlcELvtwO~HD}#rjfhy5tfXW)8t|ukA82Z6 zGP8CxtiWu_VLl=|zv|t)`+jxexc@Lb$Yo;6pgSz+Ss#?=*tdUw-_rDr$R8T#XSl3h zeciLA8rNvZ4{rhL4H(b{9G<$YL-otouif8shC-omzwhk)cGl#N#F#O&VT2C!9|{|) z)V(F}_b!c><>eP}Z;(k&EEM6l&&~JUOV?HaEB*JNPyv2lB~nKV@qN2jA-4fi3X|CWvh$xzQN7ZT(OD`>l7l zTF6oIV@40o$Gbrf`ThLgLe+WUZ+WQXVLCF~@($dpXEj|`czU+In&cBK=W0R1Gcb5e zt6FEa(3wVCAT?jzSR|qE-4(R%2lhS6{5OqpGS3jBr8IMRM$8V?Q$`n#pd?!=hVMbdw0JLPuEQ` zb);HdUEPfP_c4htTfUrgSPqnegUSDPi?p>T&+Xa*Ip@q2M_1Q5uCJ%Ge3KUaOq6d` z9r1oLz~#uOsq%7i3!J<$^+NT`t8_YCQgxEH_&X#N_%)>dC>RT9{y$HHs&?8jxhGGb zc7IEMkMm5qL8kcJ%vPvU=&u|nuZ=iM5t01ce(fP*G!n;xd%Q@-0ft&G<=5ET8T6!G zbIOzfg9qDPU#*jK3QTd+$0T)uTpJ%DM56SJ>r^2C^{2WzL+Qf_adBFl?tqhuDAU(_ zMGim{0;sm!?L#z`Jbjw~`T1^mFqj9Sy_l9K8*{=>5M+JYuII+r40u@b{#ATzER8`U z`b27XrVz(GzoSQwo*6$bFK6VKF_pKgM6#bh-$n}5*faJNQ4|s~F7q9~BXC0z9he+! zX@9xNQu3V;zI1t!@2q%~gs@Qb@R!ICKTuQkWwGQV6s23aYkI-IB5zhFu8|D-MV~)? z`=X!C?|pI7%8#KVfw2B=W9XNe*-a}d+{Wh*Zw&6YmgGkY3pLsiBPxDSH);xba>|tN z&`BEaLXb64v4|)INykIUD^{)?BPFF$B`q9#x^bs;hq1^0V3%z9bTKKZiKlfBtnxy3 z=hqh-D^k~4#+i)ko~EeyiMvpI%xmq93W(|QdP`5SN{$Bn^s;92BwDciS8I_dC1xn)cynZ;rzy)g`mD1ew_O=IN0Bo6Q z+HQ1cvc-k@zLQh$U%S=_N3b<>98{Ps#I&bmCXqyz2%J(?LIxwF36fY%Fw8ME(f<1C ze-FJe@(T2`kjrhr&fG(etBg%?z^ncA3-KX&Pof1p}~QOyO4WQS6OZ( z$wGH5P;jt;n8nvNOjeBo)Ckgey!{ooF7$Ph$bbRQN=w@tBP|nNXu6csvcY)<{JY)2a3_UXySk53Gwf)!Ivp0d6(@bfW$ zde(rUvbbP;{{B>+H@O~vzHpMDslgWOZVu{qAO#vQZ?yiEEjk7k{{dib5S6>Sx z6%`lP@BNH=ETtD)}3W=s9wA9Y2C zdho}zU797sLl^7tL#MBigRBiHeISp&!{(2<@#%Z_I*8!KT8j8@6cj#=nYaVg17iV^ zfSu%|y`;b?kD#_(FxZxXJp8;-wttuhQ4Tqt`FAqjaLrAj(*4>7 z`soIopJb8jm{Zdtb?l~)$}Q&x_u3~nb!ur*(bbl3RrFtWBZY z_Q^zfi5{JPdP++BC6$E0E%v{^f8XNa0fO9^AxxLRBpu&T@kF$!ynH7&5h0%b{d8W# z^ZK8_|0U;VwYRn1eEDpT-vHfx3x>(aOllB+3$Z}KH=kiDiX8)ya*>u`BC4pcnRnVa zVCdJFdGnW=nkITAX+M9oK=CB1AEfdaMR!a9Savp7ezA>Cjub)u7mS{YC5e^Y8RG(vsoCB%*r{v~ zYe8jBwqX6rm6c=-!9+cze9*|rNQSs%eBOW%_ie zNra4xx$0I83KJ(LTc_y8mE?(Qqgb!O5(_oyq;2`Is;Zmde;#LQd3>O%QF3akMMmyU zTCh2D=hkgY)>k~KAZmiw&Efu0nbSpr0!3d3W~=8Geg2%jwXTr9UURS0YoC+KCgCwbod# zCr%ppUN92mC3sPhu%FVaTkrT4U!%uh&2aG3SbOi5gH zv_>rl3I!L)KHo*GW(v0McK@{W^pVNI zWgob4(sOs;)IFahwqcLbeykV*H|(^Q$)DZ&bqfIv&g%vJy84&4+ZzaoNnVos-6N7p z(Y8Q&2GVMB5~M%O?~^tsRmOy=XI}5%Ce(Gl4JxYB`el%XIsuY^Ly#f2VEV(6!GsS& zDR-VfYn>T$>5^`&MON4Em6xWggaequ>REleDGt@z|a$2ZPCL4 z=a?6`zFOz&YuDcb!vH?{)T{*gFU*jRke9~Hzr;u#jDTYKCwUf@GeojiGS zMu>^xu`$|7390?$?0NLeB<$L`bBUp0$ln2{K{cJ7ot&w{c#*h_1ILTk!cOwQ6Zk|uA6~J_+S(H?3;}-hT<=MD{j8SD zXgC=d82CSqoyI_!vDGQoy3XSn%FQU$^3xS8QPw>6HKP1;BY6 z{0A!UIJ8zKd)@x0fg(H-$k3>)C| zx4UjvbNGb|nUJ?F>uh7g_7j@uGUqms5T&%7!lNv<6ZF+8m ze<8oYrmX%axTB;y?7)E>?e{+8Gns!pMG5>KNfVfS8o27Y`83dU zSZ>}j$RA+4v8B@;19fpqKpj+S4bq%G9m~&RE&Mhk#l=5#DET^RRN&C2fj{+kOP7$v zkv`%Se%1~@s3z$`IzkCeRszY7DbtzJ9n;j%1_4EKT*~r&;RYJ*tp-}^k@!E@JP?VW5?VJ3OAuK3XENw z%4lv9P~YvI*2OtMDNe<<0BY{;n9vbB)B^ z40x!Z2n4>9O-1>+xs)ZlYK!Q=u?IOF5n661^8N`2iYRH2bbw0XWFYvT?3o=^>MWllUcY_!-o5kki*C-O5Uwvi3wLeMqUruWKGQT| ze(zJu*M;K+rL8sYDiy@CTdoxvqbG$7cKF5-tscMbE-g?)FIg^=m>K@QsjluNI?&|P zE>2D_+PXuQ#AE;QZ7tTG+awr|0Izh1pL z7aUwnGXfq`%8PvkW~gmwP<_|ZUt^{R5hNVbk>?#STLKTwkC>MZ=+i6Y?@FN@kzJSY@EkcZxJ59^KpmYZNtvdlg{SJ%$iCyu^$t3V2X*=eewou%tkuf73wf#& zhe0L1;R(;|5gbjl_Am-#+|?#eUQhc`TGrR*=d8AljuA!U3>q}?`)EM~;h9w0Jd6fO zaKPU^e+iF#gtmMB-~EBiTrr;#=z8%*8{8@)@i?d#8&l{q^S$V?%CV4$Vbw|L@=4L}gWHaAMnT zlfdNgb&KjUI=3#I_JW}Ur|~BX71Fa6MYbuiG^1-IT@fLG+?&Dy2{7a zeb_QXu-mgd%7j3q~Vtek$Qmt|UJo6lZd-G?;5;@i6Z?0ofs z(VpQI)-Z+hb`N@UJS3!)2xV?4=9fU1N%+b~E9vlvlrl3-To~GfT`J1T>T8>H<%*Hd z?fJoaCx0C;Wd<{#H4f9SRP&p+Z+$RE$jd~)uPI1+zi?5lJnrf zg4)8?`sWj)%>4ZZN*w0u81;Yv0otpDjhsXJ!q1^zCHw4@4rUh=NwT0q-q5Dg4tv`n zx869JX!)2Inbb-xinXY_ZZspJSTeaFfkr<*e&U;E9Yxa3c5pqFPE7XI2uyUcvhSZ9 zrJG#oIak02bhuWDwl8m&kPcR4G>RI82!5H61fufEMYHMLFxstP_;i1N=dP=n)`SH`*t`%#AzZ)94Zu90$$2^gQ z?Z-7n9f4cga>uUkrr~tvwt;K2iN*3H`^qQ(?J%pA;~bq>SRWa;xpzcGQ+4$ZZd(tT ziUlE?=EPs}0_3=Rg^NKHg5+Pner*R+iMMi+uSINfau&62=FOPJZwf6O&Kh+iU+2zG zKXF1#{IvN38X!`GK!2Xu{6Y#e$P$=g>KRp9I*;qN;%ph0_(={QKTem3!Fh+0<+}ef z!)qm~qm=S0sN}P@LnLFZldZ2m03Z^PJXY5N!SX6Z_nZW|2oJVtj^hOqqYJ^kaS^Yj-W$Zqqi|q)!?84Kl1*4 zR@g$rm|wXg&5F=WiRizdce%5X-=v3e|K{O z4Qb9{bz$^9S>U7U5NMrsBP?%3%oOWGMRi6E^>R#YVPsl%C};m$|Ky#RXOs^2yZ=9D zsfEqe-mJCAePpq(`eLc`jrGNw zx*D*Ur|*3^N!h1`MG3LXl{XyRgsm>rtZ8kr#+mD zbq@u@I~dwUJbC^4HWatWj~N%|9-TTo`JbmXEum{ouBFPKEQr<%X7F)S9iYFKt4;s0 zIOdtAFdw!|IYGU;rlu7uQDEe=iyv}}3LkGMX7so5(a!kjzB5Ny&H=Jp>Xq_05?>I?e!Kj)!z2DaGz^%Zr->^IdO;c=N8GA*8fC`U2*jC z3Ovf1nlGO{lgsy_VN)JIJ_ox_T{7hVqOl(a`8|xg;2@YepT2O1n&8*X0XpTB@!>eFKwx}7O)Ww2S2~{c|PnvJR9|F7SS{HOP^TA#5{Zqz|j}A-z zP+eV?`3i>!|C|A)Zd=&dXZh=x^@5^Q&CDOTzKC zR=7#Na^>EVwJloVxxC;&hyDz&l~}ZaqyAzX_t1Y#W|ehn$ET+L0CG~PK1H{$f^Kh=4Imi6tJRo#EFsp$}_u$`r$?p=;* ziL#E$e%TrT{ls(P;LGsi$3HYQ1U)-s)61Zb_%btb zV8J9g+dr4nZXGSRNsMlqtI~93^~<||cu{8540GuL@|OnxJMO>7RpSO8oN#VviPy|? z6ILIb(C2oyY}Nb9{wBH?Jl=JjYKpbA8D^m*&cHpYSHQ1zOuh(ng{%(?P|}z>HGIBr zCL7Sw(|1AO;r6KCewWQ?=ylML3*?m4)sG!JmgUo)x;;lRV3F8>K6H^aEuI+WLMP|b z{m=Jlo5J2lMm?4;JyzgU=vqt8?5K~ABqS!xyRks-Htr5gyCN&%UDo+;n|R~gL3RCN ziIF3<_mt^GkGyB*G>n*^-Xg)*Qn$PPp{ePp{sQ9(d&-yHX}OCBh8wj#>IXL(-ShOQ ztWE3J3(x}3DMNnz8Z~Ov?Yno$=*)fiFS^7*15h98|AxUon0#g&Fq#4qM~xXnC_^&; zUqq_oxz_R+Gs+Vh{tbZ_U_TU(IqHg~g#|;@Tg;>MUhBog$K%@7YFYes%z?NvISd-E ze}f3T&s0?G4obU4R!Y~KFTs+-QVq>I4?={UPgyh7wndZl=vrR{**AI#ldF90eRId7>nB#tbJc2?!S&i`ZrVA@8OY=z6?v(*G1?Q z!=ym-2ONMNyP%^WA~f;!^~!TQV`$CTd~;X*xKXUA%q;Rt^uPc9D?s`wQ=Hp=F*U&# z3g9#s^@Kn}B`3T0Udm=a+%WFpMb*#@mm9BMRbPfptzlIbYmdYNLeHO1Tru}-*`O*~ z;mhmisZK{TSp0mK%f;vQo6LFXTgp^zZTG+D{?Ze_gBhina(?D)qoZ{Hts67KktIEj z`AUIU5BQGT@B9Fi2_(xCMI$yNU~|PDcnwz}%{J^E+xF<~d+qyAH|m-5bj~JD{Ws`S zHa4K&F=HYK7Qvr;p`@m+?pIz|F9cfVJ9d6~HZGtBNX_5V7%5^_H#4zWKdmcUKE#=* zrt{gkZJW#Q-D`;a?X7160tQM>wc3V%8^@xl&v54A;+pNQ{wD|N%(gyxgrKi}F;Yr^8+-Z)m9oPK);(u}GG*xSI4k_1OHC(bp z82B|b9GG-MQv6`q&^=()`(o)QoHlGQeq@-)(mQHI;YyVli;dII3T&~~KmM>-#U-Vq4uIjcozrdRjxI6j>f%c28MU=ma8NW^QKafM(#k6c; zAR3QIT(ZYY@!YW)WfFb+M744JLLPBZVC)_Il1t4=#R+o@Cs}Ty?XW9^vYj_ ztp}T27{qxD=%KuyZm(C-cC=xLtK_< zP@Vv3{sydg^4(D?Sc}s~&#USb%pBY`IzQRjJ59{fOw3eRa#x>l5Iu+CmW1d3F!{nX zGaG)!bZhQgfUjsT-fRX^{f^}Tl;|Q5L+n`}bh=1lKp!CM*6xquB8xvQ`hbFg43V(@ z^6)UD^LX0E>j_Q{Ce=0-kbBwe7S4iU#o7iK=Y_huF;{om9D~)ytO*U@r62I$2N)OU61!VTS`JM@2;#>e+hKYWmcvA*&?y<%T_;IVY_i zqZTT}3x8jG$=*L|4gE6MDHScg^`&xS z6zgCF{ZMsF{{T|m`57LUygo16cP@U_?zXMZ|M<3Dg1jdNjbvq~J3pfXooIS?f_bqm z`VE}gn0H-@l0mX_Jmt0xv_#M&DUImqA**YoSA`iaU7DO%ykqnSv>2j=A{W zvwl8NQx-=d{h2x@_hacN%hz6r>_Mma`CFC6BV=n>13|yz|Gu5w@V_+UQgA{+Aahsk zHXVxsL?zOiIu$GHvD_b5a{G9(eNk8u5!u6FCySQ6-qhK?4Yiqk+8ZN7ecj`M#TtKD zMS-W>K>9^_x!$lVQB+mnFEXR)IKip*GPf&TLiQk-a!EX!x+bcrnV8=z*fQfBcVHK* zP)NAWO;(>(_W!J7xg=;jIYUl7C=Vk-*|$rO#W0spJU|T+EfkmiyM0#F2Ut}TQ`7TW zZc+!b+pdh60dJ+Hb;^h)JI-k7IW}vILJjsfj3kc9!Ro>t(@7q^ACh&;|3nL=_Lq*p z&D{Tzb>Kj6$;m4gx@vg~K1+u^e*O^>xO7fqHf_9l-=8=vo9IkbsevAd;ET# z8!raLc3fq}UFhAqyVh!_Y}8o`c=g?PVCGZ$?9j0#!tMw<1_&0r?mZ)LLZEvW*KKY0 z$$V0I#wfOYbL5FLXFNN8yLr7?4S74&@^Sz^%-AvgP~w8uPuP+3xfQBSa`^D5<5mqX zuVQt%cS*%SB$LfH+xK?2Z^%mu8a{Tc;M#ri#Kg{UQxTKbj{fS0a|#|mHd_@OPxAb9 z`Gx=o+}znt$k95Z?S3IQc`jx#hW63RnH3t+sBVy9jwdxxLY2(xuWI+na|xHsDScxr z>|Lp?xPCV0<7L-@7s^$T_I4*NLD(L4a1%HA6Yw3!*y&5Fw!m+SxNfoNOe5}3Gf|@a zTa$5Om@q~Arrw(9s4@4PNCu(WTW$(p>%{|LgTb7C@yilwd%+vj-Mf$eZ=R4x)4@mt zPo2PlLRw~J6|_j+aostTFIIn$Um^JJ-1@z7?OMzC-9`jOT5B(sG-6SE|6R(E`GEHJ zc9F;rugk|Kg8;?$Ol8GlX0cq<9kgCyH;l+eaYnGA`I)FRUu~n`QW=VljXAlS3kv)? zTmgao1ryCkJtER)L;@XgX>Zu@hZ1wQKk_je$BCz>7sws2_0j&XC)wQ|_U^;iL)H@(%de$4fEnMiXm4G{+C6^Re=jtO zi}0W(W1wzfF%a_d*bP+0w!EY-WKlNmUCT^Y%s+ZIB7$Xr!}OWsD}M4s(ey0jg)*~| zAqJnK*U++H^D{Fo8XIo1ximccbE_=;g~u~y{dVh%J5Pq1K+tlq``2hMOC?zmk?5#z zr|ME;1Q`LmPJ)p&-?2tuOR=HhYEi!NVQ4X!cl)<@b8i!lL>V7oxfWTF1J6tP5$1W*J-kExRf!ym1uN%ZD!gI`gw>4{` zo+!);2=gw@Q#gi4#ki#L=Z_x``);Rm#>n(+q^>67}F%AD6%lK2GsX2@3XSb zxxa#CLM|ld)&!mIqLh3+8uxMQ9l?R?{rlQa$1~v3GUJo1JjMzWbMYMCKW5}+Cjas; z#BGwFIn#?`C*mq4J!8uUp$_dmpwzN=8aBZ@TVpAS4{vNCZ$oD<%{kDuAdl^3G3j=` zooy*60@Z{<9V-ECQLIL^h4@SZvo%Wt{1g6eQDN0q_W8-Wn58 zoFp@Mf@BbyX^i&J6RWbNzm69Rz~B=*$jLg|+EiRplCk~!O_wB17sWs4O08$lzp}0m zv+qnrw?YX@%(L5{^HI(I?oYf}XAZ>9^LGxJafa1tF<$E6R_56q{YHufaIgh?%FIEA zq-)T67nCpKycOKBzk3Pd%|G6IQ=B0VL#m=$A@iYy?)tQAq)5*M?vplRdS;$1SeC!1 zN(D0lmJwrV7W=GdoFd};3E(klQT=^u zHw+|~V0Z%yFgI!6tN18;2$e$V?gHusmk!Nd;g_FGya{%qXRD!(_4EWyrPe)A?qmT( zB;h6O(W0?DkZoXWeAO)-Fa^iW#tj&k)7!)kUAVAHU{Zh9pFDqlQmM{~)2Byrjgpdz z@0_p2$7}m``vkNfy~01aCoJwsdXV%wZ@Ltg+g6<10V4g+FTu{cF~{xQMOVj^Hj*lZ z+SPOHw^z0)j(Hd?!+u_#sOp_8tqWs9Qw+-Cd9a-a_c&1L^h{luWeM;n!sy1C`Cl6w zrNzX`{AHSJL&C#nQoO=%w4k{1QGwi5&FXb{>bOEeh8->z>0_FqHr-dRx@dwx4OS=Y zPi&YuVB5AX~L_bH(Aq`aE#>fY_1w*A&D87~wfIc=Z1E4-RUX zhyDDJNersn2w;0VyX&`Yx2?HR0Vq8=Hv7dQ9Cq!HI-zeq#ov`?XXq&r#H->AZ zey0WCpKjT{ebJbW(=f@lbFwGs1)d5E3znI^!q6oVn#FeIN|sI(o{JATaRPtdLi`2m z*XzYUAJgLRG-VVMgF6d4Aua#jvSyERj75We)PB9_ipolFFE4yji}dwh{c+*UU=*`_ zu`@a`jN*{)?f`N7?g06kWBH?TZ{Nb>Cr^@M&N2w2pQ7II?Hg+)R;(L2Wcf$|wi?CG znmH531e!&urb?J$pf+`mms;?VEj3p$HdI{|*#gQY52d~OE)=MHb8x1S*QGAB6Q}?xwUm0Nf&=N4L#gLtLL2T9_*|87O%9sv1;}%84Bq)Bc5Y56e>OZQk9HA* z_^Ayl>?>uu?tVlB<)oW5@}#y2WJvs!=1#Qa06IH=LGYSzG}-1x{>zsV4rjl@>M=Rq zzh6I<&M?6`vF2ePiS3@63tNL2Wuk5|&XOGSiamVa2=SB+mfN6sxAB1rqC~FB4t+yV z0&8Hs&JJm!@{&a{<9py8VX`9}(ikWbed&_asCmV_d|^y$!m=wr)w=19@f@=Yt6*lo z2d`I%btf&Ze5n zJ3rMQW(yc-%DnBvC0n?__o?fQH*cOi+43cK{N$>&T98gegICATz`SbWAi2wm*%fH0 z`Q2?b)Pl!@Ste57)HJy@5 znLuw@S@ZRlT|I*b21l>Lt-k*L{=y1;c14B?G?czr0DH8*Q#0uq5N}bh0v^?vD~ZE+ zIC^Q3;?#G}XH7u4y?6mDE~;(W)PT{ncfg`R7HbaZGsNkN36rppl&pRbd*s0GKz2~S zT-u+F^LKVNuln};VOi`s_*+wzcZhc=#6cn4WaqA3LkABQ#>0QEQV|(0CWaHJy19A2^D{YC;_7UGx;ZMP!rfj}jBc*nz-TXJ@nmv-s9H zG2qeIlS8z?1F|=HkvyA10q~FQx^I50tSTxpUO$YzKlzDAp8q^C&-_ss$66a*T`VB? zb1N~MxhALI@}vU`T{lm(-+W>EMzQs#mn}RzS}5~jrSd3lRNWSc#w)k?zJIbU4DSAR_bjp)~ajw zX6Z}70TadNj~?~UvkOyJQvyicUhN=?2CZAX1!vazc^k_nm`JU_X>|nHYMksEKMemI zS5`JH`HdOe+yppZLXrfa>-9@lEZ`VcE?IZE3A$5XOABnI5tEI*+=?_E!dxrOLjZR8woTHcVidQ zdi9_*Ss1z!76Z5&Ofi@7VTtt5Q!6yd#@r*IE7K;=CE(4UkA-97GcbQ?ksmA9-@2 zHU|akbLaS(ECqCOss*3%9uqt7EtvD^0qrRY0e%%M7^KHX^d(YHg@%etOS9R%;%?YO zS~&(pFYW7UT33GI-i`*v`%SAs78tT$i7xDV(=5m;Wxve5UQ8;MhK60a(i;}I%u+`j zF20(v@v@iM?DE1DU%krm3akLu3MS&vNR*WOrc>xyG#k6>>m)R0##avn_-$Z`rYt&kR4{gb~p)Z>q90Uxaj$j?BWSCb{=5DRq;>SD0?*t$#F|=|q`1{SM+J zt>>^A&DDXACHmbK4TCEX>>^?kD->Qj1XStNkCtIal|aA+-Y`;-3}R(9J{g5~v#%=A zcAdpUVwL#M+rk6KA3ua;BHm)xe*~bzY}|obhQ!rQnlbnXZU9!<&jLH;Cd^@x3{MpV zaOHs!hn3I>V)EhnR+gC9jE>31M1Iaz&a$;nfv zRuDD}Mz9R&0p#TH8~>5qw80wSd&LzSmR~GgD$5p=u*6ymTie~WkB0|*m1C(Jy8=*j znl$Q|-W44<(B}T`v=ec=Pa4GZ#rAHK8dD+raG7T3?||LoSa!<57td$@d|zY?%oTr` zWQb^(`a*KX?Ac4tOwqY?U@oh`aYe$nT6}OF2la<5seY0SaH_7RX8er;`nl)z+sUK5 zcI{$?*q3o_G;MfMFr3+E?cL+#gzx|UmXEri5Z-fD>n=mDC-VM+$>^o|SQ`FT-(aLo ztSCe^W>y;s`fgCiFchExt$d%#k@0-^HtdPm&ZReHSZ0-F{a zA8*bnYS+%gkg^x^N?Ti-se{T`w96NGAA5dlYEyT<2QfHbpp+jqu zIX8RP=A4LCI+kq1AS-s_&n-00CKt0_*3fjxrN5`*FE6`t>{to00cR2RSC5gQ$Y~pv z;4@kOR+V>l6tNdWK@?Mu;rtJ0*)t|shs&0mFqy{AulCjr5}mQ|id8JKLry$h)J3OJ zD=Tfx3J#B$|K9NS7_6r!lSJ>{yN6yl_@$0QL17_d7&RoXus@P4MS?Q@dSl65TL(j2 zxa6rhV1zvlimHXsKk=p!JAZyLj}`sb{f6^&VMRx*>L1QpmTymHuZC*&ER+>h@J=&+ zs~N+j*SgBP%byS(gb%OTVPZjCT>NqS&{DZ^mUsx!h!jMD5%^0B&S{C5E~B^pzfMtg z?Y3-NP%0C(As3vE$X)AcHCF=e2zH`9v(uH0_?kd5Cfp-KLiTm&j9wm#gV9hL+bSH1 z)oS}h;vOONbVai#d6kr|wb}SEemx2y>u$X^dFD#~Cg%cJa~X&^+D5F)Bub@xrRWhy%`mY3bz?05Jue1V~Pa2fq6y93#7t>G&rEe%E1h}-abMWcG$ zQESYolb0A6@Rp3PaMdiQv!aif*)g0F#&V6sgoM$uvg)7x*wFh-L)}~xk7)t*PT}aZ zQAfyOj!hRe0%hRvfMDf3uCPCod4O-x@3&2oeg3TXmDqC=9eW;9{f!_K^0SB~u_2!=CAEy@9c#NyJ^1l z+aURuE!3kQq%Vzq5o88aVrcmx0wojL(Ox6oMCo31%n-2bjO|~ZQJF&j3Rflk#GN}= zoJM8N%y6!I^l!*>OCZ>;f{c&j+ zsfbLB8k&%M$pt7&x7|sKmC*1LMhIBa1R_S2arIn(fmI6Cx6GJ_rVMRblvV&Ci5vey zzojYGYWHQKG3jhK9Z;1W9(?J4+sKy0KgT(Hsj+>T8*l9NA*=xBR5KEga18%6@fx9s zH@_QiWwbn^73EWLg=GD>b>;F7Y7MoW4FszyqAGU+XI+}!-YI*8ffG6{;qtE1va++J z05tK48EYaLb+f;B%cJ8VivAHPO`{7#AJlDK_w>QGEnA!qOzQ&{z2*LX^4#24B_-~t zl1e93L=NY`ht*P?!_M#L5iZKQIO~}ErjYdTqeq|egn%wr_n*Id;^avP2*In)+Y;Z- zG1ZSQ1<8NJMBmlMew>I!rYXuiGh&sB7ZUekobX~ETvfMyjTKM6d-twx<7);eLuaPt zR8?gr_C*e!%V_KNXr+?{u$$qt)O5;bu?f+Yp;+QWB%{Vj3jE9gdFghpHLFJ{fuPi1izX-RovhF@J?`z9uCf@`a~CPkgGolr+u926)HUv(XAgi%<=n zTeg3Zd>3nT1X}fK`~Jj916Yp2V`4EJbp)qM)c*8)zCGPEZk_OkYhB(eB7LKQSZ8>q z{5*zCwVefPeVbMoD4qluFrOz-@|3{6-_#mWA$XMrWfPr!u&C&%d7Eip)~qRwn|uVH z>f^?ai8~)YESzns@48voh5|#oF6$N83G6BCbCb^uKefk=CYAkdf)k$c55o?{S8xos zR%h>L9Ffd|EU?xuKJLX!|EB{3WS%m%06Zg-)#lb*SH?^Ni&jAnC(NL^s5SFo87`zP zV6+d_bG?hp)r@WD5q5rmTUlR2gcNJ0Wb?8(l&#>l!=^Uc8nb76V_zw|Cr!VPlXR`j zXumm`qOAO6A^rY80$(f4DmR5C#>KXlr`)nMG!&-v0j6E&`jj?jXwvQz6?!I?=t_SP3DCjvu=U<%lI(JYv z&k@GO_ve?8)b~NGi=y1I#Dt76sebmAjg$PthA*4q6IafZi{8(1FBe3kCo*zrw0?~v z_m0!A(mjQbPCH%(@+U}@>eZl+k_ebG_gQcXsG?e1N0YG()s>8_Q=JAL&dEMsCeD10 z5L3km7={5Ry(h8f?J<@rTxGgo9phJA8h?B~G&H6$%IqMHlKWn*hq70*Lv(F?M}C@X zWkm%HlcD=w{-PW2rsI7_VS7>#ZS?g&^Mu8w^(>M%=Y?X7);>J_i;aWtQ`YM9zLcNG zBq!O{e)?1{dFnZ;B6Z6Cmg{Kt544rSg^(bQ-q(%z@s2?)UL3iG23zIz68K+SWNr7F zGf!^NciHiL_x|Iarz&2*=EXtDoWgnYR`qReqgE1D{&|j$DU;uNI|S)P@2D@U16iY5 zR_o&-QWTpe9ka*vGxR zbvi^A4s_u_t`b$8H?Im^+&saKJv)3czI>6iELUoW-|p5Fan*6-)<6Ign_`bx_y&=K z*XvMyYq07nCg~U>TMJYMqpEo7vvrZB2juIUZ(Co!*?;}F-dDgJS(A6n#YEVJj$`Ho z;!rp0I`$B^D7EC*sm4HCa25Tennx<{ZX|6XZ*6AL{aH1F1W`3~>^&v10QvFbNiGZ> zzsMC_K#aRA+EbJTgUz;=Ta)Z`aV9WrK}|f}U5aA0x4mxUHRU%0SY*c!WN#2_Q+$`T z9tGA3JLAA9wmaY5-*GI@lS2`BxXNqyZrGKGM>SEWyIu56UZlHTh=^FpzKgCof^Ui$F&ribFnjga&CyXn(t!Z1%zI-Bj2C4-qAQbL1`gU58#`ZItE-Bw|hpd+u3K& z*oaGDpB_5{m8q+xM(7(LE+l-!`0=bbCA%7?uVh&XaRvK~)C&A*U%HvOH-3lOc8~Tr zyeLo%GoY|!bjAGdDs)}6#Hez|yF*a=I7yXnjCz@})se+O5D^~jKh}lCe&8|lkDkxU z*Sq;iZo*mN6^6`0z=iN2##99@Fz?eV8=nXm199Db-MW{#J0dGZ@ko=Y@jh|t47H<& z+g#p1^&l5YhcB1{wc~~bEaF96P#~YT-hs$Pb{%r_1JVpH86X2d&AM_R1!O~RZ$8Z& zSK9w+SZPf+?99xrqc?;XIZ!0YT@2k;uWGxZc%P1rLr5RdjWl+}%MwV&e+UyK3Zlcp z&Wk6m)$JOIb&$6aZa{f1t9c%3>wFf*8#gmsl{YLKQkQH&mTe7oM)a!TIH{XSVG`T_jt& z{fcm4r8_O_@=w)@@Qy%U@b3I3@pc>=xJPy|X;k)2sc;%QSAmcyy@{H^pEs%M7=+fL zEQ7OmpxrIcOa}3inSZm!LB&Vj)&MiP0R$u5`NH)gBnl#piO#iWE}zSF^xz!}7`tP&H!r(_J|~QEBsSZ+)tN@`pwc48 z@hU3(SJdarapAlfb=`&y{rmQ1%$o^_eh2nR7-cjblV~d!1iG!{1zmOY2rpS% zq12^2t#9~Sm_>LEz?|N!L1+u~7z|ReWH|AK+JsLr1iUr|J_UuvACrYfotX3a^KKWx znAmZUZ=PhECU80bue@{rr!w#QxG8DZl-Z<0Vmn4da#qtQ+72B^=tMSe!pw2&sv}L zS*Gu9ffJZ2^UX>#q54VS?@L$%P;jeOvyEU)c!jrORhY-|c>MK;mO=?^1{_v&IPTm* z_t+`nj_XkpSh%=r;r+jrl;C1~#m+Z0{fK^$u|l@5eT>cb-^UzmxaFI>-}45;p0T{+ z{6kgj>EcfO;TfZC^2yz95F#C zD$fx-;7kFL+y^Z_*_WD&jO2*fkGtyQ8h9olNnYi!PZ1&og&Hpp4?i$eP*7}gow0v2 z{x0*vdGME(RvCFV!v04|L(oFug(9H6xAM6laT0U@ZlHi4*tR5)FLpn&&`Q*wG9{F3 zCd8ioha&J|N2Y|By1kE4*Gt$YBcr}%hSb&r^De{zY|k`0x(1#7x$#Fwwh@wv1$kcH zhx-HWZ<4!nn>gY#2oG#@oi|aj=FRKZy`+6-AFE7F_sh4!pg2nH=_d*D7b=!r0t+?s_x_?P)B!d4>9=I3(7Ly%}^Eqc1U3wP>PU@4y%?|p9jWZFIS<1|FZ8G10$&UgFL0zeFjDs-UFBmp;^+tVi9#a0=YQa zM!f>^%Ic(%tSE=!%=#a06g3>?PUvAHEv7o|c=3x22D6c|T=M&7Yv1x{Qc**9+BF!r z-fH`ZC<6C@x)1D5Vcu$0m9-jD7Hd+*2~eHa{5jQN@Zcz%V5{4jh15D|heZ|Qp z=k#Ly&il`!B)$FWfdmL7A_J2Zhs0C}@DpvL-22elA0qx?d2jCqfL-z>gB2@_c0lRw zc=Kk-?~`Z;`%ho*<&Hbw8;KHbF&XFsd9suj2G8HV4dd){Uxl3osG2H_nQZl7*kFW< zaXA>(7U}K%)Zt6=$`EQG`XHlzb9Ckw3fQlA`((YNl68d+_y=7`Kyjk-6s$^d^XVO z7k4r`bLY+5d0%sh+YkmNAN#Y^H&;+@FKn!-UF;k}kfbruXWGC8ezqtz55y)RXAguG#kzbtY%G+Q8x)1PmzrQi}J{hnJydwQxWDbd;WU~$6e$*J*wYDKtC&`ON1 z_9qB0Qh8v%AvpMNc)Sdh?p~myUJgZY_&aIGvuEcCIH>1@F4GOxc$l56 ztg0RNk0A3}$a~e+-X%3P#)gK&hP#N$a?YyKC}MT5FJl0p(Hfo1qwRHbp7XY>ro5{_ zM4M`L+;5B2bvjx|lTnG@bYtG|SkJuzzXf>5im$#IVUg-05cn}Vu@M##=S@B*kUqjh zSiRf(h#Uh4oVkK!DyNaw{u{Ya%0}j)ABFMH?9r@a<}XGuEm2U9LD^FE+)nSy>j!;Y zs3Y{KA(sO|tlD(Z=J&D?ro?^afR z`0KAVmkzdI72dr|OHTed@BIV1IE$G3FgmA~TGpfq6XY}Fir~IrR)F1Olz+?2+wC)d z{-_CI$ctB1P<9FEe7c`;of>Lti}ww;q2t%XHgt-c-COYn$qzjRk2 zvTDmqD#n8lj_&Dfk*bjy&=S6v4~&@_c6G`UJpvo4+Dv^TBf2M&n|X?PR&gARK%?la4t;zNHuZoj-OCYP|alK9Q_LgQjTSl8EAs z?R#O1OfGLm*^#KW){A36LkP`OlD46?lxc{yVq#;1c8oBhmxF%>%S+m(orkW#uvfLM zF0vC2^nkFbU6{X%zSlIagnTiLkF{=y(#qlMu#7Mps%@fwzNB(9e(V?}rO1D4!;mL< zr<@MV`o88gm2%8D?rD5V_3FJz;$(wAmdX*B}sLy_O!xJBLsXk4rA+ z!lNF?z~y6vIuXeUK-_H}|mfe^TUY_ zl6!{J8b;EE1IfulcgOQ?b`4w$w7TCh$ePfsF!XQa`Ftn!3GV3fdR@AtoNXGwC(ICN zYiT(S9=^3kTOvxb*HNVBgd9yLB`t2g<9#BWAz{Al*BR#2eUe-E+-MdC@3OB+eYC8# zUaxnYS;7YnJU|Np(#MK`fEgLJo>dpFWMyIAXu<2|b|Leuf2lzlbMxkLT6En?r%%r? z`n{H_F=eOg{e4$33Q}q0;m|WseGNIOgjRuWCidcpVQ@S!QbQ0Ny3m(CfStrDvZR~W zt*dQlSaoMPt;%GXIGU~OfA4t<86A!!T@NowK(+q_TEDCw^lk3K#V{kN(GbhC&*(xU z9-O3|=CoBqBhA2FnClJMIRAsEB|@4?H9U7mUl?a5DAhlCay2eZBD?I4 ztEXo-e*#AOnK`z5OMSY&d~rT(ewwN!v4v65<(yq?Oz??7%)!a#Z2i?lc^FX%19+&R z@dL;aH!?Z(T=!tON5j)j3(Ud0pH@MMBf zhF%Bx7b}o8Xigfa$H zj8&K4%+gmow+qbibj|f5ugLe-d=^Jf&;7&Gg z&BzEfQS=XBu%epC&oQV7l}AxEr?@HzViSu#p&JwaNMYzj#q47!(2)L?|L(g5XKO5> zM}tkO5rBxZYLke(I`0b;1bG&S3_rE17tCy7C`Mp?767b~+>WE;DWe4gP}zhlL8yf) zb{8<<SZWvra3*uxap zQH-e|B?3J!FK_Q1iXDsPqudcyPfRTJzd$@j*u1OT+r7_EZ{H+xr2kuz7J|jg6RCQ+ zdw6^z!;6t8gXOx_({>19KZHUR%bC3#%oz@n44Y5hnP*Dk?A`MVH(=hm7riXqH z{aB4zg%n1TrRmRVJ*A-t2;uaYE+?qLQ^KzpwU~Kl*qZcohFEE9o$m~{*P0s~Y@rYZ zXELkP;Odh2O22p&Gi2?HHSY$`Qt8UjL9l_}pwqbs>&`Lq9O*Wr|CKy*iH`5wIjCr$ zLS**LMT;JBYEbzjGkRimS~Z+IQsZPm!O5$7nLs`iLH7}gitH@~C8hdHKRa63p1O}A z6Q=K?o!8Rh8yGkzcG=l$40rt& z8U~?+y2aHYFx7yjFe!ghhz%9MxPz&SA@d$}DZkV}Zt!S5uQMl{^B z410xs2o9e@0oK)P_}OjJ(=XsWA4Jcar)~P_fKkMGXA&GjMH{dy42v%BQ`ddEhe`?o zypXzwS?=}L_7|gKGOP#4e1b}lyQruD6@}At7HDziUP*~CU!1xla=9^34R@4Entayf z0K!MSI7oFatND$?L3q~qPAHHu3MQg5%LUC}b%d?yspy{KraL~GNK4twe>ND41d zeJ0->IdTMQwPlKPy&3+37Ms8hm5q~^P>e>iod#`e8I8#tM=r7}oHEhWy8gpUaSI$< zNCPKrE*v}&r)=Vjn-XXx56+EsD3^#HGnQMdp%G4|SlC+2)j&a*O*WSmybHdSFT1ql z)qHCb*fcz2S6If%N+fUZG8=E!tp6a-=inf>OWN}Bi;iOZO0&HSts=AH1>HNKFC7>V zFL+6Cw+Q?pE^w4Q#*h4QM)1cR#;#znseco?dl#J8)-hbg)bF>eA<#a_+?4&eQzGkz zQYn@f9oFFHvOxvcf{{Za$ff7s#jtFbp8KJ4UlWr)cYUSx)@qFKYyA*yunosMMSYG$ z43#t8bs@7_UZ+gYUxu;cLcoP|G=>sb$Au|8b&4s^0p^}>rnyfs&-|Ehs}*5i6D_S4 z5)HWHK(CWJ!zl0yw}p(ZmW{}6kM#9Nq4o;aKD%K?a)mGR3&VTfT*Q1A=H`}8i_O{M zH^R#2X}@knc4v6vhCjm(h1#T5`#aj(KfSzRoSvS;sw*-p`6cw#a?0J#MNgB}tVf%Y zEsRZd>wA%&5`(Ki_ZRIM^kGpnZ2Du`zdv8cFM~9g1a2d`cGe!;5KwXYQf`XTuh z43Sh4Ay!@VgTO+ecI#C>%o5@sE~lrfOB^f>N|tk`X@YB4pTIsPEwVIw{@Ya#iMuJ@ z0^W9tq~eGHflAqD#3zQR(X12!9Mv?dUjoiMJ&DMZ!YGzp>L;0>aRk z`1sYfuX-2>%Y3xIK~?NdGI4gYS|ZzJ2o6Cs!eOF-a^~fZmX7ssz8QS}Ss0{d98*#D z`r8$feoQRfVqvQ@tetD*$cDT=SvX*T<+QNw(=^{*s#l0SW=KVX(EB>cIY=y?u55Oc zhTH%6!x}Lp%6X3;Kc-i2`^?Oi8Qb5F5Xk~e^;D-4!ypR(FhX_eN+?Cl;S&;0G*{uw zE}iFFHxhXm7_@WDAP}rHm@e1B35A4A`w8t+84SfaoY3UiMGl7x+(Tl9XptFf&BRzn zpK6FSQU1OUD-U))Wy*D7e5o7a@8pW1!vkK7K20j~GbNKkQ$s!3O%MgPo`m}TlU^&k zAQlVxiLUpWhK7OwI=ow>v-74$PO1tGSZH zY!zu;CGcWkoKPzQ*M6&LD+=_KYy0?xHT!PpP~e%}EVYdiGbQrW1f^641JG^YX?%RP z=~^|k?Pbac918Fq{iec9TUarJz(=EK7QVfg zz5|L*ZqAtXbLZwR;P8>PU|Uw>!}NTr!nRm`AZ$ z1AHaJBE+6%xeI@Nu^zpR&A?fBhWQ0isl1j{mv{B2_i=Q1X*)_+;04Ls!p&qmE%-t1 zRIseyo6ab^bVf*h73L+@AsYe@m^D3dLPtx4NKt*qw+^NpSMUOyv{H)vPoAjF!_?** z5#eyKne+34y7JMf;lFiwT~l>(r1pXY2pgVsSy8*wsrxj%?@DdiwOz5`n@6u_8041b zIO)I=!k7zD-V?eWus9rPU=6x?mMPV(A1*Ck4Wjt|?ZbTKrp`a%EtBx`(}m6 zdnXlsj2<#%h%i=XW4j{2GR0!xq2NLMX@V)vGeadP$RFf<03X);F65nEPBE4ic}w8} zItK(WHCQ$7WO}-7Cf;nkNlo`z^_*aLXTwowjXsOnO1G9b^69VRG%y$`{U);-?Q2`fG=%r2C-?Lym$6W+19p;zRaEdusPN?m zms6ew{k~=^$H&Weg!5c4XOwesaD^>BGRR_ZV7$vI7MnA{4BfK;dZHzB)!|zaed-KL$U$C-o8!0^&2c2 z{2e-m@2wpgcdj=CJQ@;0vqLrQVBX#uslvUC+VQa9+_7WN_$+bG3hP0W;E}TR0Yrk< z8d*7-uHD^7Bu(YD-O*Fr#X_=|APbJs0lK#;0Mnb8N5LyCpfC_}F_)s=gR_`|v;wD#%xe_rl8;onI zxOHplmQVTy21CZ~&bH}2IQt}ah5f|rU)*V`i(?nR@T~u5rjV^BtRqPW9s%fzcW29( zJj;aAPPTs|O>hMT$jgJ^L_Ea+bEVzVj{*EioC3PAT&S14{8#?|=ZGt#*(>nbh%WJ% z_%4cKyS7ex|2Ny=*(+EBV18ga$u?lQg4th~Y&@ii%)F<2b4aU_C(_zvX=!G5zo8-1 zRsUdzyvR;w(1KCiGb8Kqj)?D5-5|q@S&d!nq3e6y7xzjz>??d{*dz!4kM?%WFEX~G zW0Sd`a`m?;VX$*a0pikGBybtlRC(8#lA&q94N$xQ(}G|5 zo=>{!D?c9~vfDOlRGgapsjrC+W8-&}G5A$-{NOyK5rZzYJDIQPc=d{m1Yv&z%g*{k zsY3LcPSLM;MEX**lV`hkMqCRafZ4MDShE0%pO{Saw^OPa_S?GtGLZoncWyLU8@VNB z=n!jhm$boWXRm^d08<^s8YB9%c;8S^wpXP1KAG)qLOA}TFq67t7DaODUtia z{X%t|+yFglTzonwXHG`#?cir821bZ&MUpiRjbs9FEWQ1wdo^s*Y6Mp>A>o#G--S+* zLKc5dNAWbxaW!pFs++dP5?;`tMJ|@NoO{|Z6$)N^d3a!d0s+jrNb4+?IWu!!Pg=Rc z_$1m=#KAIbCz)-=Hx@3Jkzps(o-6uBMn)VdBlFMyk&)HprwB3su@&(_iR^#j$NoE? l>i@WY|9>9-eSgo?`LpLfS)={Fi~TG!!`?-D&dw*|e*l48w;lih literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index 69de0c7..b12826a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,7 @@ build-backend = "hatchling.build" requires = ["hatchling", "hatch-vcs"] [project] -authors = [ - {name = "Remi Gau", email = "remi.gau@gmail.com"} -] +authors = [{name = "Remi Gau", email = "remi.gau@gmail.com"}] classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Science/Research", @@ -19,16 +17,16 @@ classifiers = [ "Topic :: Scientific/Engineering :: Image Processing" ] dependencies = [ + "antspyx<0.5", "attrs", - "chevron>=0.14.0", "deepmreye>=0.2.1", + "jinja2", "kaleido", + "keras<3.0.0", "pooch>=1.6.0", "pybids", - "antspyx<0.5", "tqdm", "tomli; python_version < '3.11'", - "keras<3.0.0", "rich_argparse" ] description = "bids app using deepMReye to decode eye motion for fMRI time series data" @@ -62,21 +60,8 @@ doc = [ "sphinxcontrib-bibtex" ] docs = ["bidsmreye[doc]"] -style = [ - "black", - "codespell", - "flake8", - "flake8-docstrings", - "mypy", - 'types-all', - 'pandas-stubs', - "pre-commit", - "sourcery" -] -test = [ - "pytest", - "pytest-cov" -] +style = ["pre-commit", "sourcery"] +test = ["pytest", "pytest-cov"] tests = ["bidsmreye[test]"] [project.scripts] @@ -105,9 +90,7 @@ packages = ["bidsmreye"] source = "vcs" [tool.importlinter] -ignore_imports = [ - "bidsmreye._version" -] +ignore_imports = ["bidsmreye._version"] root_package = "bidsmreye" [[tool.importlinter.contracts]] @@ -120,9 +103,10 @@ layers = [ "visualize", "bids_utils", "methods", + "report", "utils", "configuration", - "logging", + "logger", "defaults" ] name = "Layered architecture" @@ -137,24 +121,18 @@ skip_gitignore = true [tool.mypy] check_untyped_defs = true disallow_any_generics = true -disallow_incomplete_defs = true -disallow_untyped_defs = true +disallow_incomplete_defs = false +disallow_untyped_defs = false # enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] exclude = ['tests/'] no_implicit_optional = true -plugins = [ - "numpy.typing.mypy_plugin", - "pydantic.mypy" -] +plugins = ["numpy.typing.mypy_plugin", "pydantic.mypy"] warn_redundant_casts = true warn_unused_ignores = true [[tool.mypy.overrides]] ignore_errors = true -module = [ - 'bids.*', - "bidsmreye._version" -] +module = ['bids.*', "bidsmreye._version"] [[tool.mypy.overrides]] ignore_missing_imports = true diff --git a/requirements.txt b/requirements.txt index 68ae0d1..75b68d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -56,8 +56,6 @@ charset-normalizer==3.3.2 # via requests chart-studio==1.1.0 # via antspyx -chevron==0.14.0 - # via bidsmreye (pyproject.toml) click==8.1.7 # via pybids comm==0.2.2 @@ -145,6 +143,7 @@ jedi==0.19.1 # via ipython jinja2==3.1.4 # via + # bidsmreye (pyproject.toml) # jupyter-server # jupyterlab # jupyterlab-server diff --git a/tests/test_report.py b/tests/test_report.py new file mode 100644 index 0000000..59853c9 --- /dev/null +++ b/tests/test_report.py @@ -0,0 +1,16 @@ +import pathlib + +from bidsmreye.report import generate_report + + +def test_generate_report(tmp_path): + + output_dir = pathlib.Path() / "outputs" + output_dir = tmp_path + + (output_dir / "sub-01").mkdir(parents=True) + generate_report(output_dir=output_dir, subject_label="01", action="prepare") + generate_report(output_dir=output_dir, subject_label="01", action="generalize") + + assert (output_dir / "sub-01" / "sub-01_prepare.html").exists() + assert (output_dir / "sub-01" / "sub-01_generalize.html").exists()