From e24955036c8179e9ba17823c676ad1f57b30b790 Mon Sep 17 00:00:00 2001 From: zyallday Date: Thu, 26 Feb 2026 21:03:13 +0800 Subject: [PATCH 1/3] add support for onelap --- fit_file_faker/app_registry.py | 37 ++++++++++++++++++++++++++++++++++ fit_file_faker/config.py | 2 ++ fit_file_faker/fit_editor.py | 22 +++++++++++--------- 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/fit_file_faker/app_registry.py b/fit_file_faker/app_registry.py index 1368f73..8f45a41 100644 --- a/fit_file_faker/app_registry.py +++ b/fit_file_faker/app_registry.py @@ -270,6 +270,42 @@ def validate_path(self, path: Path) -> bool: return path.exists() and path.is_dir() +class OnelapDetector(AppDetector): + """Onelap (顽鹿运动) directory detector.""" + + def get_display_name(self) -> str: + """Get human-readable app name.""" + return "Onelap (顽鹿运动)" + + def get_short_name(self) -> str: + """Get short app name for compact display.""" + return "Onelap" + + def get_default_path(self) -> Path | None: + """Detect Onelap FIT files directory. + + Onelap stores FIT files in specific locations: + - macOS: ~/Documents/Onelap/Activity/ + - Windows: ~/Documents/Onelap/Activity/ + + Note: The actual path might vary slightly depending on version. + """ + base = Path.home() / "Documents" / "Onelap" / "Activity" + if base.exists(): + return base + + # Fallback for older versions or different locales + alternate = Path.home() / "Documents" / "顽鹿运动" / "Activity" + if alternate.exists(): + return alternate + + return None + + def validate_path(self, path: Path) -> bool: + """Check if path looks like Onelap directory.""" + return path.exists() and path.is_dir() + + class CustomDetector(AppDetector): """Custom/manual path specification detector.""" @@ -301,6 +337,7 @@ def validate_path(self, path: Path) -> bool: AppType.TP_VIRTUAL: TPVDetector, AppType.ZWIFT: ZwiftDetector, AppType.MYWHOOSH: MyWhooshDetector, + AppType.ONELAP: OnelapDetector, AppType.CUSTOM: CustomDetector, } diff --git a/fit_file_faker/config.py b/fit_file_faker/config.py index 003536c..5803e26 100644 --- a/fit_file_faker/config.py +++ b/fit_file_faker/config.py @@ -613,6 +613,7 @@ class AppType(Enum): TP_VIRTUAL = "tp_virtual" ZWIFT = "zwift" MYWHOOSH = "mywhoosh" + ONELAP = "onelap" CUSTOM = "custom" @@ -1673,6 +1674,7 @@ def create_profile_wizard(self) -> Profile | None: questionary.Choice("TrainingPeaks Virtual", AppType.TP_VIRTUAL), questionary.Choice("Zwift", AppType.ZWIFT), questionary.Choice("MyWhoosh", AppType.MYWHOOSH), + questionary.Choice("Onelap (顽鹿运动)", AppType.ONELAP), questionary.Choice("Custom (manual path)", AppType.CUSTOM), ] diff --git a/fit_file_faker/fit_editor.py b/fit_file_faker/fit_editor.py index e38a42c..34e27d1 100644 --- a/fit_file_faker/fit_editor.py +++ b/fit_file_faker/fit_editor.py @@ -209,8 +209,7 @@ def rewrite_file_id_message( Note: The product_name field is intentionally not copied as Garmin devices typically don't set this field. Only files from supported manufacturers - (`DEVELOPMENT`, `ZWIFT`, `WAHOO_FITNESS`, `PEAKSWARE`, `HAMMERHEAD`, `COROS`, - `MYWHOOSH`) are modified; others are returned unchanged. + `MYWHOOSH` (`331`), and `ONELAP` (`307`). """ dt = datetime.fromtimestamp(m.time_created / 1000.0) # type: ignore _logger.info(f'Activity timestamp is "{dt.isoformat()}"') @@ -260,10 +259,8 @@ def _should_modify_manufacturer(self, manufacturer: int | None) -> bool: True if the manufacturer is from a supported platform and should be modified, False otherwise. - Note: - Supported manufacturers include: `DEVELOPMENT` (TrainingPeaks Virtual), - `ZWIFT`, `WAHOO_FITNESS`, `PEAKSWARE`, `HAMMERHEAD`, `COROS`, and - `MYWHOOSH` (`331`). + `ZWIFT`, `WAHOO_FITNESS`, `PEAKSWARE`, `HAMMERHEAD`, `COROS`, `MYWHOOSH` (`331`), + and `ONELAP` (`307`). """ if manufacturer is None: return False @@ -275,6 +272,7 @@ def _should_modify_manufacturer(self, manufacturer: int | None) -> bool: Manufacturer.HAMMERHEAD.value, Manufacturer.COROS.value, 331, # MYWHOOSH is unknown to fit_tools + 307, # ONELAP ] def _should_modify_device_info(self, manufacturer: int | None) -> bool: @@ -306,6 +304,7 @@ def _should_modify_device_info(self, manufacturer: int | None) -> bool: Manufacturer.HAMMERHEAD.value, Manufacturer.COROS.value, 331, # MYWHOOSH is unknown to fit_tools + 307, # ONELAP ] def strip_unknown_fields(self, fit_file: FitFile) -> None: @@ -492,6 +491,11 @@ def edit_fit( # Skip any existing file creator message continue + # Software message (35) - skip to remove original software info + if message.global_id == 35: + _logger.debug(f"Skipping Software message at record {i}") + continue + # Change device info messages if message.global_id == DeviceInfoMessage.ID: if isinstance(message, DeviceInfoMessage): @@ -522,11 +526,11 @@ def edit_fit( target_device = GarminProduct.EDGE_830.value # have not seen this set explicitly in testing, but probable good to set regardless - if message.garmin_product: # pragma: no cover + if message.garmin_product is not None: # pragma: no cover message.garmin_product = target_device - if message.product: + if message.product is not None: message.product = target_device # type: ignore - if message.manufacturer: + if message.manufacturer is not None: message.manufacturer = target_manufacturer message.product_name = "" self.print_message(f" New Record: {i}", message) From 274d3dcf26d31188e74beac6142e872903b989ff Mon Sep 17 00:00:00 2001 From: zyallday Date: Mon, 27 Apr 2026 19:39:47 +0800 Subject: [PATCH 2/3] fix: address maintainer feedback for onelap support --- fit_file_faker/app_registry.py | 5 +-- fit_file_faker/config.py | 2 +- fit_file_faker/fit_editor.py | 24 ++++++++--- tests/conftest.py | 13 +++++- tests/files/onelap_20260427.fit | Bin 0 -> 377910 bytes tests/test_app_registry.py | 68 ++++++++++++++++++++++++++++++-- tests/test_fit_editor.py | 22 ++++++++++- 7 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 tests/files/onelap_20260427.fit diff --git a/fit_file_faker/app_registry.py b/fit_file_faker/app_registry.py index 8f45a41..868103c 100644 --- a/fit_file_faker/app_registry.py +++ b/fit_file_faker/app_registry.py @@ -284,9 +284,8 @@ def get_short_name(self) -> str: def get_default_path(self) -> Path | None: """Detect Onelap FIT files directory. - Onelap stores FIT files in specific locations: - - macOS: ~/Documents/Onelap/Activity/ - - Windows: ~/Documents/Onelap/Activity/ + Onelap stores FIT files in the same relative path on both macOS and Windows: + - ~/Documents/Onelap/Activity/ Note: The actual path might vary slightly depending on version. """ diff --git a/fit_file_faker/config.py b/fit_file_faker/config.py index 5803e26..8994a49 100644 --- a/fit_file_faker/config.py +++ b/fit_file_faker/config.py @@ -1674,7 +1674,7 @@ def create_profile_wizard(self) -> Profile | None: questionary.Choice("TrainingPeaks Virtual", AppType.TP_VIRTUAL), questionary.Choice("Zwift", AppType.ZWIFT), questionary.Choice("MyWhoosh", AppType.MYWHOOSH), - questionary.Choice("Onelap (顽鹿运动)", AppType.ONELAP), + questionary.Choice("Onelap", AppType.ONELAP), questionary.Choice("Custom (manual path)", AppType.CUSTOM), ] diff --git a/fit_file_faker/fit_editor.py b/fit_file_faker/fit_editor.py index 34e27d1..101fa3f 100644 --- a/fit_file_faker/fit_editor.py +++ b/fit_file_faker/fit_editor.py @@ -36,6 +36,9 @@ from fit_file_faker.vendor.fit_tool.profile.messages.file_id_message import ( FileIdMessage, ) +from fit_file_faker.vendor.fit_tool.profile.messages.software_message import ( + SoftwareMessage, +) from fit_file_faker.vendor.fit_tool.profile.profile_type import ( GarminProduct, Manufacturer, @@ -209,7 +212,8 @@ def rewrite_file_id_message( Note: The product_name field is intentionally not copied as Garmin devices typically don't set this field. Only files from supported manufacturers - `MYWHOOSH` (`331`), and `ONELAP` (`307`). + (`DEVELOPMENT`, `ZWIFT`, `WAHOO_FITNESS`, `PEAKSWARE`, `HAMMERHEAD`, `COROS`, + `MYWHOOSH` (`331`), and `ONELAP` (`307`)) are modified; others are returned unchanged. """ dt = datetime.fromtimestamp(m.time_created / 1000.0) # type: ignore _logger.info(f'Activity timestamp is "{dt.isoformat()}"') @@ -259,6 +263,8 @@ def _should_modify_manufacturer(self, manufacturer: int | None) -> bool: True if the manufacturer is from a supported platform and should be modified, False otherwise. + Note: + Supported manufacturers include: `DEVELOPMENT` (TrainingPeaks Virtual), `ZWIFT`, `WAHOO_FITNESS`, `PEAKSWARE`, `HAMMERHEAD`, `COROS`, `MYWHOOSH` (`331`), and `ONELAP` (`307`). """ @@ -272,7 +278,7 @@ def _should_modify_manufacturer(self, manufacturer: int | None) -> bool: Manufacturer.HAMMERHEAD.value, Manufacturer.COROS.value, 331, # MYWHOOSH is unknown to fit_tools - 307, # ONELAP + Manufacturer.ONELAP.value, ] def _should_modify_device_info(self, manufacturer: int | None) -> bool: @@ -304,7 +310,7 @@ def _should_modify_device_info(self, manufacturer: int | None) -> bool: Manufacturer.HAMMERHEAD.value, Manufacturer.COROS.value, 331, # MYWHOOSH is unknown to fit_tools - 307, # ONELAP + Manufacturer.ONELAP.value, ] def strip_unknown_fields(self, fit_file: FitFile) -> None: @@ -456,6 +462,14 @@ def edit_fit( # Collect Activity messages to write at the end (fixes COROS file ordering) activity_messages = [] + + # Pre-scan for source manufacturer to handle platform-specific rules (like skipping Onelap software messages) + is_onelap = False + for record in fit_file.records: + if record.message.global_id == FileIdMessage.ID and isinstance(record.message, FileIdMessage): + if record.message.manufacturer == Manufacturer.ONELAP.value: + is_onelap = True + break # Loop through records, find the ones we need to change, and modify the values for i, record in enumerate(fit_file.records): @@ -491,8 +505,8 @@ def edit_fit( # Skip any existing file creator message continue - # Software message (35) - skip to remove original software info - if message.global_id == 35: + # Software message - skip to remove original software info + if message.global_id == SoftwareMessage.ID and is_onelap: _logger.debug(f"Skipping Software message at record {i}") continue diff --git a/tests/conftest.py b/tests/conftest.py index 564b23b..e482035 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -87,6 +87,11 @@ def mywhoosh_fit_file(test_files_dir): """Return path to MyWhoosh test FIT file.""" return test_files_dir / "mywhoosh_20260111.fit" +@pytest.fixture(scope="module") +def onelap_fit_file(test_files_dir): + """Return path to Onelap test FIT file.""" + return test_files_dir / "onelap_20260427.fit" + @pytest.fixture(scope="module") def karoo_fit_file(test_files_dir): @@ -114,13 +119,14 @@ def zwift_non_utf8_fit_file(test_files_dir): @pytest.fixture(scope="module") def all_test_fit_files( - tpv_fit_file, zwift_fit_file, mywhoosh_fit_file, karoo_fit_file, coros_fit_file + tpv_fit_file, zwift_fit_file, mywhoosh_fit_file, onelap_fit_file, karoo_fit_file, coros_fit_file ): """Return all test FIT files.""" return [ tpv_fit_file, zwift_fit_file, mywhoosh_fit_file, + onelap_fit_file, karoo_fit_file, coros_fit_file, ] @@ -170,6 +176,11 @@ def mywhoosh_fit_parsed(mywhoosh_fit_file): """Return parsed MyWhoosh FIT file.""" return FitFile.from_file(str(mywhoosh_fit_file)) +@pytest.fixture +def onelap_fit_parsed(onelap_fit_file): + """Return parsed Onelap FIT file.""" + return FitFile.from_file(str(onelap_fit_file)) + @pytest.fixture def karoo_fit_parsed(karoo_fit_file): diff --git a/tests/files/onelap_20260427.fit b/tests/files/onelap_20260427.fit new file mode 100644 index 0000000000000000000000000000000000000000..3b90c93b88c1f567d69f4b55dae53d0fad40623a GIT binary patch literal 377910 zcmd4aca#)W*FXB2VPJ+LH95^dPXq;Z1Qi9v0R%+SkQ4-A5JW&ilMW~dP0rBdAV?D= zhY=+@U0qES5gA1gkT9Sik{m@OD007td7ia?@9);$``+8@x7PI!m#>enANStpoI0th z3nzMFs_x5gJlUv8+cX1zMCXf|78Nxirc`u9527X@NzqXgDwc}Ie}2@XB$<)pN=Hu3`>(-&N}-6z5)nqp zi0D!gW|;`1JYqlPD)v<5sX{;hFaPHM@s~P3HU4KN;FUItKJs*;q9ez}JSs;-gb@`H zSunyhBg{e(X5k34bc9hRA~M4;N*Ss6=Z&cG5mDpwM@=XcHNIrjgt({)O`;~Wc$Dn| zkN!OcBge)@j&nzjjgK5xC30NV$Z?NFj;k3t?wQDO&qa=_6*=zt$Z;=3j;kFxuFj(Z zfs4sk#Egh691&S0BBFSHgcn&UBC>Ks!LmdB6Z>ZUqxaJaBfBa|Bdj`f3bhot~*A%EcJ|G7!Mz!#r$u7M43DDy=gr9 z`~SE9?Z5x%gxZh(%ISZ{zjYw;lJg%$w{HBi|F?fK%50lIJY^7+^x3=aco7SlFQL2^ z-AagMJR8ek9SaoW^(=LnR35~17L;*yxQq>f;+q5IjVyHq{mVIH6U=UcGUjDYQ95ID zk+Jq!>hFA%vbfOelG1=vhC*@ALo6M#)C>73Wo==p`ePb#$}lL#e8lo*mKsxlQW_U| zS&F3zr@Rj(7mCp_OI?emly{2`l9Z;L@&S~b1&F0nmg-QDQhqM_lB6`_l;Jv-g@~nd zmion{l$x%ElJXj-jDV857_oH8QgaJa%G1S13(8owE+0bieuP-MW~q##lya?jFF_f{ zDkF8qmLQh5vs9{!QpS|%DJidV85^Zz`53Wu%Tlfql(MvBS3w!W#^Tj=@gtV*S*k=S zN||3u8ZAe&EqSz7Rv?xhSt_jzrJOH4UWjFMb1q|Jpro%tEbnHiLFFjr?J~^-WlRfB z84Ja?8nN`uQlD0!laZqyBA{Hor$5Kjq*^YwZWvf44XKVwMURmmm%9OIU zY)eUD`~CzS%O+&3cb3`{PbrnkjS5Sd2*r~L<-IJGl0+#@%e4rlitJ8zEyO*#0sdipxeUQ2wkzDLG;rccLV?Q$H$x?S}QOb|yq`p6bReVs=4kDHTS?Yt@lybD3G$!-P zEGX_nPzGhG+!rb3>vFAyjD5h4$+LCF4kKejvedwrDdm8evEgjS=0Nd(4P_|iWA!M- zF4sbcg;(b4jC}*;gDlnk6-wD%uDPJ_b(sgndlbruEOovSt)vUehit~?L&?mBGBQgw zZ$c?M#f1U)!S?+nI+ha{VWwxP zp6w~+V)^j;%TlfUfLMH4>i(OQQn7-RF}_!R48?T{vCPg={Mw`lI|F5LmfH9Z zrBtmrHY{ZYl+3eGmZFXIq?CRY2MP*5!mNbiKZiEv$GY-8N;zL~vZSyxrBzxv4`pSR z`nfNqydEpf`A4y{zE8B017$T<79L7j7duRdg`dH$hT^+`x~$7mpJ8|1xg{HMlO%e(ah|Z0jZl2QAY)%- zsdvXyN{LERUA$~8o1plwKnZ54K9eY=R;8g*EbNZrW+?Wr$XF;#O_)k4Z7PX%VRw7B z=vb~ImiP)heogQZy5-JVP+>DQoqiT&hRl+vwIUqKng#`3Ao7!>c}EH!N|rF5v& zKP+V{lpL*mlchddKq(C?^^+7fW1m6E)*1UQOKn(8DOJRbjbxS2p=9V-j%TTDODV+^ zGd7%6w&_^V#=Ix9)M7uS+=zQm$k+&0*$yQa{lz$gx~!y>FXOrk3Lnc3D27(fXQ_3o zDP>jMJA(278_Q0uV1)5rz*B4;rC4#ZB!%rSyR@Q5%S%~m@OSW^GdO>&&nVaFXoosJ6Y--i&D0V z$_MNm#)guKd9wF@mg*a#lv#1ZrC3-c1jW$G1Dqu8qm*HB()kO&uH2(DrdJjb0oClF zvl4A~jhiaO!ms%DLP>?<%@zrmP-Tu#N`k12VcXb#C?1_L zGoZ?5J1a5f@3GRHe=MsUfa22CFA`8UzN3_^SSe%USmhuTL-!X~K%F>FDN|w>2z43B zDqliz>$Oh+5|pn6}XlzSBb{>m4NDU(^)BEK2<^5yWsC#d1U)&+fwk^2+AZjmJ?9Y?w}<<8&HQHP)dXHi^5WVfa1N2 zSZW2-i@4Y8v(hlE*|^H6-c z?>7yom8B?UZTWdpENmOgf#SIZrFlRlmUUK2n_rio9hPzdO6u>Ze#?Lg;zGJp#!8z; z1!)e$SN|du`zB&Z52!a{DW!G=X-uBXX6z>@uIq@UbwCxWOew=ENTcNxwvGJ^B~9=4 zK-n5kDZvU0g}U&{B`E27)(7RyBuXh*QL6qFcFunpiaxXOwhE~7RVby6cw#k`Repi; zXuS{R_+ylEsiL%coyICxp!oC|AKKWK>XdRiHaz85oiUuf8c;rZic+2tcdn+gtE8)1 z(Wlkul^@oil*w_@Ng}UYgW|?50&k0en({2AoQ<0(wB)Jmj^cGFxwu23l~K=A%Hx%! z72i~Lck~7n|B<|meN=~1Mpu$n7L(XmZbGrY%4=i3y3R@|^UF%oZqGzk`3*|?mr&66 zKdDD4#Vd;no3UId-UCqDp;x{_DcvedJ0ufX<##A)`Yv3DfV$e4Qr1_N&i8rc4;{-s zD4hbTW>aUSqEudz$ zqLfW;X>ucmcO95bu7IDs!@ANIT|lzY*IRxu{%%< z9ZSD}vO7{rs|4w}&&P5XN)GOh8Uq8WY8PjvxVa-i8k6}E<{lIqig!prt$3SK5)-AB z#U%Dwc^`_t2Wxx~P<47xO7Fxu!pJ{~ohkhd#iL{SFrYGfIV&!6S)#OeF^Qe^{R73& z%IJV<)t6FEBucY!K4TA{xI@U;_<%a=p_JQ+((Hm){?!=^B9=*5@eQVwJBeeYy0F!M z2*rgP-QKAI6*r7hPA5uFq%F;B$;}oxD<#d9@zMyxD+QtCEXwPZYZp_BiWjTT zj>#qzuYQYjdO+P^_U6;vFCInQI4U{r3ev_b#XZuSr zD5(>WF+3~n&6Kh=eubd$qooUqF&4_$fO2o8l*94Tspv#Dmf}!yMj>OET@2esDc{F0 z7h>U+5>T>7Kp7QKM|M)msrZisg`ba=gp&C_6wJqJXHm+}@zV2!S4u(gE7WCpKy3^< zE2YiL@zO~mKT|5LGd2jy`vDcRmr{O;muBO9UCKa78vtc!Ky^PrDFqXx+4uyujg^Ix z+8=cp6i_*boRw1Ms|nH!mhYA2pt$-W7EeI6JVGfu5~Mc9&o0VC@%Ke6eFExOHl@6f zD4p@~N(Cs{P>i0qsq($EQqufdT)*%mOhsK6WX#(=pp280GA3!R&?|W*7D}eBOGZGg z`O#S^Vg8aNoxM(A=gDzUa&#=%A!&M^Qeu*&wHU8df}-1)w|zj}z38kIH%lc;&wYM> ztTGhedr+`*_1R@g$w?CXKASN&6r(4UHUZW3sqaRYrss!m&lpkTLK=Gs_V>lI! z&ll-<##hqZo3L0I$M`Y1DimK!#L_5$@54ANB}_xyx#DAa3`)9=1!v-hNh!6&=L_GG zABSS-y5M|&W)WwlxcOe9bSgTT?fccBxY|O&U4@k5l(IN+nNXK0tWq6H+Uv;J^8vM? z6s7D*l-k%7_RQi5C|=zwaZ{yEIcLRX-bh>_#KNB?Jqg8r6B(-!P`^~96jzeelBct0 z7EeLR!8oSxDUGR2DUT;fyQ9i3})V8Kj zN;mN-#w#_Tr0VBOBKBY(cUFp;Ba)=Eq-ku%YC`eqSSklp^pljbAWY>MC@$2+h{f%} zrzz#LBx(J^x8!G`WOqa?xS`tTS!bn)xhF~5gXMeWb5PtJpp*`%k}puoaWNKNsRbnm z&q{rB_^TJ4mBQv(F_y_}^`D25fic-zJfPl9qm-YLr2VnU?1|M2Q2Z^RxB{xuE0ps8 zAQpCnsSPFbHMFr}SnoG>RtlM4C5;wFOFmQNF*ttYdjDuP#;IbXH9B zwd4^(^?4;tD>d?zt8Y#E)b3pkzM@1^s1jS4wG{ERFoUQXh&@Ew5L0dWTY8 zOP1#R{4Ih8I%6qNFv6ttqLgaM(t3X!JC3~q#hr+Zl?bSB`cO(_vUD!SD-E?$8L>c_ z=b@BulO$yV8%rZ785N;m=JLl(!P3IsY_vCEf;#oeKr;@-$jO zDRspicwT7>CH*Fp>R7*gLMch&o)WLLgJN7q^`8o;C)QC)w0LGQoz2+mP;!2Sg7yCF zjg)dDUOE}!d*vHYa(_X_uy=8B3#A+rPptS@+CxdZ3PhNA7QsyT}Yvh@1 z#=1bszKV>c2h{V2os}5#M1oY8ne0<61B&Yg6r8^-`-W0n;uCo$``qse#rqpF_Ig0o z`p#L2Hk*of6!}=*hGPE?1-GErAE%U&;(UyMR(6Bp*H1B=@ijU{Da#V2XC<$6hm!Lr zV!?Ukku%Op0rQhYX^qS)?`TCo_i^^xEr(JTCQ7SUUg-hF_ZMQpIr5)BQHn~Gc1U>T zT`0Lw^qKgGUnu2OF&4g!^@QTrvEY3F?o~>0iLvlXFRkdh48jxnCZ&8Y-X7$Y-cStP z#&GW<`VUH3k|5n1=au)MWa?gtThN1UQ_4U&7Iw7k10@^tF=IqPUAjvtjS{5(!jH*) zwQ>#0NPO|&A4-W&kXkbTMD7R0ub(eojL8ov#Sot_ywV?vM?aCrU=KE5KF9OQ0_Fwr zxzDdFJy6mwLc#s9-UTS-ka$OtpC=E{%6ViAH|{bEQpy%N7PgHIgpzs&3T_WpD?%xY z;-$41AIl&pX{VszCgjI1N*O8Mx#E?;F4F?)a7jw(E6&DwWe60{_lRXC`b$|# zc{^UZH_j^xivK&rG8=CTR-lx2Va75Piu+qA^YBEDqm-6n_4&62hv~W;MaC8d)HCsv z(pp@L@fmv`O6E6+WeMJYNureYvck?SKhUupK`ef(_p4A!$1tlu97-w_@5+FxUyV|F z$4j>e_|bBNj^%48Yw$#Vl2ZD|OE*>cHS&j0d{B%H0d@arO6enJj8{hfNAYgPEwyJU zrDwc!^3Ko4MnSQ4#D)a5#q(o7ur`4!)2oiVNK4yc!2 zrj(b&y70;vDCxRa1~Bs1rKDI8CpxF9}{2lIEzfLLF-7AD0cwU*T>w?*ZaU6F?J5WlFTRO4gl_^ks zKOkc#aZBwjO8HTYg|Ew0D7hyQ%b9?BErU{yx}{T5ezcqhCF3-d^SGnfjZ*fA8RJiQ zr$fp45gEIP_bYo)%67N3)5|L}pm@$AmP-LuuD7!iZC-bavp%*Z&xGQ}oZovTpoa9N zlxM}K7{7k;LCMa6at+_R@K8zv@%h5f$7VtCVwGh4hWp8bDW$C(3%h=q4JBRg^!|a{ zP(vxDf4p?BkI&c~D5-i6_BQs$KcJK;@zQ9?D|5AiHL~|EZV`;6l%3*JjBm;FbjGj; zYy6E~Ioer?F@F&E`FUkN6t6zz`8S}>kE4`d#5accmBj)mxq62r!cvVVQOX5zKE|KC zFNEU3ZjU#grM69_lyAkg7_TgXlB4%v3s@> zDVN0l!r#02Sl0!&2)rdM^~*9!xe{h9%b=vuvEqMl=7Q6CiAhZfRa%dvBX+x;JbCAp^-ywgN74I)rAB<|tQ0nr#JdXoH|{n-$xMclYN?}#DdiWp zbdJm`8=++5R;Tfdr4o-)%1*a*w})3YK}oNISZZ0S-FK8S!!7M8O=aH{-VDV9#i(Pc z$;X|QB4#JIw9n5gTcD(AcRs-@n#=&TeqmsXZq@-(*kpF!~_L1|{G>6a*FbY*Fc z%qyR3Mc+?Ox0HQ_Qih0i;gxMVV+n|*wWZEoqm*Hlr4=7v{q0aZdbE7qQV(uY%81I+ zJy^c_JD{Yyp}dJU_6MabklPqr{hd%A-RSOQsaCfs<@?IgZV#WaT~O>e#FBwFc9&Ah zx}_a>UfB&LBNj?GOYQyJSt(+63v;ym0!mH=w6PwRihf8bmRq`?%vV1PN?Lg+y)g1e zMme5V7dBnucYFA+n+BlhJMi83F4T{u90mWiwm&~vDBerlu|cAe2TG3NY|wZVi{_wS|ur^N0_nf zf#Nn1%lp{7DC4Y{=GX*j9OL);_Cm?lE4~jbRjdM~%n@&Q@X9_YsreAg2upn&M=1;C z8{KSO_CxVK#CrcjOI3-dl=|Bks)W9TXrJ%VeLAnRapNk#TvEZh? zca)_rSD}<;V)c3DODMVe&Xw0v-K$Z`+A#0I9)j{{7jCqretnWszDzk=e@ zJBnj1l~IFI?ui-WPpc0@(f44D@rdPFN~s{enZn2NH59+Tojwt5>;+0m5byQzE%^u( zufEYe*;0SJNGTQN+qP`4{6@!e0m?K>^+=N3+(#b2S6-^8ys z@HdCEb;k6atJ#*C-I!9IN|L_bz{m2fR&e^}ooA^!O`Vk(vu~301uDM!-$AkUN#Y_) z-Dp86yTtD*@X9eL>H1W3iKW)Jrj!!N(wv{~mES{2)hE1u%#&YtR-(-j$vC2rx>p{s zRPHcmrGPm%Me3Ejat?~2$FZ+0b$SG)EJ=~>!cAqXe;$gBd$8UkmRjqjl$9yc%7Q;f z&e4j#5%I01=8vP4nJLmroL4SD$<;R^j$5klBuZ%`W{e+`FGBI^y9y^Q_3|`INlcNR zV!ZMbl=Pnv%a4{y@=?k)@hbuRX!$dgN4E&hS?aqvl=4}a$|bGnHyc|R8 zX`3uPEBW`te}&@F@4sBL)cdO`RVe!1YVS?lh*(c4-IJv=3tqXVW6>w? zxtQ~BqLkj^Oo>;n>$>QZ_gj|Q`6;CgOqQN6ymA9dntp%(j-^I#qZB17{Q3S(D7N0a zxNoT^cKv@>_*j0^N`CCaJV0HtD5Y1JZ7df`rrzy|2&yk_N@*=Be8zrrPs$x+6a)DBsh#BLR`#NL# zUSFl48ghwJY9vW3K3@47imm4`@j+GPS4ydzBt4P&y8HvhqwiZL1=Z&3lu|&f3;%VX z2T&f}>Z}@6?Q<#RwzzY}x8#4J7|58ndQe@wMJbmPrIiK$b<>AXyn5dO`F17jlt_3OOk1&<|Q2clw+1n_n z3KpT1f5f`*H|`2R$#=F%N;-CXygh=d-xHLwRQy6a-z$qj$-vH)(L1POo~D%d#b+h2xS)9SE?i&q{bwkp zO_;G1hw|u6E>BQBs6{E&#c!$ctE3W8Z0x}rgAq#|N+~S1F@8Q)5=xGKk8fyDm42C0 zE{fmn;gwQQQuRCUA7FM-pHk%CN#vE%P%`iqr!g|9%tn+lNBm+Nuatq}*YCZKMt^BS zDSgG?eB_m~Pz=1o>m3(VRx?Uz5oRpqprjo}#wG<-<8(@?nIPRzCwP@n#pVRDzNgLdKQ^)w=GK^0{~io}a;1hT;h# zmSsUzttX|d5@X>NHqjZ`#a}VwV@c2% z+l^S(233)Plrm9#Cy`eYp=9oavN5RM8$u~c>@U2M1SNMnGPWhCP7I@zeqm-T8A{qV z#IiN0l1EU=zQG$kp&SaT?QHs}W0fP)%J-DL=$ZcdmHlDJbcmKsgpv=a*8-nRsbk$=_8- zg<`Kn#(oH@)a8_NTztMvVl(zM6xRyGayqCsuA&tA_bYj&1{AL!%GsbQyp~eF73;z) zHKF8wjEr3fszDnl<&gMxEw4NS#j_N#{2WxLHdD&s_?1FS=6`48SzVVUh~-L9RozM} z(#{p%#-4*>FNSh0sOD~?l>K5GdLyB0n>5sxI}5r&&Sdbic9zX1Y14%E2T_Jl=}W0 zHe-#TWa!Z{#a8xpO7V$x;g!Zv3_M?q$8FUvmr}-vCsw@js#ZRN@`SCf-=Y+ctgzK@ zqBDlsg^_Bj{&y&)llX1|AIobx7Cj%UVXK?>DW$e}j?61fq2%hh<+HYW?_Wx(D4y~0 zEx8$#R6XafWvhn~1&FiPeB!TU@^^ZhL-DLbU0$$NkNlK!GeP>s9sk^K0VQW6VySJb zt1*;vQv7zuG`3f^grZmC#*27X7N(TF;xB+qXO(m)*?RBdC0m^_9GT3CE_XNRk1F-^180emx!gIt)?bX%2VRo zwY>6%uFKbmrID>}SD}LE2gHnDISK|B|b{(~%Pk5Wz_!=;!oE1+Kc%>5*8}IRXTiELC^OO=4 zzo^42Z)rt;6EfXabzh{EJ>n{fe_OCK6d%6U=xuGQFJ7jUUEN8 zQY>+=k5@8uEc!l7ds}5RqLf4N>x9`juXKfC=o>^GZFRBx}6;SDkHD ztvRLG;uom+Hr5SFn!Y>Q)m9&;Q_5!X8xg$HUB{A(x^%NuP8&*DDUL9FUEYC`qrcYp zuB~2tgHmRRUu)#Q)7t}z?-nxlo~=H8lTxONUpVBIccJJv>%Dz#72TOq#)-X>S9(H8 z`wPkdTfNzpQihA?$h^`EO2!>zY>2IPbf***X2yE!SoFQ|VYVvSlTrqW-{|IJc~92` ziZQ}g1Ky()Pnb#{ow0j}WfY#s{V1i6SbhFo(Y{c8P>iv*DnF1?`io!EN%M@Gf8Ro3yH@k_WC9im(WTGW|r`xLV2ukT8?v3*&i36ZK z`cmsGTa6k;DV@c;3cNB9%A+?R=3&l1hEh6+8RL~fP`nr|y$ftrVFIPJ5l0yQymBxU zJ@OkL*{bVgO6enJjK6g=1WGENV#YFCeK(C#=87j)yrOi*@I>}5x7Fi5O4%sJ!k4{YrWZ4tCv#B$6+eNq4@P(>#eqmTuv!-#9qnYPaXj!N5A#F%~s7;QOaEL#ESn4 z&4;=!dS$WGR-dn-lsREOUq(Vn!(D6R3uJ5qrA(2ZmF%6XQBX3@K?!27Z!@Kg6niC~ zF)tLKURQ=}wfR#@87M0JSDQvd@$2uy?88%R8>I{r*Drh<8w2Ih_hG)YRkxj#GG1JZ z@xLZN7K;AmFXOPS0$)(dWbquCSH|gBzDLH6*y_0;rK}V0_VDkEjn}d0ThQOyYUCbD zxf%btuzKZ3{s~&iM#jFk)xG_cGEh7f<)6qCb;k59=pSs==Mbg*ks#ew;D3XA5){AQ zg*%PDe}qzA5zoc=dGchfe1(jivz0fSQU)hVXRrJkd5Tu_zU2j5Jvc@w)5X&_UYV*D z+)wuYY^#|+P>PwfLuh0CUf(pW;C`}k1^wkmO4*(y-MHhG=}R@@ryeAXgMEB`c^1~L+Y>* z?RfT@-)xg2jr_c_07|O9pX>^$x1%Vfw-^imd|9X!eJWZyq-MoX%KItOeSZEn)FLRJ zEy!58kb0*urHl+ySq#OmZ$Za~R23Jcj7X8*N9IqfKZ25>Z$Zb0l&ch_3`q&^OneEH zbbSjtDWr1BQcABBX&mEYS*l~f{bX;IkUCV6Qku%&>|*!%KZfGQ&0*v5kQz~$Qfj0~ zUpVB?_m@HOEk`WXLh6k~N{I`zjrpP2`X1~PAyuOar9{iIuo+tpC3g`r_Eboj)tr?A z=AC4zjq$y5g|3Uf4V4;F2cD#q%i?pNS5`tvosC#(gw)&`lyXx1nihXkWfhe48Hfc+ zqvt5)V6yZZ5PU43Xk`kNnjsZan^F#ovG6O4)jDGn@|1HgQA&2Qw5P-?YoNHtl21jYLtly)Ii&QB>P z#9x`=tG@?IdQB*Agw)3?DP?zpG*g<$W^6B%oTpG1D8?E}`7~jZkTG7_r|VK3O8b!V zt*4Yl;!S%#WBZ|as^%$wZlaV4;+X~i=Hme<(O9PK1e;Z1Vkh-5kDRagC!mq`SL-F2%(krCe{!A%-#T!KYF5C$y_U}9H z7=1%(<1fxiv{_fo7{A-|1C-QUC>}ibuTe^(_%=F!-|{4s?3>8gppa^MlTs?gOS239 z-uNk9mm7#>Xh^O8ol;Ek+a0`eS}WI~d=OHn{-l)qZt1OOey{IGDE@26*oWAGze_1U zyQR0D`CYg(P;###meE+h{Ozp7m?5|H+~<|EP<&9l<3h^*mr^p_(tQ|SIR_kORf9W0{GySd6n$(0p0U7$3_8 zC~n=xW@G275Tz7%OLs{45#}P494N+o>?#y>R!sAB$xzC*&tat%uQKTx({CH}ax zQq0^Iw@a!%`}OSWP(1&ljeQzYQ=g!e(Q(@)rRk#&sSw{(xdFvzU^f1FNR3H#R$OL- zxD8<`H=+0=p==MS&NV5ec%0ZP*;syql8yC?cPIM(bIwX}^I@#m##kj+D+LhC7nt+E zKq*mi(i3?GtNgAr7K2yG;_3uH+c0oB7Ql;Ba%B)zaCHvUw--nW+tA8S-esAZjlr#H>sr;=o zRs_nakouuLrF4w7gpAF4jT`y@fs&(luFizi@{Z0*d9!8g7hx$6v{DetxsdAJnNk|W zN~7g0wl4od@$3DuoRDhX)mf=vzADBto6XomD4zUiV;3=6cBhmsv3rD!&1PdUwqZVo zXQlTijFvqpWnio{yO_f&5l}q(xqmsNj`VU?Dw+#p_X@G_N+c9p@522OQfvB9iX9u2 z6t?f@gJS#*rW}a#_krBxopOwpt$u6_G(D=8R)FUn&ri3C7-eUTG6xdYa!Kf z2&Ht3+b6^_hs{_4D7kw5qLpEk@^PH>Ej3<=hVp3V3QF_glyW3ax@|j~jU@((Pp`zG zv=~V#ah0U=%GvB0UqLA8SMuu8W3;nU(VSFitx%U)Y%C^})Ju77Y`{25`KOZf)&Q>* zf|7O-3i`|ZiIlRt^6Ic+DGVj!JnDiG=J*t6rGh!tE!`XE>rw=YeFnVzJB+(< z>nWvy`1TX8l!0O&L@Zb()!yu^lrld`mPUSFDXZ(UAF=Gid~B<;QqsI8-nE|9ikl~w zgOauvN(g7K+nkjWW)1Oo-hHf69!jo_j9ECb+C?eDQ>5SH zVJa1MU9u3%j*uDPEz}G^A3` zQc9buYlSw(XDktly$-S9{N=F=&WdSz#aL#su_Wobtbu~Fq}MJvD+SFBRi$?lXR}H& zl=M}|7|z8S|LUy7m|sj>;%qver@qU21EC{JT%TY>W@mmD^oWBMX|1iYz z5$;;YQcA8k=jWB0P|}AY7MzJUa62ow-H{~y1_ZA>qZNf%R)*A>L`q2$zjwiZ)%;l~ zo*{^3HP$axDCNBP{tLgdcn*qhFk-=ctV}gZnJj+o#K(RKycU$qL5O8D=KN1OD+SFL z5~UR%|JL*KI+lTmWot;4twAaG6Qusa-?)1LihBTJ*@jr2rIZT^YlL3OE46hj{SnKq zkShNIrR)*!jq_h&FXT_Z}lEq;ND|K9k^P`o{$U{@jSHD{%e z`AxhugPq0h^`$|{eH$73Dx|hGcUDaEDX}H$ ze`!l8FN@!X;*|zEV{hcuC8a&3tW8`e^nJdKy#gh@EtG?JTdx1kmfaaIbN9rL+=b;oDeq9ZMa=g8i}C zBPgYIl602D_m>t>{IwAa=3`~Ol=5_v^sMx;)o%$U9g5x`+cuU`;*+GA68|e^={jRN z7R;01n@A~TlBBa&z9qMUlB;9!ht%yUlyYBuyOvj4L-FbAFF{M5K`Gyfzn3(ftxFpy zZrzd>qWZHbWqslt;rYTVZK2pujJY9|Ige6CB}%)ae6MT=CG91|;=_*OB1(B9OyzYb z9<5;K>Z>J`@?4^{bH&H<#(!hMo>EIcrIb&UzN^41?VwuYPAJ{_2QD;q9lLlrk+r z8e#albkTKr49Z}f-m-}0U7S{DQ_5SS!q3NgKyj6U(mkZ=d`~Iq;`ftz ztX_Yhl)B<>5C6MwJ)z{5LM)v^D&LQkQbip3`MG5;T^AiohmiW_9HqpCnX%ql(RIOB zHLhKtltgi_kMEW5>AI9dEUiQ8)k~D}e1iDS2-}kTK*^{8rDaI%xI!r%64nW`K0cPd zP&{!^n&V4H*C}PJ__Y)MDb^24wi|V68d6Jsqm)(hjv||}{!r4A5X);JRq&Rx5^bJL zkX9Ca#yn7RsvwqE@s8DBlu|5Fd{>kmlLzR!R6{IK%G{@vI*DQ%W0iqWjHeJwBfQD= zfKpl}O6OwJ+0(Z{P`ot|OT&H6qIbeuB;nUR|`=}WSDzOL!qQ%AI4i7Ex8z_v=Dbl z___>(;=-Piw-%~jl2S$|N$-jC%KK0}^`O+mms-nE%Azop51^#gM_p2J@1g>w>=JiK z_*jNR$$_H3)B8*%O4%D`EF*L**iqEq9y}RODOpL*};r!M*n?JH_gN=bRAmmMv~Lhrl#F z@%)9a%Qz^H`b$hm9jxoD6fn<=zgf>K<8@th-;WBZ!SyL+f0)VyDA{@(GeRn&5vA-* zl-@_?pZgP`_;oD*;8*{eP|9hsF4NfA#iai#cWqU*Ii(a9+ZeA*{*PkZ#;^URQ%YyC z@AJwOC_bIBTw9H6ODX-uug*?m`^!`)UaefWRjKxr(kDr}JIcp04N8Vq@cS>@J5tJ! zFrSsvp`>XAzh%?03#E(|tIzk!8BkK882DYDeBCHzg7}^VKU&U&;?}X8!7qOHpp;SK z6PZ_h|H+tf%2u!Trj#B@(tAn#ZqFHv7_ZEMlB%DT_~oy(A(T=jX`S$_JusDlSJv8D!j5t$D(f$;MW*8Orexj;&Y#`{$d>q6a&93`P>Xj*^{_lXvzGWDIY;e zYm8Vn+3NT#O8HTI?(@nLC^=A!_4pO$d6eQ3dnK81WrnRzZl;un;?5Nx%PJ_j zP>iYgHO8%!a#kMs+1dCfI%E1~7k=rx$975yhB@-D)){MoSn#XmqjpisT2bMj`)i=M zS|S$w0(;E>rOXoF;pJ!JYoXXsjN$lgL7P&BCrY=|r?PYYbvk3X*XJFImb{Nr`idhz zKTlo{#b||C2HWcUgOt))d?NG81}HwQcx+YgFr~B-Gd6>5$s3`hwnoPK*y_?Zl+sZA zraeC!-vlLFD?M%1^E*m;DNJQEl=L=Gy4&jFaZ0HzD*Ur@3zQrvMps)6JVh(gx{{wM zWkSi&_h8?$Rn8eoX&{~^@XDt;7AQssTTRTNl-K2(!)&kIs_W7Yv9z<*-#<}G7jYcp z-{$xXimjDawi^EnrSup33$J_*#r-;z=C&$-jZ!9uS(j~4yjp2utF<>NMV=?~BmZ_N zxloKowyN_7rDTaCKi^+=KuOd0#_QYa;B99mzj;i2%Z86-CzMnuMqOJCzDFrV#2peo zmR(RXbX{uOs?Y;Uc||<4;A7bhNC0(y86K%Dq45j2IhPOW!f|99sNZhtctUxK- z!|eNepycXP(KxIt<0xffVt8jsd$pp^kt^70bv&iW^D(|x?t|jhx1h_}>S&U)5^cUB z?<%mpazB)8obP)}qa|0Rl$XL(4nT3&f>Od(=c-Z46XI?Ue^ce4&e$_(V=h~Dc#2XQ zh_eg+mfDv(V^2dVf_ZWcO8H8B?(?_Q4(W{Hrix+OYWQ=MQcm6>VQ&z91;vAVu-<4} zm8?xE^~AGRK4XWW*tiGljk48|mnh{8F&4i1U+Y+^K#8!`kb2Ha44#$J+fRJ2JOU*% z8Fl$Ls7f`Ylx0cM*Bbd)zJcOPf^t8oHori`_O6r5{`0`{> z9T-L_SHw9#zn^>titpa`JI1A;>NbK>-bj|d-oVFl7D~=-C_e?&^Il5XEVg8R@8TSk zv_Fus3qf^rETwD{f9IW7&O`CuL@YT$wP7NqEKHW(6y}v2DCt)b%lV-4O{J8cqQZZN zAdQ{vY1l#B}rds=aoxPvQI!kOa5&srK}cbuyfeG@yk$hzC*??2G!Q(lrmME!^~wf z_6rpEQ7Gv99X_Fyp5m=eUbzCra~R6cLG|}qN=X;rN9L7Zq1Xpe7mUe^H&V*8;@px~ zuIh~KLo9f{)Xb!mxTFojd~7aT{cBLXHWbWZPJBiwcf>DI%wwy69f}b^#;yic(;bv@ zUR+o5$_*%cmaEC1B7_@Q9+dgK_TJT1QS&MUWdEK8BG zJ3-a<2TCa;{suSS#{PohS%g^b1=Y`|DW!mzF@ChX10{DJV!^IL`*W0XPki@{SMEZ| zn2lKe4yuC}DCL&;wji(EgOWW1vHTNM^)FG%Rq@TodF;r4A4=L(#Dd+PgI6f!qNwn( z{0+r75z0f<h7P*lPE0l=5Ym$^$4~FY1C_xF)wK#h)-qdLpwU z|G!XjM?i_R)fazJiu}d4x$Nrop;m@L$!Duc_bFwZ_&zeP7~4_(A$iKm2b9t$VYZYp zHkJq|#z0gbu|!1_c05TeV73vzc))+3KN5Mf#UkTvr60Pgf6$+mFAC;z*;^LR|cqIl(b{Av}Go|z8D5aR#EBWdd)OG2MSZ)T@ zo3WJQlAo1q^-UeZV< zDK7E-eZD0Zfs*?sGKM{+>yJ@N=`fX|P*S0I&jeM!Cn%*@f^@TskEIwCUkAi;3hT;L zN{JH3F@ERD1;v2kJswmk&rr(UcySkwt$uMRp7v0_4XTy3DCKPYG~p@6pNf`%lJf?X zBSDp~4yBwBfBlEwgDt6Jfnt0WR1ND=$~W=iubHqJE2U$39kCn;D!U$~?2VVcYR+e@ zw2lReu{Ws7Hl&n*m@&SMm4TAm4zUD-s`smuvR!Oryiyj57mD#kP+e$BDI3L%@hiS^ zP+U5e9oT_yNh#~&rEfy=PqFe){B04-=RvisHKhc@R4PD8h2qT&s;liN>S7M=LK=G~%svVsurKGH|D~mWN*{u=FCqXqWgHqzcoWoRt zk`BeYJgEHLXhpiKz-O$oj-?f1`8cR<_n;KFxO(MhN^U3~DBi_E^>%Mcd0JHX)oVPI zR2|C#?Dq7fls83%-?vPFlADfL=3u5YfKombXBWJZ2qhDWF%vC$Fr`cpzYWDJNl?;t zEK`H(xnYztIYIhTE3YI&ap_nl1=YOal=7iGw`6-|3KY8~Vi_A$`A1R85OL1W$5I7~ z2a4ehD$f{7=_e}uX+l*fsXCSsL3L|9rMw;HIQAHn+!l!CeViptrj*w5+>))!<504- zG9;+Jnno$j#EkJzJ=ZQG!j?hyiy&CS1WykDsv8{)E9dtuRH<8uQS#= zs7frLlr(W&$?w2F`Cp|+P|aLSDNiSebAGn^PeIAl8GAdZZY-sg1_|4RIX^!ePlaOZ zjCBdBjOCQlSlmzMm8YRRYRMh3cd?368i*~Kze7^vzsegyRc9@w7~-#)@Uhf{lB?^| zCaA0pl(JENzml!~Gf>=mv}_qv?Y25wU6Q$ov;xqOf6t8|( zHV&$V+bQLAqV!7>{LQXfP;5O@Y7kWMyD23{JQd}Y=b?D?Y&9Qb*TC@AlNfdjCsGStg$G@qPbA zD2Coqd^V^Ge@!W)<@b@Rf@N!?>B~$4#is%drE&$$`-LMd>eZe zibuy%3{SDYC}m}sN)spRYSom8vO`#Y% zmV9_?z$ikTy-pOrYR=cC85CRh7bB=n=A)D`;(DK7Su}^@(f#EgOT8IQDIbQperW;4 zrDM5gsh~+IeZKl+r~UVR$7Sid&DCzgud4aY|_yrqW8sf|1{Q z!%{^`Q;PhBcE0+pbzSuH<*KD7m7|m<;!2!X+CcI2g>u+)#}3mii%qQW}Z-V|*;H>x}6e5kFe0P70+o6nCw86`JnrS`O@l+og^Gw{{#t}~{0;kH_;=IfNQRosK+R~GL;@iv9>siih|pp;OUN)ISG zjiF>(D&Z|k*(Lvy5xahQ7fR|Ys0)<&8I-a)LAnvaXRIfbjI=x@wmYSKEXTt3mtIip z7xOaaeV0*9)!e~R^mlKLc+&n(qw2&MFqS4nL3`$6$No~NuDMk&3-F`190Ka`x5 zyo|X=I4jX+d-*qo*?USJD4s+p7-3e9qLlpN`h~xzGyqD58*OZhr7n$eR$|OW@k@m{ zKd%ggk`@PLlchROpp?qu4I=*i{XtODDx|l97_4pEmfaahC;CmLs@Mp z*8*oH#w;q{Qk%j4Qr<8fOTj$l_eGS_Dqi~4KR%ZCq2%O8T@XwErIfNgUV3MQS3ZD} z5t*la=66=2&7$HsHl1x_!*#~~-G)0?mU?OxrSuR-nCWa?M(B*)&r`OnaaIbLTg15~ z|I1$=LP@)`?T&YqrAlp}lp_gKg>j5;V zjVxy++Po7#H>~Q9)yhv$7F+6zAf>DnpJIH*#zFDrKv`(1GJ7ede!TSi$b8=)4<+*~ z>N4L_?;dbgV$3UUsg3c<1Sr|3q0F_^)%*LM5*Um~o^F?=f zWAY>@_HigPEmbj_Qi7GmUov9HNl(MjrbWe#_rs-IYqAp`CHRX)6 zQrPSsrZOE$npV8nxjIiNwJLof)MaMVM`dEnF%Em zit&M^yq74&sPv@}3$OT~WN1a9`d6HlqUN=@y^_MVu~|@DI%9(@HQ^ei9E-Cgh0WM( zD4rvT#bc>yH=UJY=FYesVJUN<_`inI*HZm{r&Zhmo;fmKyx0v*I!r z#qADDnFqyt2ucr29lS#+c3ex>E4F7uO0VjE-o%OWVY4dpFMjmk$UF_k|TV&TW+#ZU|j zEx99B;sq$BZ)NHCl6>q8_9G}cyP>>ksZ#|h<#J_dO!l$Q$|X=dJD_y1RM8@o($_83 zWi}hjQYg8f<;Bv;MJa!|rMk>zm5-rhXXe#qZb?cR8Xpj<&nwHI*qid&Sn0CPN-^_# zy!5S3{=HW}l(Y?qB?C7iDo{#`1gS3b*s$@m1Z^v0QZlCx6O9F(|6Q0B9Hu&bc>S3<#4?Cq+Q@@b;< zMbLSzxtY=@P;!?eV|XIptwt%oiocG{zcaELO1dAh46)RTr<|1{raMXOm2Ae=K*|0X zu?(|R-I|orE=g+1d@O6Bq%B1(m|On#9HmT3l4j$4UDoMXK0+*`EVa0{vr^dnB1xKE z%x}Y0e?63p#fSxS{uk;}$|W(D`D`2803~N3Vwq^E+Ekzn(__}05NuPySu-g;S&RH?dP|9XuwB(gfbu2Rx%g2^F(4JCmrAS|# z=9R5bQfD9*>?m&QL@5bXq?3__Y+XKsk}(Ym_QoIY>Z}wryM(EH4kdRAGPWM;{qB@9 zvC0OaE(_aoBg{4^uE~gHv!&Mdq?7|yq?ytpHe=hNq)kLDID7T>aaLl?2UVoq9$wj@ z>oOj(Y{N?2Ln$?@iX#jg%T683Sj4j1QlAf|l#x}XXC>dpc0qBEMl1n5UxraipsLhg zcx5*f&q%})veY*toRw(vr>ataS-@uO3mwaF#Iny)m%WsdU3IO{l6fTyO2$wq2P}1E z9HrP*rT)Sz0Vu{GCdtt@yY2*ukwFJpyg zQOf)((#{pH*jnjhpa+1jYY0V%ck{pB6eR1uH}`(I%81u`F@f`DM7gm(#OLh9zS;=pfts>n{pU2K&zST+t#DY`N9mgr9N)_=NcdYUq6jvHz!Roc# zDN1QvMeLRAoc|aUyAG7;ma1^pS&1@RRgqfq95$Bkp?GTLDKQr)rGA*oaVTju^D=hl zXG(dh%4VUz@UfhLVmy^s{p(jKC8~<_E)TE#043+~Jmu@_lyW>}ofHdO{gY7qDNuY^ zzvNQN%9QY8Ii;0^Jf-)alrktq`ZW_iW2d2bE9GUZ{at4zpV>G?>@RFf{t=3=LS7p) z|DlvpDN^6(=P+lWc*^AUm)wVxaw=Ksm2=rx&gxi7K*7C>9r=nmp1$QXKT4MFkj!D{ zW9Ojc7DZjASZa7QrMwlUavq9Z2+AZ&y$6@^%^Yu%;{rF<&RV0q;yDA^HEhFdD9yt9(e z+#IIzGZg>B&+!ZFnDfU`%J)gqjR-!LOImsG`JpO58NDDKN4w-=U%?Kp`;y#(i1l!+fzy*@hQglm)lTs4kBY1 zEuZN`DQCnR5q#hO3yQrDvGl>4^<5|>D^Yq@^0C~3k{LoQ{Vmm^o3oPNTqefC?_J!5 zl50U3XsI82P|AwLgTg9_SMEVc&w_#*5rcYD%Ckw*-Z-z^hmyJ*b-^4aUq4F86#EOW z{0${zCt|^TEHJ=XDPTq=OTCi+Et`L!N<{6+9pe9 zeEc441e7!=-Z|L0nnWqxlcim2{!2%ZP<%R;1(y0{Dy6(5&SCgB>+?bJqWZ>1mRc~A zQas7h*(?9;jwmQ@^cVfc(0g+zw>EssT6?nf6?}z(NR|4 zAMibNxBv+qsgq1gXojK)76K@OiUtKiK#8C<6(eO!6GlM=5edCR0BO=9C{1RrYiQC9 zARs-0V!;wnsv!6GB)|WQXRXxlY;V90&cFIW2f}HmCIPX*;}f z+b;$sJ%m_rR`iqkobrTETOX72`idLHfr8VuT^DgmJGH&c5O)(wK*`(=1^X}YOF3n* znx`b^!&p$VcR|682U}Ng$|&`hEGs3UB=3cS?2dO=bIKsKACpV06qK9;_>E!rt=kuz z(nEb4T~)COEND5F3Q%%>hk~0ZhJDK^wf))(iJYNY5lTib6zo=~ z9^sTiYF?i_QmSN1>>tz%`msXCIVD@|$7H24loYdag+6)v$>>TE>!7*{Co5H;xNbo~ zUf8^=hg z!f(-)!q$}-Yri^_)Ph*G#@XsKe{jlEG5(TkKuN|5d=Mkp`Ik8*P2CrhYriIxOzb9@ zo%b`kX(-sQy!Q^LjE%7$i-(e4 z7K%CbTp%v$zF1*vvS0nah`VnIMyZ&u{8X4zLNO{HC|URBm)Pmz(Un40VnEAMn<4Ha zdyP^9zhuQbDtFrbx~oIU7+k`oUF*&PexqAP{1@Ew838S?ZC%FS7%AG(!j)Ga^Y|b&A8^#mEzW5b+=kp z8bS%0Sa3U#w-u-SrLK+3=PMgQ$uO~C9p>}a(UlU`5|4HsSw1V;7>Wxyie??A;gg)w z*P~`Zi)TgehvJw_$gD8UYZqOytX3ZFjtKb-?*mZsvTZRJ>)M`EntJvsJ&de0fs+0V zV!_(Q_70r#oM(@s$o29dl;rkMHizlO&e4^U)@=QBt#}@}DU`SlP_`nUziV{GvTo{o zNn-6cGfF2Y;V@0=&M7aeSvT^#Zx5Lg>jDL*2A=52DVx>ZYFT+0O8QGswukAzUeT2j zmdB^=@rmn}k3h*zgM!l>^ZId0H=ow_zxuo2&oKi$pCX1u-)=;u=TF~WS4D$)6{G+xPS$P6V+!(}y6GIa-qASI% zGwK@bR56w|P*OfbEI2XLXDX)@QO{V(wci#>a4cfM8tm$soYK&*Wf92jqE?{DFhFVq>|EKzyh#4-~lhE;_}!km(+uE5Jm2NMhK?l7751-5ZYhWg!?=ldN^ zEOQXc*f8zi8C@x5jaGYpxmCVkV!@p%!7*W)vxifLsClq*iFJY!#QiWX*p)vy|tX03bAwx zQ^qw;8LIxqWThvR%+2{qhg+P|RXvY9Q!KGpp`?VMyo?hN|8dGdpEl}~*I-|RlJ4Z! zzEhxV)P1oyD?{y*<#o$mP|~(T!7q705l;C={Z`6KZzvhN@>}KU5}Z;&oma}4Dt(}2 z?}mbVN9&a4l;_k_obr6XFO=Y3lo)!;;$@>NWvoN$-m9$iGbOeUvAm5{xC)#yTfON> zR$hmawjT<7l)M{sM`F|$ z&nbHnwUhO7Ed8P696&6i!<6WYu9UK>CTV9yW#ug>&Oy`*vY;C$MOR8%uOw-wYh@)J zO5W|+$bx>g9;d8Mny>Wy@*MeXD6S(YG3@Vu+#tGQSv6{}Q51Q;Kfox*P-4i4IC_6{ zrGz!DwpL=YG7w7Y2`I?!xZO0mQrtRKTifH4l|fMA&OpKLTk4~na=o^8r@gEUhT`O) zUNC!|*)qCP%qm+)+ZCNIt|<|ej31%k_PNWgIi-3XEpu4Tht$L2;Lag4=1Fa2J3YFB*Rs@%2zkfqJt&#w@sbf%N5g|SWxkrHBrESj$uMtnt->1Y5KdVT zqcR#wN;N20PyT8|bfvg;KA`oMQ^md4F;H@9Lcz}a)*z>JO#DJ=m9p{ylsNOcoY{H* zAi7e_`a~V|$;yXNf_N#ddwcI>TIji2(Dl4BrG4G0+UvjO5oHALRi^<9aD4BR8)tu!iw}exkQM1(K zv(=wMaoXg^a(y|cl+^E266<9ml&q)oYyZe9PTA+zTBTfK8Bl_$`R!$47N<;C=lilU z2}()_lo(Fg4E>T*dik|eoU$?*N=_#z*n53`6Q}g`&r({YT>Dd?q~b+s^KSLiTRG*3 zU%RPQ?qQ~y62t4*W>36%eP5XN?c5URQ zjC-z*aLO|=me_14seKU3@h~kt&M7VQk&@Vt%`r+}D5o$-KE){y==CBhbD_BVp~TK% z&*B`XR10V;S90FPJSe$Fxqw$QE^tZ__4^`ccFl*9`8rDM7rdMBGpGEk<|@d_0#h$< zAeP_pn*1eB$x-j;mv7a`gpy;F+%S3m;*^c*i6Oa9UI-=qO_bQxFkQXEDHGM3^kiib z6jy)5as#dM8mIK~Yjb4zE4J9gVwBtXCEwzdCH^e!D<-y=&!A*OaTTy>$bXztUcIYR zj%A4{vA3WUv8i{#a#8nRidxUA-xv8Vf~8PWpae_UG`J|IysP$>a_ujJl4_LFHoazX z$`rL9%MiaW%b}zhrJPNj@8OiO>ey0NR{XETT$OEVTaHuS3TUg=vhq2UT=Pq=YLl-L zr#!2EU*uagRzk^ZFSTr{U6oU61+=?5CyDj43QC6QVLUedQ-f3fRPRrgV_9uV3_X7^ zVAG*^PT8*BZYtjpvIa`fe39$e6!CG&I=^s*RmCaDx?Vz-?X-g`nELTqo%U|RzP%^PX5^Rq#Ob1SxsAfdSx6geICCw-u zZF;pcrwmiy=8(t9TcIRFadok&SvOAUqTV(yDnJ~6Z%ieqAV$EGX9Dc|_C@tB--vja-5QHI+z zcNnK+`Lu7PyvMf_O0wz4M%nbnNKTor-q|JRknDnz4#hRvCinZCGC^0w(Zx5WUQ8?> z+4RE)oHEd-jrrwOh22ox=KC_TUD#S8T5- zF%!!a%wHyPO0s${oIHlvXG#p;{oo9nE>7i?c=eYoEBm3Oo6*Hwn+D9}lrn1DmuKP! zpyZfX7TEN}Tu!;B-Zn2Q2caaJ@z`RUt}ft|D_(7dL>`ZQ3nkO+j4Zb)V=<@1sr4eq zk`2Y(97`|yGRUW8M99h^6AKhqmQCeXa>^>TPnO%uVN);9LHW|A!)rL@8=tlY zJ4M`iKVo8m65M3d@^zeYNPU$_9$S8A>cz|~wxYdk;FOKNIWhHPM@=k7acpX`g;VD0 zeX>|$$4tGL-M1Y!l@4>tVBbt77WriTaVU;acH0z+a7vo`OO}=Ip`@63<$jyq+{q~q zt0g8YC!pjQB^$HXJ)Ba?H#?>hI|(K21=Rjgn||ESDL;GFk&;;Zr=a9Q34U+WyV;!L zc<06x%V{VXX7Ba1O)1}T$_lTx!zBw8>o$%2gHv|siZ~ko(UjOL zP;S{&?J}o)>CwLXQ^k8oeu9#P)zRP`n|A-hDGNPX416^#-R*^=Q9j zS@{`C23D~5 z`ovf-e?Z9?h1xIdP^Ge*(oL-wIhH@6q>Rr0jjgJ{DbIMc6A*HH`3s8kAr!QiN>w<@@^{iD!N^)+`-%#As5DWU`C;gmKUsdE;(mzm~nTX}KP5Y8KCElZrl;j!TRVb-* z5evRzPuAm*YTvxnCoeKWzH;X->JBpxt#Nx5~Rv(rqZe+teYIQ%ZTXdwS(qT$>QfHk26VFK3?T zl$yFC)_wsfsk;!%uQoOB#3@ZZT8YWK2?e3#>_IG;@ty9%DZM?~ox}2OLLn&W2N26e z%*9^jln>QbDJzAc#2rE`KVeV&6;7F__WbhwFh!tbeur2tVE64cPFdm6_V;C_D3rAC z5et->eK}>FI)9OsVo=-6tgBLXlqKcQUyvzwR|OM z1*g27pstUJzp;Cvrh^+eB_YNVs{tj`D42;i-4b0XZq-nK$@2PGO(+n1FDC~iY?+H%X z7o!p|CH5ec$_~vr%_-*-v~jYWr<4fA{ScI@4%Nxwl$#0K?G181OcE655ww@;h~+|b zrKlCB?per6Z79JMD774__A{r%t78~hsRJds6_f;rHvYyb52|Z@vQpQS*yB*V4kiD| zDIGjopDg#5^`NA-ff8_N&1FvM?1}C6hWb#lpTuvhjzi6_a>}dvI9VL^-3P_p4zbj8 z=->@b>8H+-Cx^VlVfRU z>g74a(!`7SLb4~(g;d&2gLFa_Fju}%Io_4MXdeCCKjVKcW9W!DbL5) z!`u(WF-j|k%G|>#&0ImNB+@X9^R zgHSS`M=Z}cG_?w+lvCIGJb2iYm}&bjIW*VLDUYgqd~!eb2owkXSnw5xYS-qJw(6?2 zd`rNiCKl6MzUI)%dYsbAqup;Q&nufl$x1~meH^OafKwh&^OWQgOM&8s;(F7eaAQtM z*0XNJ*=q|Z8RjdN?ojIoImPQyGwwvCC6wG}5X)ePzI}*O?v1g;T0u#L5**^tW6e3G zxMzy8lPJ%TAA{mxbm1D|kkgV=N~?XcoZIs_l)N#_D2H0M=9IGP7)DlFLkT{O5*zK% z?zWs#!lT{WBj0TN1eBbopnT*|mv)>|RLvogl{QdPpt#1Py`*wVadp*NF0r;yGR+8f zB4*+pIHiI*f|Zpgq2#tjiA{EBXD3dnq^|SJ%2QBMp}3|y)U7M8Xm_c}?d54GS!QN2 z8>8`;|G!e&L5YLnn(t6r4^FA3&iLe5o`I771Y%j}(23VLrMlWGW#w5YPHV)n)S=#e zIiD(}2^f|3d)xZR;8kAfU%Pog~-^eKy)EC+0Tg`hxahrbZu0vU0b4oq+#T+?L zsW%kUkGTp(sE^GlPpa=F$x0t6d3zScB9yS5Q{MDxS)H=d7fP0irDTN8?uxDyvBvnc z-2_?b2PMZSWg_(XUQX%m`%>x0Q|DrG zEW@E>nB1NxB2={mr|eVrEaZ{W2q@Xeh6=Wc(56zHvQaHDS$P+VJ042g2&KkFSBhKf zJzpzdWO)sCB$Qwc)XP&5`n^1-Y*!U|oIDCjN>wQBA~d)%r|ec&d*l)eLdi5)ov9Hj zT8&cqTCL8w(|)3SxOPLJvR4DSg#3 zjQpbXI8!es3pzbQIS+Blv+B1}j^$%04zi$K1MsbE&M8e|RK`Pbo1ET35h~n@Q+#Ue z%dvc7V)3C~21n@2)|_&${!13KZYDrUNkS~|MCgepIpwyxZaGUFCw~eh+kE$jM(FQ$ zoN__^BFpV%B9vrgs0N2cs9*c&N(pPP8jGxCK*_*(EI1-Ul{<3E29Gu#la)zE!Pqi5 zDnd&-bILJwq$Deoq2wY%HTZsn>UZOm!d~sZ3%Tu2ff8(n+W#;@-*@Mf`_!9`eYwPD zK*=`sGCe}e`g6*A>V18(G80OgQD#S|#{fmoc2OH^%do^NnJDW9uR`XDp|DrLK_3u`Gb%n702_gj#>XDU-a~O|7z$ z2_*~bFs{uJx;rttQrrr9wG(!-vJi?{xe9KL&}UOPrMG@kSR9QnG9_kqcl_gM;k=f&sDH6udqEti{EUO+7S zBD8T;bj7mDse9rx#Egg)P*S@fmi-ZWFpE>ZQ_u3qIV7J$$$bg29Ei}G^_=3UCm&^H zr75vj@|DDmoU%(jVJGL1tb&sET7HQw-x6IZX&qBfrOC=_C>eba%iajp3`bWi>rR5U zYAq{kprpKxSiV7T8R3+Gnm;yM{Ee+O%9~JjM5xA2P8q1?jmxoQL25<_pfV>hR4 zR`V`oaRU&`mI(d!ZFHrC^^CeUE-UMyWDG z!YPZ?FS5L*^d%JcV8ntkOoQW`a!b8cLsq_ml17MSO@#hB$tiu*d#&Z3e*={CcMuE4 zmf2^cE5)tie(hA6JhRvcC3Og5!8m#Hc}^LjzGorFvdP3U6bi=tgD!H)AoUG9S=kIF zI1D9*`Af&&qASI$fqre&Co5Z^xJDor%*9Im$ti=>SN~+?Yba?W5zCASmAt|!Z>ujD z%C)~0O7eS%WlDq&T;&vsvGzko83QFFLK|*ISBhFw{o0)>a*2hZq!#LAWClwSU+%I=$7V%wnvXQN&|!7n*3x`Mq|-!w&$XBIn5 zEb|b{#0Y&_fm7z_Dtph0d>53A#fW7ZMoQJAD zUd!&7CMrjuIAPQa=3;l+aLOYwD&IlLbP$UZp~s(&u9UMH#@kx$i?JMq65Iv_t8inU zjjoipD#dSClo_IO3`*J#lo(dw4nM~!zq++tYgsuCC3P1RtdG@wA-Yn*`q`~z5y;B- zP?C2;!OGQu7o#f`t^MwuN{P)9OYDSE_M%?CjnI~EoRX%N*etP!Icb#rP!31vTKDKm zC99lUtNoc`ET^Ej4?;N_q4GUByu}S$^{e4IVitk&tiCVrJD76O>JyBQ~d7#2qpDAluHr1GLlnz z*3?Eya@+q2O3F{Dmp>wO<2_CpR8wm&azAzvO4=`o1xm>eqAS&{6*UhlwJ$3_n|iqf z<~4 z{Cdfl#3|p`+Mp?7ESI3f-OT@u?U~9cKDTyeWR|G>4kh(AVnKT;Iy1Ub%^KrgA5$!U zKuN!gSboE(Zw{yY=GJE7ax8yB2^QRhd*CCKF`rXjjMqj=@;d)tMkxa2BIcEgqAOLc zJ@HyTzpUg!NiGiMLWEvf!YPdsv{Rh&THj?TITn;3B6MhZbmd-aW`cIcN>;8wNiPlM zY=oMx;*bIM~r z?Y;{+ck~97R8wNeRj{^2S1MYI)c28P<)(?n)IRoJkME4GRIp0;wHv5p=Gz3tX=du#3`=8bWQ0X zt&rS-5?2fg)?r>e8eJ)C)en56DPk=DK}jx*62qF(t0$r>aaQkuR$_7<>|H3S1rW;@ z5qjoKbft`y8PM8`thhGgm;4{T`&jFH@&`^i6wvO5nkCjt0Vo;&;=7M^%f>%NSMITH z#aR0Vp=91bELekm`q$`6X{%PEwlgBPmqJi-%&2cM#xQ?GS4vrrC2F-VD}|w?{EZUJ z#4q_Wr@WJ>&Md@wDFP)o7qQHZP_L`em6FyzRhca+MWN)4`DaEb{bqELkThViuGvqkM#~SP4#vOVZx3lVd3f#f^F~rv}R3!zsTf##UmbOud-e zABpq(<)SOatgVUKORVxQxil2>OLh&5kh>D6ypyQyyvxcxP|{7S92}uXs&Y#0#0;&y zh^?{=lsGd-ejEA8H96&EV7#Kp{a7579Q0%6yjW5Kr%VZaswrYDWlb#CP%r%=RK_1& zDQa~JX!Rmz5tM_HWxg-HB2>IKr<4z9-xpaa55>W^GT0NNzIvRp+do4oFph7COpO|eN{!y zA*pJVicns}>Do4&vel=p!^j!k)u6bm<|_|A%_*0C+RBxzRELsWGhg}pSx%|$*H(Mv z5~~3vH6g!Vjy=yQkE)7%nxm#s{Q1g+PMq?hf1}dF$Vx3J!P;mq_>J}H$|?Q*>aM7` z+T(_jQ9nPH=G{5vEx&d@zg+wAP%;}r!7n+XC#UrEYp2oW5=$`k@&J@h5xU)rQ@Z%I zQJ<`Mprkj2(jh{7`*F(Cer-ijR=iM>A4cuBMDzlto^!BTu&pG#^_-taLO(9W-z&4>OskP2C+22yfTAVV(Ve*n-XgerGA8V zP34rU>YZJ3EcZcicZ5<0C+uc&%Ae}(bMl+Q$xyO8qh4x9sNGynx#8QTeJjNi+zp^) zbwezPShddNlz-HblH89qG_j=RD`ysS%59&vD=I6Epya-quQ^+EyDusaLP>uI^@1Me$QDjnr+>vnr70Bma45LrZe5sDzVT~0B=Wqn z8I;_SD6#u-&SD#DidbR~LrMK8UwM8nr<4h3qdr-A z1WLyE{1R(@kW+dGv>BhQJPO5`2nFBDM-Fq!l0a;2zd4lLDfz##2aj>e(SX)l%A<=E zD9JOSG{i2?Nlx)69@Tmnu^(#zC3P+ow8}PTIpx(vt;FPSWlJb&nfX1;3+JONMXYm) z+P6|}m932O8Dc?i+2bOo{HE?%$jW1;#Fj%r&!6!dr>s!-EM(P-@^Ny<40z zJD}azCM#{BWUfPr-5a5C|8Yv&fO>0_*eahiCAJ<)#R$D!uu9auq#{;@fHqQ+*I=K5 z689xaEG|NwigHShKeiHk8cNDnP)easwm4;l`W2IR6WT$^-TG~W~R8C z@C=mnO{kY55jt9yQ&RleEdlax>{(MUTc8w((Bg`ma*sN%l$BH{&etfhI}VMxms5W5 zscl~zU9^Xi5kf4u2fk|!PQm?j%2!NYg?kQ4nvGbl;ysIaPMN93BKOJ9LvcqC3-0IN z;N_Iz`knS-iFJSyw;i$kiMKctIi;(*YAv_@jwY6!hz0K^bgs)OEqvM#ES5Inl)LK5M_K7?>SZ5F><5QlY{Dt$)VrbNU-FBl zUJf9ZGY(aKh*P%3c&~LAD9PUaDZ#2(~Mf)I(5?IW)K>r%X{}k>~r} zprju`i5+&RRBKKdq2AXgk6>Sdl6e%dWaD+Yww&^+dh4uQVlP9CBPHCt9 z#$=^Cl-v_g@RCuX_MCFRS6zb@`>`}AIj2x!yBr$Xfm1wc?aQ&e0wwM&V%g?Ulg^w{ zTD=oeuKgZRTservcF69^DFtF|FFj2x=b_+Dt{1v<$`$oqYq^Jc)x`26N(}Fdo$SFW z=hd6RWaTvz%SFV3x6(3tamwKsm0nPCenu>K_pN$AP6@?$hiY#qS-(QT8+L2o3P|~kK!Hb`7e#j}Wd$f7wY_aEm3rgG#lo(zwt@1Ia4D)EK zJ#sARCYGCs1uwB?PT-Wao(sy%Le5Wq+tkY~#4_HY7Lz#Ts^@P-k@NZnKuP--%13yI zcWQK{sMXYaLsP^&*nvHSwx=7`E5C|O3q%f%Jva>`zBu>yJbazwjn zA}^U&Rt7^!g<@V|-mrjEiuej?N+&6En4n~s+JDEP4;FJu6W<-J#6;yCD0#o+L3p!% z8K=zm-H1sU0wo!((uJ44ORVISOFr$FJXefmD3mPpFu^w*TDFE$8v6fGVv&_$P;yOg zi5J-CuH%#;{+~6avsC-TjbeKKR~;I+D8a7wYjaZM5H zGP-q+mSedcmRiU7Ye( z;8;vb5K1aWu)*i?cI{qH86MEiSKIpveUK_!-XV(q^V#mrxV z?Hr0b%qeqYR7OJyo<_Yq<6bj#l$cqC zYl-*7f8~^Ef!*3KSyaYB$=!hxYk?Qo|KOC-dT%NAV;`GXu+kfB?$D0QoRSvUuf-y^ z%JHUN%-Z;)4lTUODfb7onT7lt`^3bu8Op;@ZgNU-bx&Nb{Rt)(v&+-Wp^xrx$|b+n zTgu9(P~6z#b2W9SZ-ING?u!+*_Nuu(a*0iZlC~PLG;ye55l-3S*KVPaW66LLT!B~~ zzySqbHCPB$w0;RD-B};S44*xmjmpor=m6J`0EkcPkaOg@Lr|j|{(G;=0 zOo5WV07`v_wp8Ghy)i0NO^KO|h&mX-R^gN*er*ghUyNlMl+4*s0uD{6&ME)-wK0sW zOox&?6N(3G7j8~z68IsedYJ(wYX+2B4yAiJWn@6hR9PUF*i0xn)A38L=Fn@2oU%KR zqr|d6?D=Ow2^ys`UdOJ>DGd{~dXZz94J8#yu$)63k~!sv#1l#^nPQ2}fs$sF(hkjN z%qjJfVpHZq$udd_hgLkuDYcWnk13XUP?GT*a}{xD`NN!2DM|aj$R#!(ier=l4xLKj zloCk?lvw22UtsD5tTuiL}OepSIsF&+Dm21l>cM@Y8jW0Bc>Bs)I zDX|@=+)6y8l$bo@TLdL_4od7VDD64rpTyY4V~e4fp5JxJre`~H%GE^eD<+RFK7*1r zA0>9trdMC&l;0D-QA$i!mOx218Fv?Kn(`8-oKMtlIF!HpOQE>%-FKa{Y33`Oav@P` zFS4=>O3q@G*h!n5*Er>T;y$Is`$cT0gcx94W1U;w(cfhi$s> zCa2t1dww~V&y9kyW$>U)H{Rxy!b#fLQtmBR8U;Cuu6;IniBrlaomA>YR#rjDGNbWb zHa#$mQ|?c?r77Zfe>IfMwNSR%)NLfEbV<_I#xq4_4U`Nsf3a;E_CBYiCtZpumbFGf z_F!;}O>;ivluwc_$E0K#1v5U^2Ag(%%qcUH&cvjAVd`ZgVp(U?PoHv1s;-EuaO9i7ytZadjyce-d zv}yKwPAQ$F-8U{PUz=F=Lm6+=>W!R|oTTmY$jVj|%R!XbM>ZY$no~Ndy``*#jFOF5 zMq|uxbIPYlma<>DQ0<P*M*=8D&$&9h@>XN&6+siVY?GJ1E0#s=u34K2iJRMPe)t zl+0sL-m$62eomR8#v&^bC^=>~VW3S@vpHp6(kZ3AEb1byjclNX6!u^mv{=TKt3Z7P3;Q^u*^N?F-ylphdFPn#P4 zz$r76w3RDa*##vTyKk=UHog86r%Y30SuB>=H%9phCDz5JPk-fC%C)3Kr5}@(y-;$0gYt|`#jbHmO6|IuBK9!* zpk)1y5_{67hi-AoxY~YA=_O+IHyp}6%uy(rrd8Io>0TeF z^hwgrRxcKNm}5|KE<>qiQ?n#av68gAp=9Mal$5I|u`2kD)#H?{>KR_S#J-2(+<;Qi zrnCl}GBQ!y6_u3}P?B#$DQDAw`#Gh(y8j|8C!u8AMZLt?^h#4ssj03gE*5{ur=VmN z+ZPcoK4hnNaR?~nG!=R!8>8v){axM)zj#oiETf}lo;y8bu&!I z+H=avL~V}znYh~X1C-=Kh~;XS&UNIJPxW82sGNtA>Ow46!c?#er}R=!)-M*73s6$- zpjG}Arh8uIl)8zVmG&YlKbjIVIlY&{RJaGHTvqd87K`irKS6O_hw@99iudA_wE;C2 zQMm{u=WmqQ#W4NQms5HNw3A$NEI&iZG#RQF!gT0OP6-6GJ5?5odE>u8NjD>cO0H)~}7nJ`?NZHz;m17dsWEPls~K0sjf*mnHVjA5e15Ile<-s{a9}4DxF`iSjS` zPbfLaPz@dk)1|ST(m}oPKvw=Tv6$7KyAG^~teZfs%3@v4q3aX(p#s@N3^nS@|1Ewpmf!5~fOX zIVIPpSm|M8<=X#Z z39b%PuVtLl)VD!V!w%B?U7@-VPY}Av87?!xrS51UhUQ< zS-ENI#k7}&D6w^%ve2uo^k$0pklccjX4?MzFjd|VT`6u&j8XX)N|x!zW`}9VW=>h| z)$;llim}`_C5ArPH9btPggB+PPs>o1l{==y%=cw-nCeEND{>CYrbVdB`mw=LNR!ByK zsp_AcG9gCAG9^|L%CIm+E^|r`uQp4P>!l z6sGbwIi-$Q+Y^^-zciHG3MjFGVVZlJQ~vYB7Rxcp`_G6iS-K8PZp>6 zJijZ7-1C=%l3f$A^bFGr_i#$Q=Xy*^c__(l#L_)XXUaxb?y(X)>JG2i!&HD0j7Kcp z!qmGWr?m9^r^F)HOGPM70%GYLrULhJ%23Z;O%dl}m7wH!5KG4}jjhfp+w@pOr81OU zFJegzQ-YgQJl>+(ex;~XffDCOEbYQ%dpKo;SN)BNYq0kkC4gAkhUv8cr{s9G8xQ0) z*s7*pptv3nQ>i-9mC{xn-;Y}Di#sFLpkyXOX%VKM>vPI*-%l|q)uFhPP+|{#9%vB8y5*C~-!)KTHocD$~a#zCdC8Ag|Ar98>S-dIHjX+w4y8)XRlr;!KzRa!gM;7Q|kC8#H9F4 zyHGimG=`Z7Ewup5@$iF9HursIc1V}tfq)JP}PQ#Y<8EM!qj*ur;PDVj4766D4Av* zrf`@}j)<-lw?6UCQ z6eX4gqS6RTj#-8KH$)pg;*>GoOhuV5DvhB8aR*WGMu@tOkFFH6hI?nkq}&fB?+&7? zAu2MFQ+ldB|NK-bd+-4$CZ{)eIYggN=9K5W)3sPcrHN5Ye)1n7dTlzVG}lW^R33zq zYF4c;g(!Cxr_}e(RAQMYDovrJW4Ai^bBGqqfpFbx=XFrRs6txPeJ&de8Z0g1AiJuNp=jEJo-J^Y9=7_O80wr%R=|qTr zU&$#YybG0jk(EcGxUnx5JQ||+*K&$mEiqYX4kg{}BpwdY1M4~E0k1Z;l$8`HIY$x8 z!4PfVz$s1DFY;`$URprOFlTx8hN#~bPDzeYX$d98+|j)&L?y$V@`PIZv&2|hL5VY` zpSFi665*6PYAiEFXg zTg0+4M1Sq)lq4|8n^U&vygSe}Gp?&l9K4N-;DoHAK`w^~-7g5nruQHYM6*KjxYdqRYQ= zN_C&MawXTxvnCew$-yZhTJk5S)beSkIpkPUq2y&lWrXOhE1cq2e`7PmdT9?OXuimw zpdY)&DfQIeQdXWbipe4QI7CNpa!My(Na>U15_{g1m>Knb6rx3UIAx%3rJ~4h*LHxC zcP8-zd|wJwkGjWK)XMN_Zw$$;vLlo%lie{oM0JXA$`bW08@cCy!Ng*+JKjTkDb6X& z)mUVu6O;@yuN)PkFG_LBRy7v6#5zMUx6KDfglI$@r*!r&Q)*v+yY@w+n2hdWA?j3t zQ_krxev03hE>KdhL7@;eslq930@`m(?#H^CSdatn8W5uUs&h(uV2M^@Vl3UDWSP~T zH$zm+%_-diT5mZ`R9=FTclxPshzfZ*rENf~eOY-KO5PpauZ8G#fK!?VzKW^DxuKG5*++Na5EclHDUkcH>`#5ETU#oq o1tZf*j9F+@`uMOTVhJ^k8k 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 BtP=nL literal 0 HcmV?d00001 diff --git a/tests/test_app_registry.py b/tests/test_app_registry.py index 130dcff..f68ac2e 100644 --- a/tests/test_app_registry.py +++ b/tests/test_app_registry.py @@ -10,6 +10,7 @@ APP_REGISTRY, CustomDetector, MyWhooshDetector, + OnelapDetector, TPVDetector, ZwiftDetector, get_detector, @@ -26,6 +27,7 @@ class TestDetectorNames: (TPVDetector, "TrainingPeaks Virtual"), (ZwiftDetector, "Zwift"), (MyWhooshDetector, "MyWhoosh"), + (OnelapDetector, "Onelap (顽鹿运动)"), (CustomDetector, "Custom (Manual Path)"), ], ) @@ -40,6 +42,7 @@ def test_display_names(self, detector_class, expected_display_name): (TPVDetector, "TPVirtual"), (ZwiftDetector, "Zwift"), (MyWhooshDetector, "MyWhoosh"), + (OnelapDetector, "Onelap"), (CustomDetector, "Custom"), ], ) @@ -54,7 +57,7 @@ class TestDetectorValidation: @pytest.mark.parametrize( "detector_class", - [TPVDetector, ZwiftDetector, MyWhooshDetector, CustomDetector], + [TPVDetector, ZwiftDetector, MyWhooshDetector, OnelapDetector, CustomDetector], ) def test_validate_path_exists(self, detector_class, tmp_path): """Test that validation succeeds for existing directory.""" @@ -66,7 +69,7 @@ def test_validate_path_exists(self, detector_class, tmp_path): @pytest.mark.parametrize( "detector_class", - [TPVDetector, ZwiftDetector, MyWhooshDetector, CustomDetector], + [TPVDetector, ZwiftDetector, MyWhooshDetector, OnelapDetector, CustomDetector], ) def test_validate_path_not_exists(self, detector_class): """Test that validation fails for non-existent path.""" @@ -207,6 +210,58 @@ def test_get_default_path_linux(self, monkeypatch): assert result is None +class TestOnelapDetector: + """Tests for Onelap detector platform-specific paths.""" + + def test_get_default_path_macos(self, monkeypatch, tmp_path): + """Test Onelap default path detection on macOS.""" + monkeypatch.setattr("sys.platform", "darwin") + + # Create mock Onelap directory + onelap_dir = tmp_path / "Documents" / "Onelap" / "Activity" + onelap_dir.mkdir(parents=True) + + # Mock Path.home() to return our tmp_path + monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) + + detector = OnelapDetector() + result = detector.get_default_path() + + assert result == onelap_dir + + def test_get_default_path_windows(self, monkeypatch, tmp_path): + """Test Onelap default path detection on Windows.""" + monkeypatch.setattr("sys.platform", "win32") + + # Create mock Onelap Windows directory + onelap_dir = tmp_path / "Documents" / "Onelap" / "Activity" + onelap_dir.mkdir(parents=True) + + # Mock Path.home() to return our tmp_path + monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) + + detector = OnelapDetector() + result = detector.get_default_path() + + assert result == onelap_dir + + def test_get_default_path_fallback(self, monkeypatch, tmp_path): + """Test Onelap fallback path detection.""" + monkeypatch.setattr("sys.platform", "win32") + + # Create mock Onelap fallback directory + onelap_dir = tmp_path / "Documents" / "顽鹿运动" / "Activity" + onelap_dir.mkdir(parents=True) + + # Mock Path.home() to return our tmp_path + monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) + + detector = OnelapDetector() + result = detector.get_default_path() + + assert result == onelap_dir + + class TestCustomDetector: """Tests for Custom detector.""" @@ -221,7 +276,7 @@ class TestGetDefaultPathNotFound: @pytest.mark.parametrize( "detector_class", - [ZwiftDetector, MyWhooshDetector], + [ZwiftDetector, MyWhooshDetector, OnelapDetector], ) def test_get_default_path_not_found(self, detector_class, monkeypatch, tmp_path): """Test that None is returned when detector's directory doesn't exist.""" @@ -242,6 +297,7 @@ def test_registry_contains_all_app_types(self): assert AppType.TP_VIRTUAL in APP_REGISTRY assert AppType.ZWIFT in APP_REGISTRY assert AppType.MYWHOOSH in APP_REGISTRY + assert AppType.ONELAP in APP_REGISTRY assert AppType.CUSTOM in APP_REGISTRY def test_get_detector_tp_virtual(self): @@ -262,6 +318,12 @@ def test_get_detector_mywhoosh(self): assert isinstance(detector, MyWhooshDetector) assert detector.get_display_name() == "MyWhoosh" + def test_get_detector_onelap(self): + """Test getting Onelap detector from factory.""" + detector = get_detector(AppType.ONELAP) + assert isinstance(detector, OnelapDetector) + assert detector.get_display_name() == "Onelap (顽鹿运动)" + def test_get_detector_custom(self): """Test getting Custom detector from factory.""" detector = get_detector(AppType.CUSTOM) diff --git a/tests/test_fit_editor.py b/tests/test_fit_editor.py index 83a24e8..a8fcdc1 100644 --- a/tests/test_fit_editor.py +++ b/tests/test_fit_editor.py @@ -76,6 +76,7 @@ class TestFitEditor: ("tpv_fit_0_4_30_parsed", "tpv_0_4_30_modified.fit"), ("zwift_fit_parsed", "zwift_modified.fit"), ("mywhoosh_fit_parsed", "mywhoosh_modified.fit"), + ("onelap_fit_parsed", "onelap_modified.fit"), ("karoo_fit_parsed", "karoo_modified.fit"), ("coros_fit_parsed", "coros_modified.fit"), ("zwift_non_utf8_fit_parsed", "zwift_non_utf8_modified.fit"), @@ -85,7 +86,7 @@ class TestFitEditor: def test_edit_fit_files( self, fit_editor, fit_file_fixture, output_name, temp_dir, request ): - """Test editing FIT files from various platforms (TPV, Zwift, MyWhoosh, Karoo, COROS). + """Test editing FIT files from various platforms (TPV, Zwift, MyWhoosh, Onelap, Karoo, COROS). Includes test for Zwift file with non-UTF-8 encoded strings. """ @@ -172,6 +173,7 @@ def test_should_modify_manufacturer(self, fit_editor): assert fit_editor._should_modify_manufacturer(Manufacturer.HAMMERHEAD.value) assert fit_editor._should_modify_manufacturer(Manufacturer.COROS.value) assert fit_editor._should_modify_manufacturer(331) # MYWHOOSH + assert fit_editor._should_modify_manufacturer(Manufacturer.ONELAP.value) # Should NOT modify Garmin assert not fit_editor._should_modify_manufacturer(Manufacturer.GARMIN.value) @@ -190,6 +192,7 @@ def test_should_modify_device_info(self, fit_editor): assert fit_editor._should_modify_device_info(Manufacturer.HAMMERHEAD.value) assert fit_editor._should_modify_device_info(Manufacturer.COROS.value) assert fit_editor._should_modify_device_info(331) # MYWHOOSH + assert fit_editor._should_modify_device_info(Manufacturer.ONELAP.value) # Should NOT modify None assert not fit_editor._should_modify_device_info(None) @@ -214,6 +217,23 @@ def test_parsed_fit_without_output_path(self, fit_editor, tpv_fit_parsed): # Should return None when output path is not provided for parsed FIT assert result is None + def test_skip_software_message_for_onelap(self, fit_editor, onelap_fit_parsed, temp_dir): + """Test that Software message (ID 35) is skipped when the file is from Onelap.""" + from fit_file_faker.vendor.fit_tool.fit_file import FitFile + from fit_file_faker.vendor.fit_tool.data_message import DataMessage + + output_file = temp_dir / "onelap_no_software.fit" + + # This will trigger the skip logic for global_id == 35 because is_onelap will be True + result = fit_editor.edit_fit(onelap_fit_parsed, output=output_file) + assert result == output_file + + # Verify that the generated file does not have a Software DataMessage + modified_fit = FitFile.from_file(str(output_file)) + for record in modified_fit.records: + if isinstance(record.message, DataMessage): + assert record.message.global_id != 35 + def test_strip_unknown_fields(self, fit_editor, zwift_fit_parsed): """Test that unknown fields are properly stripped.""" # Use cached parsed file From 0e2efd28f44d2a5eb3ccfa66b07fb26c5d36bb59 Mon Sep 17 00:00:00 2001 From: Josh Taillon Date: Wed, 29 Apr 2026 07:52:59 -0600 Subject: [PATCH 3/3] refactor: clarify OnelapDetector docstring and fix misleading platform patches in tests --- fit_file_faker/app_registry.py | 12 +++++------ tests/test_app_registry.py | 38 ++++++++++++++-------------------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/fit_file_faker/app_registry.py b/fit_file_faker/app_registry.py index 868103c..b79f4c9 100644 --- a/fit_file_faker/app_registry.py +++ b/fit_file_faker/app_registry.py @@ -284,20 +284,20 @@ def get_short_name(self) -> str: def get_default_path(self) -> Path | None: """Detect Onelap FIT files directory. - Onelap stores FIT files in the same relative path on both macOS and Windows: - - ~/Documents/Onelap/Activity/ - - Note: The actual path might vary slightly depending on version. + Detection is not platform-specific -- checks for the English path first, + then falls back to the Chinese locale path: + - ~/Documents/Onelap/Activity/ (English locale) + - ~/Documents/顽鹿运动/Activity/ (Chinese locale fallback) """ base = Path.home() / "Documents" / "Onelap" / "Activity" if base.exists(): return base - + # Fallback for older versions or different locales alternate = Path.home() / "Documents" / "顽鹿运动" / "Activity" if alternate.exists(): return alternate - + return None def validate_path(self, path: Path) -> bool: diff --git a/tests/test_app_registry.py b/tests/test_app_registry.py index f68ac2e..bf88939 100644 --- a/tests/test_app_registry.py +++ b/tests/test_app_registry.py @@ -211,17 +211,13 @@ def test_get_default_path_linux(self, monkeypatch): class TestOnelapDetector: - """Tests for Onelap detector platform-specific paths.""" + """Tests for Onelap detector path detection.""" - def test_get_default_path_macos(self, monkeypatch, tmp_path): - """Test Onelap default path detection on macOS.""" - monkeypatch.setattr("sys.platform", "darwin") - - # Create mock Onelap directory + def test_get_default_path_english(self, monkeypatch, tmp_path): + """Test Onelap default path detection with English locale directory.""" onelap_dir = tmp_path / "Documents" / "Onelap" / "Activity" onelap_dir.mkdir(parents=True) - # Mock Path.home() to return our tmp_path monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) detector = OnelapDetector() @@ -229,15 +225,11 @@ def test_get_default_path_macos(self, monkeypatch, tmp_path): assert result == onelap_dir - def test_get_default_path_windows(self, monkeypatch, tmp_path): - """Test Onelap default path detection on Windows.""" - monkeypatch.setattr("sys.platform", "win32") - - # Create mock Onelap Windows directory - onelap_dir = tmp_path / "Documents" / "Onelap" / "Activity" + def test_get_default_path_chinese_locale(self, monkeypatch, tmp_path): + """Test Onelap fallback path detection with Chinese locale directory.""" + onelap_dir = tmp_path / "Documents" / "顽鹿运动" / "Activity" onelap_dir.mkdir(parents=True) - # Mock Path.home() to return our tmp_path monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) detector = OnelapDetector() @@ -245,21 +237,21 @@ def test_get_default_path_windows(self, monkeypatch, tmp_path): assert result == onelap_dir - def test_get_default_path_fallback(self, monkeypatch, tmp_path): - """Test Onelap fallback path detection.""" - monkeypatch.setattr("sys.platform", "win32") - - # Create mock Onelap fallback directory - onelap_dir = tmp_path / "Documents" / "顽鹿运动" / "Activity" - onelap_dir.mkdir(parents=True) + def test_get_default_path_english_preferred_over_chinese( + self, monkeypatch, tmp_path + ): + """Test that English locale path is returned when both paths exist.""" + english_dir = tmp_path / "Documents" / "Onelap" / "Activity" + chinese_dir = tmp_path / "Documents" / "顽鹿运动" / "Activity" + english_dir.mkdir(parents=True) + chinese_dir.mkdir(parents=True) - # Mock Path.home() to return our tmp_path monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path) detector = OnelapDetector() result = detector.get_default_path() - assert result == onelap_dir + assert result == english_dir class TestCustomDetector: