From fa971ca9beaf2a6e7cf41c52cae8cb835f9f69b8 Mon Sep 17 00:00:00 2001 From: Domenico Testa Date: Thu, 11 Jun 2020 15:50:51 +0200 Subject: [PATCH] Streamlining the setup process. - dependency version pinning - automatic `secret_key` generation - automatic config file generation - installing `once` script - setup instructions in README.md - adding an architecture diagram --- Pipfile | 17 ++- Pipfile.lock | 346 ++++++++++++++++++++++++++++++++---------- README.md | 98 ++++++++---- app.py | 75 +++++++-- client/once.py | 27 +++- once/once_stack.py | 2 +- once_architecture.png | Bin 0 -> 65140 bytes requirements.txt | 22 +-- setup.py | 27 ++-- 9 files changed, 459 insertions(+), 155 deletions(-) create mode 100644 once_architecture.png diff --git a/Pipfile b/Pipfile index 2631cb2..aa1671b 100644 --- a/Pipfile +++ b/Pipfile @@ -7,14 +7,21 @@ verify_ssl = true [packages] setuptools = {editable = true, file = "file:///Users/domenico/dev/once"} -"aws-cdk.core" = "*" -"aws-cdk.aws-apigatewayv2" = "*" -"aws-cdk.aws-dynamodb" = "*" -"aws-cdk.aws-lambda" = "*" -"aws-cdk.aws-s3" = "*" +"aws-cdk.core" = ">=1.45" +"aws-cdk.aws-apigatewayv2" = ">=1.45" +"aws-cdk.aws-dynamodb" = ">=1.45" +"aws-cdk.aws-lambda" = ">=1.45" +"aws-cdk.aws-s3" = ">=1.45" click = "*" requests = "*" Pygments = "*" +urllib3 = {editable = true, file = "file:///Users/domenico/dev/once"} +"aws-cdk.aws-certificatemanager" = ">=1.45" +"aws-cdk.aws-cloudformation" = ">=1.45" +"aws-cdk.aws-events" = ">=1.45" +"aws-cdk.aws-events-targets" = ">=1.45" +"aws-cdk.aws-logs" = ">=1.45" +"aws-cdk.aws-route53" = ">=1.45" [requires] python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 211a14f..cb3edac 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "41f21128886eed53df2092337d629cdfa157f5e32f93ddf8faf59d51c3b8da3e" + "sha256": "85a3f58b964eac12c6836bec87d6450451436d461081ec6f4d8ca5d96f7333ae" }, "pipfile-spec": 6, "requires": { @@ -26,195 +26,387 @@ }, "aws-cdk.assets": { "hashes": [ - "sha256:38866f982420c5d1a26dc8fb6d88cdd20d0fe6febb94994cfd80e60a1706aa6c", - "sha256:fd07d1b704aac06e05b45490a1a1fe86abf34c33e160b2a5ecabb7696e1b0c83" + "sha256:20c78b332f9134cec0a1f4b90c981b596c0997c03908a269d7a9ec902f2d957e", + "sha256:3128cd3a44f2dea9165f4c424f2925bfaea8e0f924a0c3c8e285068e95d454f2" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-apigateway": { + "hashes": [ + "sha256:297bd75142d206a4f8ec6882ead8fde6ab0c1cda9995dd76e77a4cfc3203ce6e", + "sha256:6d8e3436dc466ce677228f6bb14c7650f48b272a8ca7d4938e1ec48b36e3780c" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-apigatewayv2": { "hashes": [ - "sha256:c1d21aec9633556f85d89662bd1793b87caa65081ed50fb5c3805e8f4cc809cf", - "sha256:df54154ce6acc664f3ab93d0875c824d0bee10fd38d184f5f4dfc09b2309d473" + "sha256:61a0e44b2dd593fab42364d0e0bb3245fa70e164dc25fd94d5cc64e114d442e0", + "sha256:7c6822259ea779ae48b1d050d2e05641556a848e1ae4ba2f62743574353c6689" ], "index": "pypi", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-applicationautoscaling": { "hashes": [ - "sha256:2eea9539f8068f162066ce1b5e2f63bc2e3c359ac278666e84ed9e8962716c7e", - "sha256:ff38d2a0c88848147a4cd7eb698d0e954d752435a68fc0493bb21e57ab7e81ac" + "sha256:211b43bb9667dbc3ec62c7f743fef9a02588310b62f544c35eb3492846a84230", + "sha256:6dadbb5f81c5a0816e59c290b0139333683db2e87aebd736bef6e31c52300468" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-autoscaling": { + "hashes": [ + "sha256:5d9bf9109c4f26bb6f02fefbf08da5b65fc9aad033e5a6c67922a0b4f922df43", + "sha256:c35de70bdb63fa6baf6d43dc9da436ef548e638905eb9a2c43e54e79eb90184b" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-autoscaling-common": { "hashes": [ - "sha256:c72dcb3ad1b30820ae46e49a3f5812a2c31f70a054a4b224905c4cf26c2217aa", - "sha256:fd0292e2924f60914407c580da69c827c9ce5b4efdfdb033ae4b4996c1c1ad9e" + "sha256:c0e33bffac360e9144964a9cce260a80acfb1a62ba64efc56b7bfc32f499863a", + "sha256:f7ed4878182d90b0e4db3094cb76abaa3dfc70ec5b3cee16eff518515e360092" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-autoscaling-hooktargets": { + "hashes": [ + "sha256:3b445bf9906daa29d794419acf9a2371d2faadba2d46be5d8fd6cc8e168b2bad", + "sha256:87a82a2be98dd80a21bca9213de14a99cdf1e015248c868c2792348b2f33e8f3" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-batch": { + "hashes": [ + "sha256:2bdefe2c7ee793b70cefbbcf252172968f74635e173660ecc153fd7334839597", + "sha256:c65d0c01cf9ef5f5c128f469840f61fe9e6f6e8639cbe0cac9c4d697d1f22024" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-certificatemanager": { + "hashes": [ + "sha256:23e23fb077c4523a7d9c4530e9968e1f2aaf991392ece18c174922e738c1dea7", + "sha256:bab5a2cea794674b78fe691ad85daf8849f2a4667ffa250de5a2bc9043afcf9d" + ], + "index": "pypi", + "version": "==1.45.0" }, "aws-cdk.aws-cloudformation": { "hashes": [ - "sha256:89ac7fd0fee061e7843aed6b03ca4bfc93eb645c64694a24868ee2b4c7982e9f", - "sha256:d91ca34e399c542cea35717d6490550ccbbe2b187e4d364c714ba5bf5aae8d1e" + "sha256:48d60f6e26dbc9804ac18c51aa73d60842a08ee37c9d8ff54f22f50248ea7bb2", + "sha256:67ca3005557990775fff55eaca0e6a6faa7a28462d7192a85438203760dd4b40" + ], + "index": "pypi", + "version": "==1.45.0" + }, + "aws-cdk.aws-cloudfront": { + "hashes": [ + "sha256:a15b43e83645c1f00813b27f3a99d83bd3e77ac0070d1bb98141be474bc8fb8f", + "sha256:d7c1039945c6a0c5c4771afef08ae30ccaf37db21c6b82ad6a7db3648246f224" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-cloudwatch": { "hashes": [ - "sha256:24d1b6d6ecd16327c24f906d31795a1822bdb08bbf67b0c59e9803d0b3801880", - "sha256:41ceba4eb2f8646de7296616289053f82a715317840fc962c2dba638308ad8f3" + "sha256:7d94bb25a46892ea0e827c844bc0bedc70b8031ef48955641c5d184eb6f269bc", + "sha256:97602eeef05a09e350fcfc7bf627505b1630e8b8393b82072c386d1f74560857" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-codebuild": { + "hashes": [ + "sha256:7bde7fb4e1d033b885dcba4bbd55136c1cee417e3ada8950c93b8765b588e273", + "sha256:ead186ecdf8c39062cfd95cf3e9a2cca8e8e6a5a4194bc15b4f4a4a6f43cfdca" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-codecommit": { + "hashes": [ + "sha256:0783e9ea449c208bde96a945990d21d913e9f3c532fc117224d1f6a4b595d7e7", + "sha256:58e4a3ec4ed94367a0ebfac1caa58fba7152fc80ace7567b069b733117a571ab" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-codepipeline": { + "hashes": [ + "sha256:17de3c5117d3393ccc3f683e7108f02c60f447bcc009040d21589fdae357e7be", + "sha256:61b1a16e42731c5590307556b8859c7989471147d81497e8139a4e4f10911764" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-cognito": { + "hashes": [ + "sha256:5177efe18088dc8029976a2bf648caa3b20773ab290e5a6bb81da2d8e9439ca8", + "sha256:870ca391ba0aefccd5d5246d6804e75b1bd4eb4d9e3c5791fa2b92ea264bc75d" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-dynamodb": { "hashes": [ - "sha256:4c94ae9b43749ed616c82372cfb6e26a5cc2c6928badd8433cf455d38d0183d1", - "sha256:5b6394360d903918d2d9f381ac9e9b8c3198a92bcd591e3b54ce00c743ee82ca" + "sha256:540ae28d1832dfe183b19377d97ff7b4a10b6c3dcdab4b7741e5f6443963ceb2", + "sha256:941a5c88160588f65b5a5481a8b0600844c4b839998e287ae18eb43fc9a1c500" ], "index": "pypi", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-ec2": { "hashes": [ - "sha256:0cbfe1ad838849bebd2dfc2fb18f6ab120525617e7d1195b2cfa12adb00eb28f", - "sha256:78e24d71496c01b172adf231e4b156e46dc37a513906d87616e108d9998dbffb" + "sha256:45ea01ff8bb055333f5987140c13ba49b0a07658515b147f0eb4f5c7d65dca94", + "sha256:87674a4724e41fcbb3e51a77f0cac29745bb2bb917341aba5b454bc156540ee3" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-ecr": { + "hashes": [ + "sha256:ca76cc9a4321544097962cc8c7fb0d1e41307fcf91427768f5356741114b2009", + "sha256:d73c28c618e2417002c6ef3dece5de630a7678eea4c845cf080e9a540de3b8a4" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-ecr-assets": { + "hashes": [ + "sha256:34896d4b6f275c368d4e65921ecc205bc9180e934d40678b87fdd1d3a136f93b", + "sha256:a24407c8dde824c1f0558cc5b166d421efaf57b96612957816334c25f9465a83" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-ecs": { + "hashes": [ + "sha256:11eee2c5a026fb349699413e99c36bb0cd31fd06f673f8f5a26a92bac8fa91cb", + "sha256:85163a64ad2e2308bc79b4cb9100ea9ad27e9bbbfe05f7df0e1bbf59a6b9e71d" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-elasticloadbalancing": { + "hashes": [ + "sha256:06fd8232eeb51952e31f55a38bae02d937ddb5c11829630691100fa55d9479e3", + "sha256:fef69ff521c63e62b5829697fc306cfb17ad38639caa3b2ca674b95b8861ccbf" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-elasticloadbalancingv2": { + "hashes": [ + "sha256:00ac6d6cb779df5425e9f724464818aeb85d7fb86cf47061f1345fd01262a35a", + "sha256:21432ebc23700b6776d9e0b0e0ee7ce565aea4f6644aa01a3eea90e32b55f3eb" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-events": { "hashes": [ - "sha256:9a273514ac6f3070435f83efadd154b3a367f99c43c9eaa2729eae348ac66f40", - "sha256:e490e508461d6f13424fa1b04d581565c9ce9e3eaa5ea7bc2d50d4965f710a77" + "sha256:59d046aa1a2c5e04a8655981d0a8fc1ac1320b66d0b373fb5d91b3352a21448b", + "sha256:6d6a52ca4860bfbaba620a5761904a3ffec8d86a6399f3dc232e04164e9d2f9f" ], - "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "index": "pypi", + "version": "==1.45.0" + }, + "aws-cdk.aws-events-targets": { + "hashes": [ + "sha256:8fe20271b73be9334922e94639858061499184c2dde11986fe849b54a9fdbdf2", + "sha256:dfe372641c833c9a911c9e8ea4720ef34ba568cf52a188599eb47e02c00e4e35" + ], + "index": "pypi", + "version": "==1.45.0" }, "aws-cdk.aws-iam": { "hashes": [ - "sha256:b1372ac47af2399d97cae3c94e5e3800211e39b89dbf58467b62462b3e4b40d8", - "sha256:fcb11db48211113554a7fd0c17ebc239226cc6bcfed96fa8c740efd76e252a6d" + "sha256:a4db816cbcd3642caf741dc934e8d2a09cd4e7ecfee1b325da28cfbf23318a25", + "sha256:e94b635244ba8f4595e7ec5c52c0cc84e23226f92ef2d9c7c87a8980b66acfc7" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-kinesis": { + "hashes": [ + "sha256:199c976e03d0521f0127842a78e1ce4d1ba9ff4a336310c8ad32831af0040f5d", + "sha256:ac44a1c8b8b7ab105b4911397047af037e8596ca6da8b3efdba3b040718f4914" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-kms": { "hashes": [ - "sha256:ad2cc8426c384681071164842bc3b6e7f298258dad794553d4016a2b8e1f7b39", - "sha256:d1cd82adab05f6ac9dda200e7305b7c142e62d5c803610cd7fa147a8b2ce5c55" + "sha256:3949d3deeae0718d91acfec33e251959b980505a350cbf2de366b113fcd9ce0c", + "sha256:84780491977946ed9c21d35d70bbca21220b72ef27f0c2c56450235446126e9c" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-lambda": { "hashes": [ - "sha256:f209f53c8324f92cbcdb124db63b6f008fcc496fc57f56ff953d68841b385554", - "sha256:f2116c7184de3d0f521016d042b21f9351f8af54fffc2691190eae95a708733f" + "sha256:3120ff8b58541b9102651899832286fbe5cddd54e2a9dcebc984e08d59d03286", + "sha256:f77ca23ce9c0ba3187e91541286dd14b6fc8e22f87f7aecdbce1c79755146113" ], "index": "pypi", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-logs": { "hashes": [ - "sha256:7d2d33f7f90800dd93e6159e67c119311c52534b8618d96766423af230a7b667", - "sha256:f4a52f8b834c96825938c8a70eb190924e64bb52e4896ecbc3aeeb48a92dee6d" + "sha256:9db12970b2303431de70502e925515e5734e2c5e0d68c5aadba63a1595cbd076", + "sha256:be1b892cbc7bbfe7fe67823360f39c2c0f76650d0a24760091b718b37ecec638" + ], + "index": "pypi", + "version": "==1.45.0" + }, + "aws-cdk.aws-route53": { + "hashes": [ + "sha256:7ee747898b9df6c24332b68937b96b17316dea31b3a67d7d36763c77cab1bc9f", + "sha256:c4e6a80c7042f4216c198fbe3a1153e91c7228f938510f8301e9365e811069f7" + ], + "index": "pypi", + "version": "==1.45.0" + }, + "aws-cdk.aws-route53-targets": { + "hashes": [ + "sha256:db5f5c62bd0089ef38c4383423bc06c3337a379ec47d0ee2da28034bae8c8fab", + "sha256:e9af2e9e013579a4ccc5036e28fba7b979e8bcc6853873b7f79d21b22bbc0cad" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-s3": { "hashes": [ - "sha256:9c4411ea3f0efa4b5359d2681b2b60440fc8f43972862d16b66cabea2b1ee8a0", - "sha256:f3fec357cf831c95e22fd1ae6666524f2493a99cc125e86f784b28040694e4bf" + "sha256:16239f51ffb80820ae0c103f053ceb41a765dd5efa4f739a33352d093afc8698", + "sha256:64b261b88a0232cd09b05c7219e19ee8a59fb478ca2cb8b54ad728f1c421237e" ], "index": "pypi", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-s3-assets": { "hashes": [ - "sha256:43cb7c804976140ad04ab456f595d4cad7c713feb3b78a678eb8e0c68d4a6dda", - "sha256:b4a69afcbe08e8ff6bf06508fbcb9ca7c253723b2df79a88fba69612c767f852" + "sha256:1ba312dbb33243aad60e0d7e634e801c045308eec2cbe73a7dee642bcc29994e", + "sha256:d5d61e8a54059b8c2ac986785d89012bbd3a97379925e07d7f7579a430b91e45" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-sam": { + "hashes": [ + "sha256:c974f21e8104698395e0820951bfa9243e5ed990368a250935b4bc1964a2bc1d", + "sha256:d0a852e2638cc2d7aa1562ea3cad382ab70925a8bbbb277889e1150c455acbf7" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-secretsmanager": { + "hashes": [ + "sha256:10445782936ee2d699745fc455576a39c5612d40e01fc5e793e9fd72efa685a3", + "sha256:159dae0fcce7fa1458c6d4e152e615ab85024190d74dbd2022e38e9eb0d30b98" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" + }, + "aws-cdk.aws-servicediscovery": { + "hashes": [ + "sha256:5b00f7eda3029c0d322da94ecd5d9e5dbebc2cc52e507d329342d0db2f6d2e88", + "sha256:d006da251dd61eb63664a37dec417538907f4c25c8ded6467b0bb151754fa8eb" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-sns": { "hashes": [ - "sha256:235467a8a9b1f8824d967201f932231f105dabababa09384c0ad68e2bf26593a", - "sha256:5df1e11298b55ad4d1c2fa6d11143e73ecbae8311b1cc78cb402c62d2e5b4e4b" + "sha256:732f3ce0c193b84027a2a6df7ad6ab7b7ba3eade0ecb2c6effa8403990575119", + "sha256:8f3f4b26deb8846e167ee9e8d044265c4ed2e418e44d9cf7db4954e7b04405d9" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-sns-subscriptions": { + "hashes": [ + "sha256:8b6270507c09eb50fc3c8f2cf40aa4c6ac0250535a603a19b8935e1e0ec8007f", + "sha256:d80c4bdc3ed7c9c16ff45ff4e944e392ec61a222cb101a29d806b91f72d044c9" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.aws-sqs": { "hashes": [ - "sha256:29ed98a3f447725e3a8f8542295696d5ba41dbce708d6acf87f65796e699b97d", - "sha256:f90a98d73918e986127e8bb32ab27b94890f6ba2b72f2c6b847511a480b484bc" + "sha256:16e148f6dff195e821053176c2817bdc7cf6693f1ba9d51ddb7739c8d5d7e54c", + "sha256:a6276ee9235f094b6bf718edd9dcd639844ee1e5f96c772dd33100a14e6b3de3" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.aws-ssm": { "hashes": [ - "sha256:59d7bec1fbc9d3795ec2376f1cd50d34e789c3d04791364c6b15200d426d17c5", - "sha256:9ebb5c0729560a72d5982fae874125758dbc81bf379bfd532272479dc989e5b8" + "sha256:30775b64b5b3a6974854170406e268061d1891f606d98f9bccdfccc87a3d5a0d", + "sha256:6e1a91b268dcec24d53b8699f468b6440b71bb76dc141ef5e66480f523e24e75" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" + }, + "aws-cdk.aws-stepfunctions": { + "hashes": [ + "sha256:57076364fa525de48659689fa0fa22f1f9d5a223515d969405a2285ac7cd679b", + "sha256:7895371bf34e4c56846e54ece1c505d31d176948ff7b50c33865d59f6da9c1d8" + ], + "markers": "python_version >= '3.6'", + "version": "==1.45.0" }, "aws-cdk.cdk-assets-schema": { "hashes": [ - "sha256:056a1612932a35cd05b252d346403c7861fd4cd2475c6dcbd2da84ead2392b27", - "sha256:58b0e2fcdc43da88ef605a595c4d546f442f060606adfa8986bc3d0a15ab26f7" + "sha256:21222ba4d02d6db2c4abf40884e7a43910084cfc57d16c50a4441a1058ba2ea6", + "sha256:f0a95af77c2884e49ef6d64c41bb79b0f3ad4f6ee95b8bddba8f928258a83a68" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.cloud-assembly-schema": { "hashes": [ - "sha256:0f3b8cfbe8eeca7e29283a01d4c55921de0b412c71e845b105ee676f37981575", - "sha256:d71cbee43f5d60f67c72cacc01d07676af83f373cbb938b22fdca55859c18a85" + "sha256:458f80265a9f520d63d1fd755ae958f3773fb4d6d932b0ae31bda1fb5ee90b08", + "sha256:caf1e24a963c28171b29d523ae81a5a64b8897c95ffe9cb04b574dccf6af194f" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.core": { "hashes": [ - "sha256:589ae5c0fecb5aa5ba59873da1e6a51b49b85c2cfa35b957c8a0734adc1ed510", - "sha256:948cb0c63149a3d0dba739047a2705149c8ae4b69e725ade9c4f0f5b168a3673" + "sha256:1f7fbceb0b02064f4349f6ad7030247d2a422cddf035847da1d479ee7c9d18e3", + "sha256:f224f584df1a2ce354bc42ba7d0c870d08bcd53c3f56e9d4b991ab2be08d6bef" ], "index": "pypi", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.custom-resources": { "hashes": [ - "sha256:20a27c05624c8c047e5dd3fef84242c1c17c9d1440767d40b43889176cfd90f6", - "sha256:e972f346e61069f345003caa42defe274b348359a7b07304246dc7dce14411d3" + "sha256:05bf20b40a2ddcf364a538eb8c211160d84c909f485c83ef585cc511f0b9292b", + "sha256:920449820eee21fd644c18d3bf026a7e850bbd1cd01d57ed25f892d16f56b1ab" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.cx-api": { "hashes": [ - "sha256:04af7a26bd19e28115fdf8bf78d02bb08d1c13786932026003ecd1c7c5d2bf88", - "sha256:af386b4d6d9e26809c87be0d71af1183f9f6a338a060a393e8cf233b99b6f792" + "sha256:2ea1fd39e855f6ce8f1f5465d4215061946e09ea6b2105929331e80ba04f636b", + "sha256:2eb53f791ab9b2dbc6be564c3aa125aafb93224f3e85a299c11d41721f4d72c4" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "aws-cdk.region-info": { "hashes": [ - "sha256:09079b8dd2b69299a13312515bc465f2f43a7a400b4a334216bb4148b7323605", - "sha256:c4caa4c2ebf7cd9959105038972ddfae2c827c40976afdafec47a97cdcfef514" + "sha256:516cdeabc1104d19e6c5b7a40a4cdf80c00e98d997cb08781892d183d1ec7064", + "sha256:53e2ba371ae574cde0b0c09782d26faa9e5f40bdf323784b2b250386a1cb2f32" ], "markers": "python_version >= '3.6'", - "version": "==1.44.0" + "version": "==1.45.0" }, "cattrs": { "hashes": [ @@ -225,10 +417,10 @@ }, "certifi": { "hashes": [ - "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", - "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" + "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1", + "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc" ], - "version": "==2020.4.5.1" + "version": "==2020.4.5.2" }, "chardet": { "hashes": [ diff --git a/README.md b/README.md index 737568b..5d95cce 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,81 @@ # once: a one-time file sharing personal service -It happens that I want to share a file with someone which is sensitive enough -that I don't want to upload on a public free service. - -I would like to have something like transfer.sh, running - as a personal service, with the following features: +*once* is a personal cloud service that enables you to upload a local file of any size and get a link in return. -- it must be serverless (I'm not willing to pay except for the actual file storage, and only for the time strictly required) -- it must return a link that I can share to anyone -- file must be deleted as soon as it get *successfully downloaded* -- it must expose a simple HTTP API, so *curl* should suffice to share a file -- it must be protected with some form of authentication +This link will allow *one single* dowload operation, deleting the file once it has been successfully transferred. -With CDK I could create the following resources: +*once* is designed to run on AWS using only *serverless* components. -- An S3 bucket to host the uploaded files -- A Lambda function to implement the 'get-upload-ticket' -- A Dynamodb table to store the information about the entries -- Another Lambda function to implement a "smart" download handler, to delete the file after the very first successful transfer. +![Architecture diagram](once_architecture.png) -I will use API Gateway to expose the lambda functions as an HTTP API. +It can be easily provisioned to a private AWS account and it has been designed to have a negligible footprint on the bill. -HERE BE DIAGRAM! +## Deploying on AWS -## TODO +*once* is implemented using the [AWS CloudDevelopment Kit](https://docs.aws.amazon.com/cdk/) framework, and can be easily deployed as a self-contained CloudFormation stack to any AWS account. -[+] Publish it to a custom domain name: DONE -[+] Set logs retention policy -[+] Deploy custom domain as a nested stack -[+] Mask link preview depending on the user agent -[+] Add a robust authentication method +Make sure you have installed the latest CDK version for your platform, following the steps described in the [official getting started guide](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html). -- Add progressbar to client -- Package application as a click app +Install the required dependencies (you can use a virtualenv for this), with the following command: + + pip install -r requirements.txt + +The deployment can be then initiated, from the project root directory, with the following command: + + $ cdk deploy + +The output will include the base URL to use the service API. + + ... + ✅ once + + Outputs: + once.baseurl = https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/ + +Update your configuration file (by default it can be found at `~/.once`) adding the URL +under the `base_url` option, like in the following example config file: + + [once] + secret_key = RBeXidk41E1lmB5x839sVjo..... + base_url = https://rrjvo2i9s5.execute-api.eu-west-1.amazonaws.com/ + +### Using a custom domain (optional) + +If you want to expose the once API on a custom domain name hosted on +[Route 53](https://aws.amazon.com/route53/), you can just set the following environment variables before the deployment: + +- `CUSTOM_DOMAIN` the domain name you want to expose the once API (e.g. _once.mydomain.com_) +- `HOSTED_ZONE_NAME` the Route 53 hosted zone name (e.g. _mydomain.com_) +- `HOSTED_ZONE_ID` the Route 53 hosted zone ID (e.g. _Z0113243DF12WNGOIXX_) +then the deployment command would look like the following example: -- Write a proper README with instructions -- Record a demo -- write tests with pytest + $ DOMAIN_NAME=once.mydomain.com \ + HOSTED_ZONE_NAME=mydomain.com \ + HOSTED_ZONE_ID=Z0113243DF12WNGOIXX \ + cdk deploy -- publish the source code -- write a blog post -- add a link to the blog post in the README \ No newline at end of file +If you need more details about creating a public hosted zone on AWS, consult the [official documentation](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/CreatingHostedZone.html). + +## Uploading a file + +To make the `once` command available you can install it using pip, with the following command: + + $ pip install . + +Once the service and the client have been correctly installed and configured, you can upload a local file running the `once` command. + + once + +The URL can be shared to download the file, only once. + +## Uninstalling + +If you want to completely remove *once* from your AWS account, you will need to run the following command: + + cdk destroy + +then remember to delete your config file: + + rm ~/.once diff --git a/app.py b/app.py index 7e7ce83..6948331 100644 --- a/app.py +++ b/app.py @@ -1,23 +1,74 @@ #!/usr/bin/env python3 import os -from aws_cdk import ( - core, - aws_route53 as route53) +import base64 +import configparser +from typing import Optional +from aws_cdk import core from once.once_stack import OnceStack, CustomDomainStack -SECRET_KEY = os.getenv('SECRET_KEY', 'ho/KbLqa65F4uKumCOl30SQwWh4hV7BqpuJVl7urq2XuxkvHmBk/QC9l53og0B3X3dSZun7zDYBH6MdOkjj6CQ==') -DOMAIN_NAME = os.getenv('DOMAIN_NAME') + +ONCE_CONFIG_FILE = os.getenv('ONCE_CONFIG_FILE', os.path.expanduser('~/.once')) + +SECRET_KEY = os.getenv('SECRET_KEY') +CUSTOM_DOMAIN = os.getenv('CUSTOM_DOMAIN') HOSTED_ZONE_NAME = os.getenv('HOSTED_ZONE_NAME') HOSTED_ZONE_ID = os.getenv('HOSTED_ZONE_ID') -app = core.App() -once = OnceStack(app, 'once', - secret_key=SECRET_KEY, - custom_domain=DOMAIN_NAME, - hosted_zone_id=HOSTED_ZONE_ID, - hosted_zone_name=HOSTED_ZONE_NAME) +def generate_random_key() -> str: + return base64.b64encode(os.urandom(128)).decode('utf-8') -app.synth() + +def generate_config(secret_key: Optional[str] = None, + custom_domain: str = None, + hosted_zone_name: str = None, + hosted_zone_id: str = None) -> configparser.ConfigParser: + config = configparser.ConfigParser() + + config['once'] = { + 'secret_key': secret_key or generate_random_key(), + } + + config['deployment'] = {} + if all([custom_domain, hosted_zone_name, hosted_zone_id]): + config['once']['base_url'] = f'https://{custom_domain}' + config['deployment'] = { + 'custom_domain': custom_domain, + 'hosted_zone_name': hosted_zone_name, + 'hosted_zone_id': hosted_zone_id + } + return config + + +def get_config(config_gile: str = ONCE_CONFIG_FILE) -> configparser.ConfigParser: + if not os.path.exists(ONCE_CONFIG_FILE): + print(f'Generating configuration file at {ONCE_CONFIG_FILE}') + with open(ONCE_CONFIG_FILE, 'w') as config_file: + config = generate_config( + secret_key=SECRET_KEY, + custom_domain=CUSTOM_DOMAIN, + hosted_zone_name=HOSTED_ZONE_NAME, + hosted_zone_id=HOSTED_ZONE_ID) + config.write(config_file) + else: + config = configparser.ConfigParser() + config.read(ONCE_CONFIG_FILE) + return config + + +def main(): + config = get_config() + + kwargs = {'secret_key': config['once']['secret_key']} + if config.has_section('deployment'): + kwargs.update(config['deployment']) + + app = core.App() + once = OnceStack(app, 'once', **kwargs) + app.synth() + + +if __name__ == '__main__': + main() diff --git a/client/once.py b/client/once.py index d5b2e94..ed2c7c4 100644 --- a/client/once.py +++ b/client/once.py @@ -4,6 +4,7 @@ Simple command to share one-time files import os import base64 +import configparser import hashlib import hmac import json @@ -16,10 +17,9 @@ import requests from pygments import highlight, lexers, formatters -ONCE_API_URL = os.getenv('ONCE_API_URL') -ONCE_SECRET_KEY = base64.b64decode(os.getenv('ONCE_SECRET_KEY', 'ho/KbLqa65F4uKumCOl30SQwWh4hV7BqpuJVl7urq2XuxkvHmBk/QC9l53og0B3X3dSZun7zDYBH6MdOkjj6CQ==')) -ONCE_SIGNATURE_HEADER = os.getenv('ONCE_SIGNATURE_HEADER', 'x-once-signature') -ONCE_TIMESTAMP_FORMAT = os.getenv('ONCE_TIMESTAMP_FORMAT', '%Y%m%d%H%M%S%f') +ONCE_CONFIG_FILE = os.getenv('ONCE_CONFIG_FILE', os.path.expanduser('~/.once')) +ONCE_SIGNATURE_HEADER = 'x-once-signature' +ONCE_TIMESTAMP_FORMAT = '%Y%m%d%H%M%S%f' def highlight_json(obj): @@ -31,19 +31,34 @@ def echo_obj(obj): click.echo(highlight_json(obj)) +def get_config(config_file: str = ONCE_CONFIG_FILE) -> configparser.ConfigParser: + if not os.path.exists(config_file): + raise ValueError(f'Config file not found at {config_file}') + config = configparser.ConfigParser() + config.read(ONCE_CONFIG_FILE) + return config + + def api_req(method: str, url: str, verbose: bool = False, **kwargs): + config = get_config() + if not config.has_option('once', 'base_url'): + raise ValueError(f'Configuration file at {ONCE_CONFIG_FILE} misses `base_url` option') + + base_url = os.getenv('ONCE_API_URL', config['once']['base_url']) + secret_key = base64.b64decode(os.getenv('ONCE_SECRET_KEY', config['once']['secret_key'])) + method = method.lower() if method not in ['get', 'post']: raise ValueError(f'Unsupported HTTP method "{method}"') - actual_url = urljoin(ONCE_API_URL, url) + actual_url = urljoin(base_url, url) if verbose: print(f'{method.upper()} {actual_url}') req = requests.Request(method=method, url=actual_url, **kwargs).prepare() plain_text = req.path_url.encode('utf-8') - hmac_obj = hmac.new(ONCE_SECRET_KEY, msg=plain_text, digestmod=hashlib.sha256) + hmac_obj = hmac.new(secret_key, msg=plain_text, digestmod=hashlib.sha256) req.headers[ONCE_SIGNATURE_HEADER] = base64.b64encode(hmac_obj.digest()) response = requests.Session().send(req) diff --git a/once/once_stack.py b/once/once_stack.py index 600213a..49c87c9 100644 --- a/once/once_stack.py +++ b/once/once_stack.py @@ -102,7 +102,7 @@ class OnceStack(core.Stack): if custom_domain is not None: api_url = f'https://{custom_domain}/' - core.CfnOutput(self, 'api-url', value=api_url) + core.CfnOutput(self, 'base-url', value=api_url) self.get_upload_ticket_function = lambda_.Function(self, 'get-upload-ticket-function', function_name='once-get-upload-ticket', diff --git a/once_architecture.png b/once_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..6035707957551d204e60274d0129c68d9ea88e73 GIT binary patch literal 65140 zcmZs?bzD>JA3sb9SSX?>p&$~9fb>S!SkN$PLvo|XHeketq@tvhfPjEfN=ZwXL5P5W zw3Kv%q~vqF@B8_^e&6Ty{IL_)IoEZr>;38XISbX(fn7YuevXQY>LLOTF`%NNt*4@* z9%G~jT6#X*nxLXO+euP4CAoOpIpS=o_$1W+J>nA=bs)Ht_#_~F;^Njsq6pT}+TP9D z#a#q%O9GmJ>n;SWqn)EI_TPKNMa4yhMWuyBWemk6_#{*$LBK{FBqAX$jsEw3YkOP# ze*;R1hynxL)waes;zbK*16sq9T%F zq7ou9AfO#-ZD;M~_HwnuK?mI8_#ar_D3Ypj8KPi~TzY$e4)6$PlEVdXi##+8UDHb`XS%o1viu%o-^U z!V}fWI_@Z}Ap+;9r=?=2cC)rDi=}0^2>yy>V6oRWBNg7VEGeLS2TM?J&j$ zBrR_TxGRdJ4`85YqGhUw(YB{xwcXsj3`GgzE*JxEygJ1mkc6rxQQy#0RnHW!igdLn zS{ot2a19Jh&E1-SfI`vIS_m(1O-TaT0D`xH8rhOu)I>dD9_n^bF9QiJ4}`HRAVjFT z5!hW@9Iq#aw;@}LQov{%X^NYxi>->T2?U2!wM7ts2a%ADnqVkS+eFRP)Dez$r+68= ziKEqA)b(`4?a@XQ2Q(Q9$HFNvI|R|dKvz?j?1j+xpr~uRAf@#6HSI;+&;)I=l&c}m z3Ge8Hz`GmUsMw;A1aC4%91AiOl@Zl)m(c>-I#@%r9IWjiTAq5+t`eqB;`Wk&j;iYF zfs9OCB&_vJjh)>wc#UCDHYjg( z1l9mB6ICx~4R@$IR$s*(BjzCLZeXNiY$Imuh1GCDX<+|i;3n;1qpnY)z$J;s-b8|y zj-~_@CT?q_>F%YXN$Q*#qDGL?dRX={LuFa} zC8JLEaI{we`XOu_^bpeGL=wo!+Ylq_Aft-$a?mg^QE{;KkXBKXa#WS{#(C*G!bKrs z2I~40Fo~cd;Q@3csuOW2b!%-`Ema&u%v0P$)y7tgV(+E{)^by~S8=u1BMvUI$@FbTl&6 z!nkV?5MD?F3c^5NlWeSEY@koDMr(SiYZ5h((yqXq9GzeiI2*h%*259daNWNxYHpfF zwlD*XH=aUqa?;Qst79C+oY7wTZg4R)4sWQh>4uVqsu)usU{MpKt%HlTgS0Nvz!>NY z_dq&WqYa4!4?9s4TX9!aBaF8-)R_R)pxB~}0Y|6`I1yD40g95sV08gc;bQ6`h7qS| zkYq@bz!gIc64@DylQIT784;Wvun3T$t~NnRM_*h|)4<-|4nPAYYVV;%#7Y2nJ7A2F z?xLDbk`f3&6IETw-YQb!P){eggNcr_k+rrK&chC?p$be4YT^zBJ3#I9-9=qxw9rr# z$x&L(-B4Fg4@$AcsY8&Izq2LOacNh52#f-C$6{b&x+YFu z4j>1Nvn~>$B`&3j&_MpRDIHHyYg;l((g|Zgk8Nu5FF)>(X~fvx|$LVF>a!! zPAa-;U>O%l7+haN9PMPN29*J8i@7^%OIu5zJt!!ohL$7>+&;vT*fu9Uo&&7_ULD3>X_3$dL>c&!lFM^tY5YjI01PLff%gE3P zW@PGS?T|=WS;UgfCSYz&og7ZB=2~ z)?h5uP73f4PzNakPiI3H;0WesqGl^;>?(#M8K{{WL%gj`kwz+THy1UqBU)Qr6F>** z2K;OsRZu44?z-yMFliuClYu5%S8p3>qOpy*hpV>)%tI4zs%4ASGnEFLdJ#pz5HBq) z2Yb7}u@dU0ujlR|W`vXglo%?bit_;CEz}W#LBnlD9U*oqHd@YVPA~_gp_38O24Nrt zTs3yKcL2ks;YJuoPlT=);Biz*l5QGyB$NyaOwuL7AZRFvNY)_hn0gq&fezllD(yUF zymh?)<`16UI55zlkF~}*ib>k3+1XLlq;WWio-F}Jz}iZXh%PvBM;W{fpt!E$Hcskz zlsXiR^+u|RK}j;MGS<$Rfn*4t zni?*4QZDMI1`a5KEl~q_G};bLwAIz}lCTE>F-hM>0!Ur{qNuG6?7>bRSW}z7w>cAy zyhKH@-ZDmHyp|LQr{gT;VF0uPNd>U|fAflek__PdzsyZSy=Ft>3KbPM6#}AS=xwoL z&hQHPW%p!`zxMojO##qQyiG8+nO?fsitPF2qaE`OYP`&=prBr!W0#&L<;%El*t>vh zzC7(8k*DY6g+B?TD-UAI|K`1-FMk#);xo=5?Ii{^n8TY3u{GkC5 zWgc$kXht|4FU=L||8q(*pfeS4NyH^UxbH9$yU0*F%{TvCe$8^b=|{d2@5i&C{ z2x8FzQ*S$Jou2G9ZZ2d-{o3kQ(ybXcJhxK2-axD!yDK81Q&W}X$uBH<{(Rl>?x^=v z@O4q2-IWN(!34jymj<_+OU6|Y6p6^X7Z3hJww)VTq_{(S-KYW$gran~BEjkYO>BR= zPuF$32ZmW4si;~h?bz@?-SdA}P=ND_NDwxAI`!!pW^1l<)xryA0(SicDVZWJW+(C& ze&zB%3aqP>Un%~?l=C$&Pq1yy?{IaHzgLrkAGlO9^e!Y`mUdC9PY2m#PtLi>A>FjT zhbgMx{rWOzSc`(Ge;m7QhXcc#|C4t#4NdHJ4pp*H=`oLTme;qJVhaI6+hRJ+fgTekA`tuHjv)^@c;dA*!CT^YKoUJdcn8WV}to7nsF|C3vZ6-#VA?HnZoLhsjUk zr;9g0_=oxOkbO+WZFI_d?Yd%5gY)}p`fkNTA;tYk+S~rKU*lCziy98{;=_rOoRZos z#yvfmFw97~qbjI0?}0_#jq4WPw`)9C!@|PO)$RWZzadL{*W~x{$ypkokKw&D?QuP| zUWCAsG{#G%BWaI*x9iWwNtx(^#z-u;C=0iNh25f=P-Bvz%Y;T=@l{>iY~{LsTu#|> z3cOv>F-5fc`r_V@&wBkQH;2bTU-4r#MiHSM4;tkK=O7dgcJ|3l?w4!vZbNpT$>Y7T^T88_Z>^8C%C-BCgz zR-a3~FPvDr*}}022ywD*tBV%3==IaJ)ff{e^#1fdw_Zo600W`(WKwe_aJ zh;Gd-ViUEhi4%u)e$>sSiP?@s!j@vM93;P$_0v&eU~I*mxoUdmW$N6`dUoN zOJJoZf4cp{8sv9}Fe>iXB0E6i-glhR^|&Cr0*e~cG2>hxljis+Z%qnJdzNlyP3-U@ z)^`urvvAe_c#t+I9q(dii$#pAtuSD0KHCeo89A9*(63MDl}^tt*Pfmno;T1;U_)~+ zXH^yyXbC;(22Wq81Dc0?_r_e0He1hUXki^En*xLAnDx0!5udlGo^e6b6@7^VDErW0 zNoGEtHxT%_^XG#rV&LbQ&+$Bg@FFT5N*w|ETPq+d3jYPxo7uM6qp`zprO!X9sQf*`y0KJW+@V3<{zNyuYX$8gi%F+TfPL;#%i? zy1$>{pC^rJA(Q&)%+iN(7 zEd{8ujUPx|^)Ij5ralIr4_;|JInXQ*K6rir^CIXNUSCwvix0 z#O)w|*%cK7`;p_X~Q430^M@Zbm8@4?+B66-XkEeQ#qoPGdv@(AGI0bTRCsjYc25wKuTMJ{n zL9kmlS|IBTF0bYP%*18f;%V7Fd45BTN8U3;w6FHsA4SE}qpe@QS#F0bWtUC>e|`QB zgg#eS>1ac3dwEcIo6R?t2?7bVij#Lz>W5*TVn1^{fE2LoJZ@lC<51py-@E64iE24N zrRsi>Eco!z=9dgz$4Ag*mJ!pC72OfKeZW=#o%GSfJpTRN2lt2}(mj~B9(CCL(opN4 zpovQaf>o`#XDyDD$4Ey*twSB*5U3e$&+}~Wr+d_8SooN(;vYHy(EDu_ajc0i+78s{ zLPO37E4PcQg@#;2rL@t&!ZBzt^5D;W`j?ECjMT!Vp;^7d$Wrgv+2=y~*grf$$~!ST z`d$S74l79%sljbyvN^~wuKy_`HM28@C+OtMb<2>0(7Nw7$pwvLwO*I(@6=~h1d_rB z?0RDu1yG3&f!e$ga0%3%U83DiPWYU^i1A1k zO2BGob)KhOiVT%za5%a$4iO4{#6p(j{}@%A>GhnZWN&2{S%-X^>WTJV`ON-|H=R)y zMMg;1tNmD)WLg~Kv(cGOC1ycxe3vVTvlzL1M^)QbW!Lq4H<Lw2+@Ys;@%M3Of`U-=FJD5qS8r9St=e9mUsbn@UAyC5C8Aq9Ar{A2rsxA&@pS z?0pPWbKTr)j87FYHs&~tC$eISqbMXgMV+QTBTv+*m-Txn1V**X>DLV}UnnI}kHW~z0 zLwBY{`}m!}oyETbwqVEnAiM~{RX-G&lGUBospRL+3vWBi_xrMGqo2Y!Ww}svk*^~c zPtN|p%$9Y+gA2Xd+d^GEe?>*S1_Yz;%h(H)Mmx~Lbbm0+ATCF4_xdPXT&l?)yzw&p zfH&D}D5ZTlK>BJ#R$9S_gQ3tprv8C} zu#4AYb&uie;w}l77x}K+32OSkXcN~OK&Ngm1$KwL=%ZH#WGk{6dJqVI5aZ20h|P|Y z+J&<&B2rXeywl=cl-fNTS>bSuaA&G7;|(8I7$LpBcoji$KS`GwrurulE;a|!wsp^* z&eE7g)c{}=MYO|}>g-@{lui3iLRZR_#cXvxW;FU?TrC%htzBGwpwZz;%lZNZ@yy<#Vpo=?%Tg#uD?)p@xMmRGrd(fOy{zNpR37 z@YY{7jTnKnFYkm=BJW-6)#QE77$`<5)1ib)vN&dKy~(=nLK7VyDKw82lZv$%dGxx` z^d-w;cG*ivaR{$|D#@>RVf@uQ5grF@_UUY*$AB4p-nPZE*`iZzO~ zTa>zDREm$4lVDcjvBY))-Z_PnBDd&!J61En>mU;DEuNJqv@dzmZj=%t7vnp**lr?z%UxAZFOO2rP1!lY`8RJ$4CTdk4z}q2& z(rEWcEFeO_q5F#}7Iz&k3cqgGP(KfKli;%{Fbis{fv`&QJl&S7PL92x0eROhiwHM5 zey-!D&#$V&dXwipc9`di7B;Cs=IYS1pbP48>RHcdh%vQ%!k=@6m<3}q1$g>qo@#W5 z{?PZGnRAjK)xd($9Pc<&IiBMrQ2e<%rS!dQ(2qd{53fdqz*VtfIdaxGK%Q;k+pm?} zFxlE?N)=;fQ=~844Tw$)iH$2l+YPR1Hk4dIG2gp(SQ zseab`$BF$q2@iyzZWkk|Ddm}vpA^S39`?BzRn08(M76A_khGq!PB|jI!P2rJN4-_N zs%oDT7*|DVS9loD^DVivCzd1Brk}J3pK;FIgf}8j7w`4HOB?c2?pmW$Z(JHw7Yvg=V72wE^>w;qMoC{>;437mzj49B`ySW#g8`DpG{vB*(WQ z90rhqwo(LLQkPBNFl9tk?0KrgbhLEUwtIQ`S(SA@W_$LDgLw7wW;Oe_2nLFOdxjPV z)wbf|bg=XT2im7_LV;pdWGv)Zxh5JGxJJXJ$y)uUuMUCA6cN1+me zDe^YY!4J=o@3#r(rsd$KONiL<)wy)_VZDFsI+_It+bJw#yz$>y>9&0+2re|ZPe#d2 zR~r4BWbR0toEa0r-tpZgj0;2RxxfN@IEO0qRJ5SJ;^yf&Nfvo)9AF8 zCe4^qn@BY$ffzJpJu>v+VtXnoDokepTe_cI%^-{B${D#-(n4zudy>ALbMe6lF?>*< z=OAv5>o@1?!?s1v%CwBXwPKF=S6jCKn1QydK&@zZ4t1{*$paW=YH46ejD)a6z$x~WfmBPQIWz27mfV5s%SWw}QAEn0!t6m(vKu0?`?S`i%AaKjg;$ za2;)If6IlW_pxCb|4V`Vcb9VMUu6-p5pVo&mjBZ`nsMd=FzGoh6NEb7|4pF$pS!qG z!1&s%Vo8?&e>@AE&W#@MOYS-OSMJMBd$v}bd$D|JOJc0d|1dNE z3>@gq_+nzMDnYG!>a{PWtEn<3x#&xCSr;2jy_|L`sm&sR#U2u~iI&kIQQrfd$ zw-wYa=%vkLCv?6ym!AEuj-=T-ji8MDx*IbC%C9wy9WPi* zTlm^p*XgRQcBe5$s5V9>BgR7EQjDduVwN0~X}8)C^}oPyZ!t#e=ibH+Yulv?w-e?L zbA+V;N{DrPq#7q*%Hhu-XrM2xp!=BA>M(y2*N^yLsbK|ZB-V)!k(S*A9?+aAdy9lf z#fzAP3EYY`f>Ksu!b1{Q^NhPwPr*woyjH2f%rxDI7X1H6K8q{xTVWaBA8mg^wupZ0 z_>U6j%2*`9^JwTt!9@`AP4&%ptO)k~m-XGLN)r*dB-sBhK$+)z(q(VQSd6~A@_p&P zoUFlN&dj}AO6%t(N^R4=@c>ki-KKUp2?$3tnO9?J8NYE{bWXqNRqo7qLGLPl5X|+O zidNDp^5YP@Hb(JGg4D7};qS<#s5wrDSi=$)ZsM;BGk&iiI})5l@lp9g7gMgO@rX6! zjd|CueJXlpe=OCE{J6C*&;937fEA6lG6KmR!vQLGoO6LU*QS?INOoe}((%W0+Y4`r z#R|<<3^4z`pc{G3Q<%v{x95B1k7nbh9vtRrCnmD-(eN{a|BPtcvfIb8VW{Rat{$8C zMMW`y9~^FMrp0b2bJ|zGEG#vwaE**Q^C_2#BX(P!>H90;`OSwbM><($fQNV|F)gY7 z_u2M4bg~jtq|Nki0&5ihnBY@4q*q-hM53t=ZX(~Xkb@mO6Y7Q=5BHBiL96FoyvXyN^hT#<61tj(xSjRZPj(eXY z%FuwzV*9QY5|;&Hd8d)I8r&4yOYH+vtG&UAxiKlKPq-G)f5EhnI{#X+5&Qo0bH%g_ zp2{2D32-I`!Di9!bF;y3W}W>IFDw-k89%EkM_W8^7d;8Tr&a&-%5R^`mr9>3v%0qM zF0S$x^=Di;RAddd%;J-Ya<`J|oX_o=rmA~|X~?CpTYyb_c<=(CcSXA5Z(;zq zq%74b&bO#7@!H^FEw>&j&vW{f<@sV~>g_Q7#Ye#HAM>!&oySMF79 z*v#CMqV+6B)5}5 zj|)zXrH%(Mq7jS1ABc$Uy-teEEawe&ZVGJonCkCpSv zn&mixlf^hMS_uW*j0}<~Hl_CYJhj`Yr5S9{=J~iO)C8aW?M=LxD3RFKy#R(VF+$fm z0dKkDW5AZ&N`66pMeBZbvQPQep-^hcRdnYxXKT>e?pv5VtX0%kvb_B>BV$1k1i{!- zb0j`C3$z*yy=KbDZF?S>5I2sentAVh7?D{}^kup<%I=jV!&4TsdmUm?V)EmL3#UU7 ze3gY4`80_ZsT6tps^p(}cV>K>nr{hv9?lq682 zZ(>)6A|?Qi*&*X5jON9qsPj7jkIj5^(`Rys{rURDmGEZ@U(MsPg!CY(y| zS{u}AInAv)InF!4R_dMgD;pFFyqFZdPJUoVc(*(i>FW4CFfE>lK6A=*rzVQ;%b`) z5D3`jvvON)eL@1qqFuJC25oDf1e$-j19%E9H)F%G7iM=gUHXER)rYCOT3T9scE<}H z4A(Q{e=EWrQ&Y9GOf2oaD&`{lU*9Yle6Jo{?_;_?qWQjTR368>=pylC^xRARIk28{ z)7ZsM_G;4%Z=&wD;8zJOwvo7@D}2HIeAUh~^RD9;Gzy=!Zfa2qyT=wd;|NpF+^sIm zue_d9Zpypw?7%UWN!goppHobshG(}4K?4$1HbsyV)=ll!Qfvdx?CAzi=2{s{3MPK` zEIklj?8{^w$X7|QEFP~>jF_tTqCe1maR2c@kL=fjvg?5?B9;O)sL*|_H~U7`pX`zw z*FkOPDvHj0P}C3=xk^=0)Vt)fYW)1P@IuIZ!w!4iz!^RD^yP|( zicAS#&Mb}o1V_0SFib}cZq40euy2-cAU<4A8xSF5cU+{R+$VkKxlbLnx~8kee~uK` zt0zr2L8oPX;JVvO4Xd&o7u=f*l~&@dQpcLJKsKq7kCSQ99kR9AF8MN;y%oN};d@bB z2nzBkel)0zUyiAe@Odn<6P>SoVKjDpz?Mxk-Tm9+kA49;$Hnvef1D#_MpCvF{o|&; zKRVBJmX0WjxzT_1yAAZ~SdI(l^2?9wx7@mp?m0d&4*P0ASI2U2G0^UqYEtbZO{YB>9H;TlM_Ip6{hDRkmbto~+{BNw%lv(*Wp6e<%XYeK z&3|tugS|}^cZb@80_9LWh0dd=2#dP%vziIgnD$j-tj1RR@cz0^f|tKe?%vncPfYnPHeON; zJOjg{(c>Z7H?0)I%ewIPy&V>H)moC#oAFuvYn0ZA$6~C`J*C9;D zs$0xe%&RiF#q8WuShIbl8p0j3BxaLMdl%q=|k-g5|7D zgV(1?{kLaHx9D{C?LF8ojrHAsKhr~tUNqU_-8`OrLk1sAirkRq?Y`R1w_NCNZp9(G zCyI!@ZYZsKB#c)!_g&C43B;9Ku~Jf{^iUL-i^ zL&MJXRpwHy0Wa<2&<|3?uJv)}qa&ZVnEP9HAexx07}{l_b0VD5eWz*q?nRoZ*XPfL zWwE!j$u(B}1S#aIx{CWe{D8bxoCy97dKEdXVIO?j{trG(baadTV<~3lYR)2lMtiDH zku9VzxiHVMrwjDv@hK(ymHZ2*x)aVzRk>BDVDlUFYnG+pMmRLJw#m$SF!QVT>3_ISt|@e{$=o#fj=9AIzeZ$i(~draWlEf zPBjMnHvetj4z)sq!YR|saTfZfTcK!BFf;IzkZH^aW6x45TO9W*p?S_g;Uyj=(Zw$-_(-DkG9@<_L6-9CA{*Y04cV&_+popTwg zB9_0N*E{mvTk4eMZ_Lh_;)rOA74n*Vb-PWCK$%3Q{Ud(?h&p9ngmYZ9+C0L)s z@*xPjk>vF!Cttq5P@nq4gtIFd^|BDsj?4lj&bi*Na#!*)vfbSY_|bNOCW*gZrmQO= zYw#OUEFKnn87bD$AIz z*qby`M;70>6R9*yI-I7+mB(*i(XvR?;N1iAg-_4A+)fcm0o`UcKIbCLb2{))t7pe! z&Vxlck-)c}|9%UA%@DaKQF$4>_Djmv3Rk+;;5=7WeEFrTu0QEM_M^+67`Bv+)JkSf z`4v_iR7dZcgTp}bzsQOt13c;( ztzkVCkob-t52zQKM-)A3j(=&q_r7;CtFl+h3jAz0)8Y~Xm)Xku14;d{T=^m)eWlq8 zFourOqMMH&!P=Zf4OB|qvO{&%l(wzf@(_^_06@ zJPayorR_(Fugsqyql9o0`&Yrvzl5FD4nDk^(jHf02MC60P_Mw`j%)FSdwT7o(Dc@M z@Yo54r9YafcT|51Ond0`a?}g5@Sy~v9p7EX7)Xj4FR-vDI9zRcU-ehh`=kYd6rTAj z$&?Z&qh@8?`C(1@OP)Z&djOHuq@P{$!uoH6rf5{o1S*{$ympc8XtVSwq)>xlF7qWGeo20vW^C0Ys1xfX6P_mMEYo zzcZakuPJ;$zxGO?Yh`?A5d(RSj5OxFI-!U12>2Q?tZ&AxmUvFN@VPWusV?m37SIxQ3PHqlGbdfU^H z?^|g7@nEsi!+T~{TbTK+)_~VYPjJ=D%H+-UdF{L)f8lCOrM6|4o-{0WoXH=<1Bl5l z?UT7wUxCdcPrYIKpzSI1|sTk}?!urYuP9~Iv65CX_3joy?s=DUE=g=qBN2X<-# zZ^Ou%{g;|tdG2*Krwe*#}nzsZ5s@(c|OT_aK4>#oRpC3Pa{4~IfH6Zg+d-R@SP8fDAk;A9ik|+8a zo8lJ~ddz}j{%|E%4&S%&rBV9zQN6I~Z)dgW7yP#$82g!7p0`uHLCWU8zy1jcdfD53 zw|l%hi0w98cYV#-bw4!A`hj`h;5FJhTvvH2psoxJG%Y^;Y{eqod67TuDQ>)da7v$8 zl#lt682}I}5350!JtNJ(Vyv;G0YVDIFxKL4WRaE8UwP|#TnoB( zcY@}I;MKWDrDYjQ@3HZq*l~!h495Oi;^BBmVs}-*bdzkjv6A;tfi}mLcAcH-%Fmt* zH(0_w>4oS&-Gx;*N*j?&AL4nq;YDlX1+Zw1jX#Kx?JGSut2A#dynm<=!{^xrsxX}_ zZ2hgIitpy1eR{_x__w9M3W3ud{{5@m6hGHd|Lw{d-kDVC4~&UvEF_mSVy14Dv;fAD zDK>G^E+Fl**Yi@DKEB1(clwQ;@=Siu`piANN)K)z2tlQo@cf7y2r2So(s@kWP4{UXOxK=L8ky!CGq_0tx$ zUFmGS#O@5!Zn!HD*vZ5%2c7Ls=8FcF^G)Em*#@tgV+Mh*%O1J?R0sNskz4JbklfRu zOW)J~sV5U=g{g{leSX**KS(O_WxgS|88r}CaM^9EpH^M{&q;fS+*!VkiO~qJ`5%)% zO2%9dhSmz(ipImfo;o-EI?ac58jHY*{jwJuDzt?I-`V`IUr&FQbm>fC-TI@NGu`#2 z_ZAXxs!Fb>1o)}_dii;qhNiM>wz}Vq)P<>wNwI|tpEZ``P?Wdpg2MHyat}XMs7KeQ z*Um98jXRHw2)E}U7fb7#o4X`YT|ea4Tyve-zJu;cNQvVOxP&+dYu{#b`%}~I;-e0} zE%9xAfdM4hcUdziL1#3d2QYm-jb0848xXDlfCwe(e7n9^Hwew#T1uQpu{K`)=W`ua z6e>^O6bpjHo@bcO$`GHNM7Y1WYO-(tB|pD6+u}=Ls~XuDJmxpWpO`!2IxJ#m)qP$F zEbUsX|LOiCCHp+Zd!WP1jByr^)g^jXE7I#Y>vqE6MTcYBAFrSd-&xIUyuab&0J<<* z)?}!*D8KPrDdi)X;9V@ZV)5wH#Np&iwVF4c`A>V72VZ~C(Hs#bN2P9UteC6(I%@l| zvpZ6^r0PLpO=Px#4OfOEms)%dYj>iQv@YuKrH zaNy~x7X7I~1QGgUJK>#sbI_h<(DYfL%*l%|M%A*tKmAS^Mn;3T#`S)~|7>ki}#`t2`tr3qnM;G5S9ftH2>DE-m0daU+-aUfN-0g<^}K2Ox1)^&{^hvRvh#r@EXFS!-}q2G z`rfYH;#@v_#YW$I$b_QEwA>oFx!b^6C-IivY>DP$J-D<^%`|{*cS!0IBqB1D(QWvJ z;O5am_<_~Ro3UVz%I%tdvDq*E*PFhOCod z%XwL{;+m_pyjC|X8)Z_1i<2=;;;wJ{i%{C;(mk!_TDM#4FU68@g-N{i+MNLz+@OKs z-rkgz@1aQ(oN*dv9;q-rS@?R;DZc4xXQT}^%Oqzf_4C4u)CHrE#)>Qjuf;U|-r78h z^Z5zA8zJDmCA_-sX3{gi`rjpaPWjxEF#s>vC6SZM7cMXabz;w?GA?i0DYRUiAb)#= z8{Kroe|QTyej&yw{Ml{yL)k|!Xq_NyDq+1y(iMID>`HCZ;%@ybyNZv$SS=#z>ST9x znHZJy)Tc6^b{1rmHHf<~Fmftb2Qy-`YY*wG^ZfXYqdxvT08p(xhy6NnTXP+1-S4|U z_nKp~GJHw&&t~5Dq>+W$>GsOK%8a8JE0Tk`ACGT_0#~^+by|Jxtm2fnIYKs3`%Qkt zx8?Jt+cjQ^_LR{-H&MSzBhLlII7?(paTp(XIYyD|N)3-+Y^|iWs#&Pc2)?-@iVlck zKM8x)I!g9VelKz}3$!F#s~F&6vpNZ{PX^Mys)@BgNj3U*g)Uu1!p zRmIZYKHiR9XyL!e;hbDtlEaqr*`8%-i8knHK+55l`-aY#xCZ5Tc{#@3$f`orROo{F zZ=nT)*RM4jf{x_RkXg?}b|#z7sPq3*ZlTcM%$yooUQI9?tQ)-@^~d|XTXG$SNohtKx?u#%6`~_-5CV5o1{^-*|}S z&Sy}W`~sMzBhnmRHP=gVIKp0VU^;p#_-^e8zaLB1UWwqRF&KiAb*|}R;`VtnviV>E zq|>;N?~z-^rDUF zu}D0I1zfsr%a*<-R3xjE_KM5=m;v=#=;HhoxE;kjAuM~Xep}okarH`ofDQ=A$z=O;-|I~-FbLcVsXq;RTrhn($7q($dg83Os4~Z^t0|Wh z-ga{ITQk#VtNmx9ezKQ$B{Y^XON@_1KY7L4I?e56t9OV}PQV=dMEBERF5SPfHuR!x z=TD8#Jv~;1^NLa5xQ^u>9DY;KLsLe{?|jS!$!|j7`!_|S9?*D~43m!pOiop_S7o9S zM?}upcR#I}F$07Rm-C7$=;EaTl4H9(&40-W1f`SoCRZN7BFJ%6D|VY+40spzh5AMC z7r*48iSZu=1wBR`|CPGV4~*)wKlC*Zn@t^Db*XZ zYq9owKOx{+LjTQR-JB7=S$-%toFXRk=vuTNpWZ87*|@`_dX@Wv%oFWVe@gZfDXt$t zY%(!_xElJITF1?Ue^%NP!q~i>x=hR_igsw*Y!K+E(f^LTDkU1XH)+)zsU zx9MAnvXRs_adD6RAHHfPKkAV=_S7p#Fmk)4JMpsP1uuf4_HX(68F(MftO1-p$cz|& z^^5UqgU$Har8D0}rLu;-bSf=PA4$aA4LEzlZ1L{i@=mA|P_=qdHGhh}pex&P+%`05~TSu-5y=IIXQ-}b3mo@F2DBx@J98~?sX7Vwof!L z@|?VZP3LsH$j&7uI)=PZ@}tdt>O1AnlUrp(NXBhs`NGGd+8_TGfA=y$Zyl9`PtIzk zm#@NO|H&nRcZ?EC_sw@fGF6P+Ujoc-;!k~(9A3SJ)Q5;I?42}Bk`5pAEQfj?=)2)+8!gORb9!;LT? zgT4PLw^?C$|ENyl(CXu2eWl|i=3d=~pBH2WYLE8lH$NuDz@84n2&2@WR1?O%m5okH zuMLis$=)5b3W?l1uDTPlLEn9I`H9!?`o)}%XJZu}0uC)5b6xC0V=1Gq5FgD|Lf}?k zt_IUj^s6_26KhIL%crgVf9li?d3@FAE6$r*ozr}~l}$mE$5T@F)u*qtn&*U#2(+zZ zZnwIxeq4;Xk0|1;Ut~iF8vq=FI)rZ#_yXX|o4ATEbt3WaFS2W#vlEvs*{nkmd)`D4 zs|JO712=1a2vdEwR%QBWva&nD?&WIvx9(OP+`7w7&y+HIYRMlU4Xvav{=B$*sRN>5 zVCQ+8I3uH}&V8S`9_6JwkO^wt*V$W8JZBd++|xh?ti!FnDqNa8y+3-++wZD=yZ?H<(>$G`mqiQ9S>N;NNd2MA=$d z&u>x-RFdlYa_W5btI`jVgUFFLj+b5DC^@}7K`486UV{3pp{-OZMOd%)TB1Q$Zjt~&byIO(}V_%XzI#wM+&(bdR@#km zJAib6cfL3`@u`IMny%++yO-#LYOuYQrn@kOn(Js{8}-=d^=B&&_LYB@uSC(;9dAM( z{XVlw%g2=RXoXq9bvnEGw#-S`#250zu^;(yy$%x$tHuE^{KVO@+Qlbcd zCJkQ<3d@#7+Y_=t`T}qNR1anw(=NblQj%;3k2_4_qI}SCUUh|6&!w>4E|0GO#gprL zv;A7sw!jP)f?eO`*JN57ILdxsyM3IWiU;K65s-x#IuBywA^BCks`e6~ys zXgMz#dH;{`2bu0XTrv8^PFS$f{Jjmwh(Arkg89LgBbBhUp=#|Hv5|s%BlG#1)Z0^& zzD=kHOylA1+dm|?ze^Uv%38t;!VHQ`^@^y>gjut7^oNYk6fm7DDii$?RDuqEV;pGk zN$A}ayVLLALkU!b@eGGQX5eIyJD>lSRFn^hzt>W6{RZ7!pT1>Q3RDAZS zpGA~rYHL7GOyQ5F)K6nZ@!N-k*SGI4jXMngjD4o&f?jh-*m3R>kH?!x+bwlx5Ma&Yc{gMz4wDOzu92XLtRhV4~(g+!VHG zA8~psawAsLz2GQxrh?K%9{xGF_hPLR5moi$+;`t!1F}+&sB<%$3AP08=;ZRD?7w~} zG5l3)HNC~F_vBY#A8o(a-EY`lw%&1#-SV!1!6`F6vT{Ww)kQY8^_hib)h#|w7u4I| zmkG1l*7t`A-pBYwN^spUv(?{MI@tL(Iv6nrT(msK_0zSO5~{nTALH$|fG;9YkNS9g zmpY$wr6gBO8i|}WD1DGguV}aKKHsg&Yogdqrh8BO`A_~k$cEDVO0j!`)N3=kyjS?f zq9;#95~-0gMC%2g&q<<(jW_mpn(dEF-qt!5K@PkhE?v4Xr1WB4j)*>ua6XpB%xat_2dM>5bA5 z$?qm(PhvYvc)#iLx(5hfa%z;n%)C0F&79SL*k^2b^3Xo-=&+Cetn;cFmrGNhMBC(b zC4Ggc@shH;YKtRsCLa&o%`~Hp z+R8iAYxg~$aK24i2oQ?<)63hMF&DsE=X761gOb`QMp@t7YJmTRP#z#G?QTs=4+4pC z%Iz040fju{q8oRq&gu1s9i3Eu_EVOaKE3@KJPCZbp{;$=PPObhtY~A^8ZQ1S{!`G~ z*P99O4h<{azwBktbh?JuP3HJs>bZ-l+MQSj*d`!n98-y+r@H4i#ZY|6;S zMV9(k1;knB>u@J2J}@*T6!McMK$^Yn9NXCEv6AWA0?vCn#a#anQ*Rj;Rrkex)2%3t zq@Z*ook|HvcY_iN!q71^h)76ENeN2F&^?3-Lk^t-LwCar9ryA2-}mQv-tuDDXP>p! zUhB8MYo8gRBBA*4ec^hOoHy^dEgIr|-vhF)FlA5fGW}Jv&BNzaX|nN+k`Gx9#KZvI zevVzJa(Fc$eB5G^o%VKc1Kp(0k4?Xl%L;$UgY;%hTb#kZ+kV zs<@sU7&NO$2N%z4BzWaD_IL4>qY|mP64g32RVp1x^cp8QN3_SJg2|h^UsQB^C@&hX$s41?snA_zLFt**fBV%~LC~P!rwT@3uI>Mf_VNspn$VX#S6WLmCq6{Ce0+}K&sT@x zJF0WgA)cM!`*m6e;}B5L+(f!p2zN%mf7U{`@C>C|eEvKiA+vfdm64xdAX0bMB>^Y8 zcREIu@)$tYq?^9jcji89l=IyLJ<+o(xx(5c!0lg2lUy(uF4AeEm~@G@woT*uawe0b>$L*+ zyU$RT#%)-?ch`@F7J^?)0pJI+86AC!>E*V+eR0{-fO(4FuTm|t_ckJ1(vPSx(`?m9 zv5;hl0_(lmggV3Nu7aO<5cPs4!lDSQ4dl)e$D|K*AuFA$ z&*HIzsT{03QL6%u9ywc4)XUAFe*CFaFA;PEz!y4O-MU8#wG7^1gCo=Ed;mEl077JY~$yZ$H; z^l{67SJ#LPU#OydpUkOJEp?yxZklP z@gMa=ygKAA{-iFkDwgw&#`6Lv zXzxmT8dZkw>o+)T3?r-O9%p-88>|m44fiV5%pM6I`d-s;1XNo3Vay$~&QJ0Jwt0)w zIrILs30BY!3rQY=uM)%12Osv6I`lgq33zd2KueMvBdH14YT;qe zhn6KLEs6cNJc+>kS8LtsPSUeqt5sqa9_%d8LIjU+Ap@pl?P~|$?MFj|5i$NV%aBP}EXAVt*}4z*f5{gWMx1`gUuAagP)ftcsQ7 z_x~RYun08HTl_8b6H>D}{=h>EJB2Xbx>dM&|2`sGgl)g7OU~Y&q~6`H$;_#g?G4Fl zI)rVF2Y-L_GQ$*J*O1jWIgC^eg8Mr$1$h3_8RLh$|h5A{>_Q?Zphc0=c6 z{*1G3N1wlkiePm0Bx{)dBjFChPV&AwDfLtPZvTIeoKhL|M}kLgXT~Y(D;=X9<9W*j zF2=)$>;8k=XI2QI_%bVO@xOc0jM0i3r%f|Y-;Tuj(C`d6GvK?yQQjMXWzR@D13_2H zdAh+d{TWnZWEcQR<=O2zqHP)TQ|cL^v#z9k2_;)YK;^H*C@F@!Bvpjq|KeFYXz`P8f>CPewOgEk;8ePM=h2A>@%X4l3g6_g z)UXWH#3oNorK3(qdH$UBHMss490d<%-!jnpIavIPTA#(rmV)uYS~e^|oGCxB5_1*{Lb|9tOX)E89#w8M|a48GQ{w z8nRGQyynXpc2HX0%($LHmLyX-)?3{fW66SRBCJdAh6Z6g zsdD9)Q;YcYSDjovH2Yycp|3&cdktQqi(o`WUbBlB-rnD)$%ipx$sQA7_&ef_6RuRn z2I6JQB=z)dslt|J@+G5B8m2bZ(a14d6TQ~!^z9O*cYUr_uaB!Ar;9zALQ0i*CTKr{ zF$XhpD|na*oVHA`W12H2B_6#={Z9)3SCJjitTomM*+hK$Pr~{%3>P)6$Hdo!oOO@$ z|L&)cdohX@Y zwTtzOqOVoZWc+IU-d9zlgJ(fMmkf*+t~zWhmnqk2BJ8n2Xo>B92DkY{s1kE|E&o;h z7&1;N2jOoZT__Y^er2Y)o9h%`ChQ@r6%A3?eF|uvxlS4-!FIf z)=rgBWXOrfi(ORgJ+RTxl28INwJaAoT(+2uwpHf>?Q}-z1gZHT;%pu*FGexbjIJ33 z+C&(Nxtj56?9+ zYi-*N4d|5{a+gm@j}&#kjd)xCJ(be@Tc}fL4;VRLW$?2@K9B)a37P!vwHlz68y{bl%L#t5Aj3fo{)K~sL!pS_P5qEahH?SV(!jI z(X#zL6s>`$C7oKUXMG?(<8yZWuu(A0|1dB1LA|$9p9GaRdlem>GGF)nPyE?GPk1;9 zcklQ@mi>nlc<-}ui_7f%8S`8TDN$uHg|ec?O%(pA6H7Y?qfVq8>3AUt)uvDG_ZB=* zo7Do@`~FXD#zs`Pt#6rZl-P9D4b5d1e{p;NK31uJguE`gp{xYW_uaZ(MquOYQCmV^ z(1^4=Fkn8-prnB*ZevIL+(Mu8Gfy(PNNTHv2LEX_j!<7k7s2uHx2uFy;vgdV< z+Oxq`-C&21CO*w~A5ll{i_}*D=3jNdFVoVV;WuwKcdzZb&JBe(G(@fW!mAH^$^(>M z+t-h0pPu^W^Y!&@*1-=Dt)Rx7K(IH$K6}X6hgoL#qie%iCQ}*+SyM8nUsY#2J?r!Y zk7qZ@VLy4+>uk!jqFJ-DmOK_1)X~c#vY%h^ee0C$tz>%~7RJZ1H-~G{;*EJ5{4$8~ z3dJF>WArxsr7q%?3pNIbEtSTIco>vfRn7MCQ%t7VY?@%=N2O|JIXV7|G?idOZ{!a8K#9+oKEboOQx)K@~k2+O5^-0ppqVruKi*XqF_$>P_DVgiD~e$eMz^UqxNR+cQlsoDvuR8&VyVb-ak@jV z63=pliz(r_#5kgRpOSVffU8oFei82xw-}=_xVo5vnk^w7V13J-l!zMhfeZwZcQU=U z|0m3X1geA;>`X13($o;rdM#1Pdh8d>c*;Q&Uf#o46kglE zcK7lFJoyFc;Tymn^M|JJ?zYPOBvK>4SM0$9qylRq_bm0!NyY9BRQ@CChtmq zg8(`upX_0>kbi|^UIebgUg3)SqC(007Z8#u*#Uhf{Hy2)>OZ`3?s7_TahcN38;WS& zrQr%Y(*^9vkzXTDGbv5qM9h<3FlhQrE*yUXtL?K3!0#(IKk(Igj$@ZS8)KjISPlCc z+Oh3wwEanv(rs2-*%YjS3S)*oQ38p&YTYU`XU8aC-u=DBUY?I9A?qBwckb5HGa?>< z91lAG7OulfqVrQ2OQGKXBCK`5zQz=!TOiPcM+iEy7ivLn^=GF2EaYRxva@g-eJH-S z^n+d7*%`wK*+Q?L26~W;6c-aH@D6{BTXR)q+#;4q^KQFece|#euPcIB0K0W``RtaP za`rcFlOZ}37t*{+-$-HfEC&X($%;C|Pkrl_^yD=5Kp;oM_a4F&HS>R^4{Gkeh6Wcm z{;Yfoah;&!=DaXhDo0E+<$`42WUlcJ0f*OOY@K8sf^a0Hcr>BK#STFx5rI&RBxHv8M#e^}|9 zE31pwXqE4B0-_)fj^_~J^N#WTM)tAMU0pd$<^w!?l5@%a+s5Zy^k2Om4iuO1w<*u%-HJa9QD4Q;KEE=A-)emtnnttLufAFyYX0+)1yW%{lD2RgG z{i4o|MbG7Clb+++6p&$9c>+z^3L12eMlwu4`vL54V##YUqz^o_9{w^&B++5W=XoI5 z)5b6JH(D?#tF|qAW6_W^#czI9cT->~eWcYbzV>&pKi7V~Pzs-y?4kLoZ>hGemjpmX zEhOXQrTr4?`T{8F-A0zCKzHvQ*(b|?$R@ChWjqGXS$`l#MeBlyqqIOzS)P(Lui34m zA75tPrP_kkC$GB%IqS}>jQ7r%9#xDzFTg%~=(@joBn-b_E-wc2<%9uQwfV!r=;Y1u z?>C*ak(y?$&vUYrHWAm{8f|LB3OS{5?!g!Sy3BT3_5h=U>Rx6CvpKOO7!dc3V@qxS z57d*`rk3)%=B!Cu<_D2}#s-Qzm4r>WP(1rMR3h0Z32AKv zlp*a;|FNmYg_F<>PHQ#GqhYceeV$9}6$nO5TSuprv&&D>Sz)Nz3}2NgZ!u)?$qiyI z|a9~^|OiMh!su#pPcC| zabymJV$URIXp4WXDrgCECu9CPXcfvOeT=1K;6kV<;%jY0AM9yGg8caip4TPZVLvud zJ7xXd|LaU*%l(;*>sA-kl#-kZNf%7+;JfO_%OqM#ebDMms*_4k-FEOR0yKse(pf-UohZ06@1p@)wSebQEDdwhUZHnmWCO4A9S*IR65nq}9#YxH zqP+JT`M+Vc_y;2XX3|yFyRhy!iLOlFS9G;JK!AgRj`|8l|JYlgsQe>Q$D}HQ*Whj( z4ic+9mo{wqNG?7@0{t*VuvgXLzyDoP1Kpzk#Q5zxgoA@tbew6J(&URU-U8MI3(((C z%m=gN0z|7c#?S3@#b07HSp)51;rI&;|7#hmHNbfW`BfoNni8lcCUy^-ZILFXQ)|?E zm6ZG6bBHWa5;9)SEb^N17F$~LZpB9CFIpLU5BIm^M$^M2 zlV>*r!J)%ibSZhoV}dBDyYwI%?&Fl?pl}B_4f9*FzL?7_9%wCTFA>Z8{7gjB^D^Ym zZUA8zp>WPiFQd78VtBZ?uqS=+AHiY0x5h#grms19=vc-{t~CU;MF^%g74DaS zu&uR1f4DoVn*mFH3I(j%7AdiLtf;-YcIugcY}(yx4;-yE8d;iX-`&%ATj31O=2ZpF z^#CXW=c|l3VCG2SSJ~~g-Qbw|!FVFS<%`!qa_079%~~*CJunN?C_-P%YokxtQYM0v zAoowb@x_lk;nGxg$M)%hq*+IK0j7?L?Mkq$4()C8@r|JP^QLL-{i=N9C1!DPRD+?UtRb@Bn?H1vQ z{~b^JCR`Rft3akJyyOvy2=>fZ1CrPhl060)E3VTfte`#t0M3otAsSwNzGGgsbh)a( zacuhYWSa#)?J=Y#*m@FpJ&F{bUm#bfIg-<JGirXV!GW6#2{hyk3FE@V1 zxtF}1mz4~zzh{=R+j|t(p)rlNYbz{7w&?w=@L&G5QG%%lJV=lI>)*dSRNWa#@o!e? z**4v-aUL>CPW?985JAy39n#5(eRtLZR$X)2WHLr45pPCmw>I+Z4l00P%-|0ssAJ#O zP~P>n`sbE!&i@YmJ9xAkiVDPNDG|4J-$pZ&w{x=kc6qB8FndtlJHr+y*!Zw$F0Oirujs7`mWbVmT4Q?&w;sB*Gg}WAU>%Q^MeXNa-fEaHm__J!A}4 zIM*c0TZ~+D%+g}C)bUE2m305ti(`}gFfqfV$jmMCy_!)|0(lOSCB)0P-9bE{kuHAZ zZT7N>t)@Tdg#&N9W!2-dK=jJTs-TVO!Og|EeNxG^>%ShsD~)#bIs@bNifecpFUvkl znXZUF(z)}6o5o6MP+omf1#JyhXIu^K5G(%&N|RK_+8(FQ9bf>n1SE1^eZ;Y`03ZNE zz~q-bfHOJ#csF~!5B~xvG)XaTdtd9GHUR;o?w3})X)1LN4D%nSU$FQ*_wioANW*_w zkpeBWdHK zc43RAs*=-s*6E(&APM!Y*hu&@40ZJ#JN3?fJ^d;4zkUK?(g4ku-gx(K*G}a1{E<28 zV@~(NxP?@0jK4Xd7et{ ze0hEKd!xwjyhCqCGD<)pb4obvT;6A$7$QvF53fExoduIy1;Zjs*0}|Jt$n1?kR^uS zX^Th1(kT>B2~YgwzO#&eQp2--2f%-5-qQ9Z0oXlINeGBe`q6jJ;dQr6zZ5pAP;U@S zDcG6JDgw^qr_de^^M^c!#zmR1%|^k>Eb(6rVR?eb-;q=V9s z-^b()!7kR@6kehN}}RK8IPJaen~DB`DHW(h0)Nv}VUyo?q9 z?8qIAD42fKtJWABhL5eU6mY*WzEnS=C{#3L{yc}8(!}ke7%6^@!lbbkH=@(Rtsw%H ze2DvQ*W2l`9HL)so3MMT;!%3MoaHz%g`+6S(yb-a)_8G00AU|%mDod;4~&A09f8S7 zV8>V+sn0jcn~SE!sbWvXodq1fi`Iz0WrTR5>EJexb*?!xa$pt>5hPuHS)mKtuGhdg zhh(6XOOdPv@poyGndh^9(vP<(PsJN~{+X=xCP2?+sB&`>*GU180N&HIR3z$w43LQb z{U5RaZ~XShJO{w8$zi~vx|ALS_bgYo3>E_rQ9Ire&(&9t07LU-IX0aEF7-)P3D{;n ziFT2|-X_QSxCj~Nb5O~xrfX27<;?b3=J#o`M5J2U{)dNWT2Iqf6##<&*Efp6gI8mK z2?m=cTCy~@+#2|($DQ0}4*z}Z^CnyMmycb7{*x1*U6(Eb2Q8AhpO5}z%g9et6|g>5 zwQOjkis2F?Ca!eNjT?!d&&~xa`tGfs>;}0!g}Tj=mvodIQuI0=cq@QwH*%1@ZnjxK z6K@=TQabR}Pnr+yg%_*t#fYyxkU1OF4Y>-^@*?rKIBUfS@~=7dWf?;_GUCg+w%oE8 zmHy@VZileufTS;67#0;68Emue zl0xXIMs2t~6fKw;URkTF89}o znB}_f(+ge2BMmOnerY!tJO(=3twClc{7H!)%z2b7J8*iK+*6GfzQwlD_XA{P6r5$O zEMh*>YpkeM7vK(+CbFA%|f! zLv`mIV$OoHx@(TI``}=X_+XKe(n$X!GmbX(hnI?+n7^LoDS(B9&#&biUcay=W3!oq zo+3AwqXajz4zGh2n|!yK6{5!VX{>k-BrLRewApqYATcGC<~)(JG7R&z$p9N!V&^w-ID5L8a|FOgJKjs zG}f4S6uc7{7b88oX@QW1(S`L1*>Qq7!M6)0oM1ZDZ~1BYjfobpq{-BHC!mkAvksM7 zIF4h@qO@oC9c;v$gb2PQoLIaTbtj$aI%})NVHCmboPBVnw5*3~{)4o*pLS8Qi{hpb z<4|22_u|)KowI{zlg6(8k1h05BN&O9xGHhe6POyq38fsze{Jr zs@@$BnC_x9|MLt4nQVp_&Vv;#RdZnm-8##?n>`7Pj4f(E+c_Z)>ysN?e*+=4;NegL z9u{}T#PIRt)2CA@eEC)q6HS{2FHjQ}YGlO+ zlC{;`(_TW@xQaR>z$v<0x|G9OfrZYy&XF^YJyikbPJypHCZi@DhrUOLF>Y$X?#6@n z{B%%P2}Y1D=cSX4>w}#Y3wSwcUd!E7%E&;XO3u`i4<&-WhvCu`{-i~vsWFq9ICtlN z74eZl1@Y6Qfx4(!YM#oMor`C7C!`*c7fO#V6AHDds{aOh9(kB!atjqX50E@>lAn>i zkjTl<8L`ml$;B546@ZK0Dt9vFPZO=~ov4vtg0rBt^eG3XJ+t%UyN$9+qlnkv?jAu! zG!8l4{iJTrWGKI)-Uo{We?is|4F~15)?&G6-R6>0KqFK5)D7UY(;KftH>VwoaCi3G z$BFo=^&g?dTLS`i5RWcbWja1@_R4||&EKV6?0oq{7LL~5?BoV_&)@g6K#L|LsnL!) zmuF6aPzcA^@AEv^6pR0|OYW}!H0_Jd9RBrdv~#pSyDc-X$W#m}4>rA0Yk~-fhNYd| zuR(IQhYGhIc->cg2tJN*ZjoTfvAVnXu7vE*YPcP9Jvnv_h#J|vSY{?hv;a@DED7hU zIMOGyVCtErfClaUq=0@^+y;%Bjeia7HpMXdVeR7=4s|Nu;^izOYiaGb`3~0JL;!9> z$KLwQR8ZFI6X%J^S6|C5VQ0iw!}!A`nXdea-u6R^02g#qz)^1gH2!Ot?a(=+E&6(U zV6upwX=Sr`T+i*$Vlsv`q+sBD8MA~DlOn&3?V7Vo$L%?pP;?c*i;_Yxm$_wz{a<9n7H&1b_r*d*0 z>t*SpN_54yeNMwNui9EQm|`gjsg)q4CTG#e@4`^o?6+;fSoKcISUJAGihP1W=oBmM zIKNXL8)TS;abp9H((?nkYdV$|n-k48n()1bTYlaqII zJOSEL`L-wddwJ%_Jq0G>f16>tSRUfwICINYnt!>KGj4PV>M%$MGxdL`{O0)J3*fg= zo~}bUx+uKhtc4@haz`d0Voe`%fa&^lgE{Zdpn;YgWvIR2cn29Hg-B0YL_HVXeDZg&x^CRfMy0o5r&?6g<-BKb ztlhwN_0)Z(moIVa1kUV+LAv?aw6uhlHV8Iesw-Q2Xzb;Zdd$K(a`;bhA?TQ|5gB~x zPkI=-A7y=#j|=>|XLSOFi7X#lNZt}+b0dbknlDtwiKC*UZ_}5xHzGLY z9M-85WShIG&MiI7(2u|)_+y&s*lXOBm#qHaL+g}VgVsRiWI$vdn_XRC@8$ZJ@7+D&WScFOtN**2JZwAX5KjWlYOw_~+k=zE&FY-V3`mpFj zj3iR7K2y%B{ ze@pKNb|M*aT&66FiVCLc#BcqHTTS%`KR2gb$;EW;4%&>im?icGJ9uplEk>=pji0{1 zx;fok1pLUq7$@T5aNvHdhX2hfMq(Lrnyn_{&ijjn)UV5|7(M>SvM|!Vz8%~=ol z*nwMfzrfXxxu2GEUsK4h;fG!Ybww{wH_6yrSs9CXW#A@-u(C$V5PfaGIMxx zDdkBH(HJx*;}~^%rMVTfY<@T}x0o^SKr`Q?tvC46g*?#QMQbkaXyQrcZ}-P%-j?VO^Z7p<13b#9nwDjQJL|G9 zMKuj9^8TGIp8w94mxk^1Hxm#m;lmRL=w^dcn&K}*27&c(^?It&72lzjw>%atq)t;* zera0Q;Gaqn1NlG1oHa?GO&o+{Q zivkbpqtG*3-_2(1B0h0z(F4!;a65>{j}>hxS9^E(C*1Uqk1x34w{u*DOAFs zHuc9D%~$fC-ATxG(EH)88@CpUGy8{=a`OTH3xuJ-t+!A4^V5>NzdGLv;iMT!{S2|d z@)H%E3n)bFO2xd|Uz>~3ONv=)>lT<83W$r8q|2+lzgzpM7h{n!tZz6)<1qacj28F5 zwAjFGT4~56!1CgCL zC;0g{%XC|G_|gKnA>d2Y$I!vYf}5x&aYPYFTj3d)lvx<;nR(%HT}NN=*DdLb#LRvo zDLgffkn=(?D|0wQ5MmbOk{#bO(6Ct{cNN;RJ!S^&hD-~xY=`G`rnRNE$iFPc2_Xz z5RsD}sV+MR9)IM>T^;DDZ!vwjFu3U}j!qr9I_X)HWzc!5Ww7OT#`L=iqf=UM4e>d) z_TQ>8e9>E?#|l?<^v|zhv#?g_u>~nOk_jRCd?zTbmJ~9qgJNXtP?!pGTIsCh&0KBM zWJq%Gmb=f)eQ1)GinYuPCiQk&v7_Y`9wSp}Q1fPsZFf43`5G1DwWnfzsZ_!)-{-7N zyS`bfRw8Csti;_3nW`!qAjQSI5Lmvin**?j%4%1p6SY1obp%}#^6r3*16e)Wv{S2 z`$HC{ZkEdzLe+{d4SRY)kzaUr>4QmnKv)DP$Ff3ZN>@3#3&D3MxV-Jp-DZIp6dArH zL$k1Uyj>CZKKDhwUvlw{I?Eo^1$UT2oAzyvz* z;-0kphO)$Zhs@%9UPlUgpguUb+70i=XG|KRr4X5IB4eBA?~W1S4L4YS8WlE-6klj!IP^SWBUO)pGwnS8}e zuf$Kj9*ji+y>r+(rveV-xNi^eLOX}wnXIUF7PQh~_NIf99bA4Lp04osW7LsOm z4pSGMxxTCn{jG|B2rI2{!~IBJEFHSWX!xJxsSk zeQ&sJ#nb;fk^k>M1$A~pg5`QkuhKVm!ZRN5|umtD)DC z_j>#w%K)x4vFj07xvA|WaGdeUNy8{7p2R1au;s2A?h~b+W#&(hZJ*BiMO)_TL{tJf z>(ql@kVKV|<^z#^dwQw-%XO)@u{S^CdI!$!4neNa#$O^p z)EN`C?wn43lirVEjkTn;D86+_kP577W115qr|!JC~;Z61p9Fd450*U)WOjOMQw- zR>gq%aLPGN7mor9t1bQ&MmC#jrxgyC2pn&1sQCRm_f3z@n+<-eSBIfb}{Wq@(egPBJjeQ2g7{IqjWibCDUbB zF8x=E{N0tf$r@>@%vCtz#-uz}4UDunzD!EWIka}49=+ca&wMM^CPknG^4%=zT4@v= zRj+nhem3q!M!*9s5qlu5LIn>KsgjuJpiFCngKXwMZ8!;Upq`R#{%bdAxb=hrYS=sW zG@$(QtNu`C;@fGwK0AVEkBqVC%kfkB&EiRb$75j~jum=EI6={YA8i?J!Rq!Im?-ZhM^?nzfBkzwwoMP)X0{Q5jCUu?`DW0f6~iO?3s=p9SLsB&;}NgPT0`>pz(xDgF}7o3q~C`LolP(NE1HhaP%Kk)TC;s zBMz236RtW6Z|4o`O}ik`9;r` z#IR^H!u^TphcRCpu#78~v>z>jnz?Jmiq2`>Ie)lBFzdW5tasY*bvYqPK8F%Puwgaf$=At4&1kLshEoqa_;*EJfp0iQ>^{bDz%(vXM4C6gy(_bn zf<`jn+Q}0C$PQ~`*FMkuJX^H!<}eXjE7?rhjyYn2$&~+W`K>C)rD>F#PAdh)$5|&N zci1k*9?lw5v&5-YZ|~rh1aKfDha(A7$8-4o16u&f4A&%1j$fo9%rL?UA0m6Hf(d+ZVWWCS`9YzIC6<0LM#rnZiX1hSO4k0Hzb~@h ziSlHgnr#j~)#7}qdk}qT#kym*=a_ZT=|sZ=Qzo?!-)y*^A%!PZg748Zzg`P)C1D)-@QcA66jX0fQ{3#E(=IxKWF z>_hvCXPE=@m(cO1+a82M;U-Z2=;tLV;@TfUdAEpOBL2yMF+e+dqNi-p)`Dff4T@&GF z;0!TPgR-%RVbcUuZA}hjtXdq4#ppJ2B#x{9R12OQ$W+QJBg-PNjy@ZD^y0LEcWY0l zNqHFceZ{X^JB|r6BPmn(z6)ikpamjf3f_PZJ73yW9XRC9&=VSe4Y4}-i5e^q#t_$e z$@n7gGv^Xffh%@2xn;_yBroj?)xW-qUDA}DLyvs$zIhNT9`srW-pg%gm|SB#>u&Py z(N}Dw!RVrHwlEV@x2ko{2@>V%_0`LZAVb&b_7LuWfBRA(r%N&;+ushYi`TBBR%yss z4|^fNpgp~@F_@W{lTrUe^nQvrS86)V;pUECKGGbcz|t^5l}*16ixFEuAqH8>F9_pE zB*da5jcB~1=!nk^bX858lf|Ce9Q5yW%uc-%O%KUghiU{1S{c;7igy-AAgtq_mdTIO z3863r%g>**VJ-irTZB-a>~wLuw(oh@ zLE1!e>WpFKn7B(h1dTN3RMY4m>T}6OO6@9&f7K`za1ka`0~1tHge||dt|cpX6aSj( zlsdSOk(e=QbgvO(xzK}om(u;P!V;((iW=2W?cCbAhNQ+QS%JtB!>G{5Y$FPo;TV@% zF7TVoHtfd1a;<>ncX@FuXJv>EO%68$ z=Afjuw(iXtCl-z-F|15)D2%diwlKYijJH_qlZ?w);N(_VCElL5+z3Aq8J>1%`=-*t z+!=?KI7N@?4Q;aC3Ip5Mh8pCFr@Gm7-_SzbH$J^OwFh0>$015r&(XDz_TdnDPo4%^ zQ5;A3iJ?^YEV4>99ux*Td-46EBOIk8F0;j~ToqKWf-vOKS zd)yH;GbGyGqbAuY8VRiSImVUW0H=gy)DsImlsBASl9EMmQW&b{TE^Jxhfg2)c|K1u zd%iM5Fuy0nDN$D_XyN2?PFC`qQOKMVt>L2C(7z@8F}*W@Dh!FyuvIWO+tsiC=kOXQ6LlhAQwaABfuiyI+_kbsoenG` z>|=}HGLsi-zo!{-a9v{Li*iP&s@X=d$F(tmye;zhLym#A3|S;*cGm2S>Z ztJ&DM7WQ<6p-DcQIfp4Q;!Rxa@UJT!>DO-oaWS3a98F8AQ~RxVeb;dCzLW0VVL2kt zHEaB6eo2(U%xT-hH0Zr zIZPs54Tet+iszg0o(EiT=_kV}kmY)XQmeEr|F(qwymPsF;*#8Moz&usUevT|K zjXGc(lD4;VJkTOeouGg+NIi_H_-JXnCFG}``pv=CUWP@x!oy=@+W-&Td0dTQ7EBFI zP@P)rfmBeLdJ488?~2?tvfT~r0<)dd)AX?Yc`;1> zQta`35lOjrKHRwREpiaBpTofA)$?Dtm0f=r%kkkN4R5egpSsSRk0z=-)O3eQ5}~K! zy|_1aE7-5p8-~=Dt4n#Aat-Bb(aFQt^!nnwT#}CUWxwmmJ3Tqj=%7#@2X5;+Y`C|R z`^Lw~e503lm#{YuhP?S=Bg0GOMT*Nbw_)SSYP2&*{HOGOa&`6RE%f$3H^LY|8A{G+ zV_n6dbJ18r7+24{WNeObLr&4ZUtrW?WG##+o5)@OSycG2v^JH~fyh!)jJ!_6%hv7N z&vu>fDsd-7R${N>mrF(~Wj^JFUa1Xy56Q>H?hwoOJwR1VT&?a^GI}CZmWeBIdz( z8*_iHzq*os3`nt0Wy2CC$DlE|Z%|v!RCrE%Lj47-K(e|v6TLr6sl$s}*7He|P`8Tr zVV~#k{1kqU0(XDReaf0rJdF?&o~z{Kimn7MW&09KR_w-Tm$ge_6xLa-%kM9WW2B#p zEO)4&w0)CNyVfi26DC`i-xct?2|<5mEff3hr_-}XJXk@^@u)GA=QI0cr&An}Pl$Ac zQ8`GqxTLi67IJUQrk{brtMx;zYqm5exo1PM6t@w%mX#z+@4t+ylhnTAC&a5N1>4V1 zk2>L8&}IS2C}+q6AdeHXM=_3Refv&a?BMRPv0#-JJene9b&RIXO_W$Oyyc1fLsKSQ zNfY|+VMqj63qn;O+?SGeoQZG|xp4Z=+v5&aVEd_XuRuJUvf5G2*92RX zIN(e@M&J_+Y+X{Rp|a|09)Z2~uiNrR@ql{F`B7yRuQ|dJ8-DiLx#iVi`uPZ{f=0Qi z&`r=}r0-|MhXeO7mwB>p3i=0_y0;}jw@DG1rJsH>Hnsi0=M(w4H6QGXzwW(te%?@K z8rJp_NA8vnOc`DFaa%R|{JUh#ebTpY&n6GIDSaknRq8I+ne2{O|H^Rrbo(I6G;bz~ z#CD+{7hi7kI*A9}C+$8J!&1wARl)Dv(YT$sDpsD=kzW|Ba>g*<<1CrF9cXR`PihB# zETXmMQGZEC!K8Yg*Y)sDV7Fx`fB(qg_WsE3g}u(_<4oQ3ozKT_-d%i>UG)MlrFDU6 zU@FtEnQ3rt+?l-K^xkb@CHN!Y^;FsC-ca52LM>3GHtp4eB+gu|rta}H8T&*jR@OMe|9>ybDTSe#7_1OcRo60m);#8_~HgelADR?fOShlLN2f-G|4U`a6HoDuqGwtDCtD zl*%zvuGV^JA)5*{sWV#dPeK-gbcQ?=!gyfPnDc&`7aX*$x?EEgnFSsC5*I=(_BrVd zYz}dEY^ZZd zBtjN2R#2@+r9qnmwH3Jz9ZK3>u9L%9^#lUcTpWR~PPKqS1#*;`_~d~>401kqW1H5% za#^Vp6plV3ia{pGYyT58xLlsFh9-~!f(GYZ4;`R=NVmF{l*Dfa6LeaDKiE&cNrUh5 zVxUNlWDNL69#RKytI#~!nsG2ziS$**=K4SEy=7RGYt%M8lr$oZq@tv>fYO45ASERr zCDJL~p>!HF2-3~aJ%9*;bVxTyGaxW@eb=x<_xl{rzwh7s?qeL?%*B1LYh5eOb*?ot zXmYWYw(IfPhk+O^l^}0e&T*O0o!QKakG)L7X9QHm+xMlY*k?$t!WoAsm>OA4^zcbR zrHD17CWqVeCOLVeAEVxxvjw!lX%h2f*kdGZz`YUv@EPDgwD0eM^$OHd&~u{pU|D)} zB$0vQm{>srh>0qB7PQ-bg2-hR4#>5yNni)^)C{-%2npv5M9}-q2b}@mG0u^N7Yw33 zd;}9b#SWb3Eb~AQy4b)vapXEewQ-Zo8*^SH=9H!l@1Oee0s%$a-b)d;C+MBtjlA(* z91F=!pswCG7Xq?b0qW*4YVum84jrI{bfg~+SvH09+$O@JMv^A&vFtJgNYdn^rL-J! zl^JrC*#a23ZEEEPt5CoyNkIBo%lIC?hd=A|l?69+-asRPId?Iey2ImCfI9pbfa2!H z%9c8AHJ>uu+xN&mL5c+jtAH|)s~&(=x9Gtttj|N4a6p-q>B> z59d6cKxKAFl~L={wE<&{li+5OysJt1F$NL5zP4huLlVc$r z^*M2j(vK)Fl?2A?$SiRtggq97H1b6G|HJo6S`0+6#Z|Dz(0}|qHTw1-Qp%9{dS=L) z^If6j$UDT~gMMNRZkjZF%~*K)sPOB2h%@d>>X(mM&J54HEBn`hX z3OMbEtTldmhY$zkjI_^Nm`Px8$CtuHA=m}4H4I(W7o9+XehPEL8f6vb8zzLdH2o;% z`^N})!AiAbLa*A8Q5C;o70FJ8FAGcHycN#p5TZL_5+5KT*VNu%KT&@39OY%qEfA55 z3?9=6ema#8Hphv!r<7BdQ0BlP^^Io9B|?#-XZDjwBVr*jpnJMW8dTfmTPpt;&P8pJ zL`;g2H?*>?cB*+@YiID~p$%jO^#Mxr2>8;ytwR-vr0LKd!q*_@uqWDwGJvd2Tg?K3 zwkk5T-!=&Na#9TmgxI0E3Qn~_!zmY!@J|IHTZ)00G*5F6{ffu}zO|tZ7SkDbhM6sHwAv+M( z{l_LXA^Pl&dp%ydoeD0T%)>ot?}~jDQ5^}1;nDM?`Wg+ake?`HxxV<>5*)(IP=c?= zs`b~KdhyHe4)C=V(@s7{V=f|U2t&o21$XwnQQ}4vOo2@J3RemLk9@lN<)lCVZtE?s z?egzkD2}-*+F|-$F#I{zRGl4%N0hvb?&X`bceokYl~G1Y=0V*b4oiO--g|ll`i9XMt6Z!4|{UegcXzr*v<4_I^xU_Izti}{pgX{>9&QA(qHmyKWGyT-atDP7OAHkaWz`F2Sl)14?Vto_=@e1H?nQq`;ob3O^?ipZil{wv zdj?$W%-x!hoz9B2c6}~iC!fHfP!E)DhCX;{`u7!3?-+1i!6v4^VH1EkT`VAn$}%eN z(HEHJ6O^&ykrJLaPW7x1_)Md{Soc!l;DmjjK>wK)N!MG;jb{fMFz%dA{Jz9rn>p*m z!3D2hU628JL79QSpb5R7*p1xd&U6_R|EHT89@+YjD%_Ud(A@se;(7k4nS^B(F_2ur z_3|0J_dZ(~!pfFjUyDTAaEO$M-VXvcjL$VE&Zjg$#axzV8OIAtGg9ihBaV8(^tnyG zqiyR^x=UM;?Mh)!~lF< z@_Qg~a@n~)bp90HGjvJAGV?^qmL=W#w9b#KnzA9U!ps%75IZkKD1J(zf_phLA`|}R zGb9+X53EY)yiXUXI72i1#0&HL+{q6*SF)c8P%%tX2lKnv z)>a6eqFDxelv%%$tY!K-L+*KXloPq4cc|jj-Y!$bE97rIV(H9%p2+WYK>)BbJql4U z*OfOt_!B??BNy}Y+25uk)p_>L^E*_>g=VuTIJame(riQ8n4GzsIC@}!u!5U(LPCx8 zvG${WIm&+MAa;G1{~Ay0*e#sNTg+S}RiPn8IO_sYXd9n8UrgGZBnxL9?$A1(FV-M5 zCL6n^AC8}%+|~m+zGI+sB?9|@E$0^#m)rC9u|;Z@6)Cq5vqFYjFI>E6IUDPI)P`D? z^CIQSi@u6mR_ka}1zKPjXEW!T5XPkAJs~4jfZUOM@}@ah^LB``cgiiYo#0#|V_6p8 zm%*$=vT4sOIDJJ>$I;^JqaQdkOIoWgu;ki#><%7M9(M*ka4Gy4c4^9}l;jy)HlJ$f zE{imRec+6hW|(RJIY7b-^laKmj$)-}69sGEzmF4K7c~~@p_BHoBY!S&Q7^RK+Je>52!dOfYULC#a*&pR5`Xb zM?m@2I0yDS@mAvasSFuMsD?@^p4m)84)*?glw-#H!L!Dm?pmR944G7dhytkd);;E6 zr}mnc)49RUu5*Y7>FHA@0YhBW(iLlJHeVM>VKFhjyxPLqYuukWYNoGE5%A`h-OipG zu4uV;X>I9t?0Z-K#l@~tb@|q&sfX=5y(OqL8kTL6y17%uRj%*sd`66)JOTrCV3d=y{DvVP8ZkJvNBt z&p!g-%(r*#sto>}Q11|mqW}0P zQXc%3iK5jHJf{w)Khl^czgN4d{^L>fx4^s^Sz46 z*g~J?!x60yNQb`n1cZeT=0oziuHzAZp?p{HbqUEx(5cmCmDHwq zqk$S@5}L`9HNo}>Q^iE^ab<4LI6P|!cg2RS<}xy?)ETJ1;!Luyl*mr$pRri7TA!Ell8#Qx1v3X_&e+xEU0QI=#!5PbBM;VCEo zxrBhgyihy}sr2q=6p_!=YPwjXwcZo5FJ%Sig|kIDz(abR4&xg;i!9vO-;qIw+QYd| z$jF60YP%*IOU0mKIc8l&l7)!7eH2~ zKUNj^;XW3TXNc`_@AP=BkV~@HWS@@n>9Z7LWK?f%xq>9Z|Kl{J(JPugm&R<$6|IMs z?}$Gn_*#i`gWWw%>h&%9w%hq6a&n|FG+|d&JN)n>^qclcOO-(N;w)C5&CTuG_dRI zpMPC{A7TA=|4J?iq+HR3iN#lWIZ9U+ofM+AEs=)s4Wo0y|*zW#UWbuOt}8 zsM?6YpZ0FuJ#@A+hzq#9z3uf7wEJ?%X!PYS=()X^@h+Dh*bs;|93nJz|H??}=nwW^ zjGnR{Cwh=sWo=`5lSZV!nEQliG$nMGuJL^GmH7Wql?i$_LbObE zTjRT}ljVoa#l{&f+-9Y%vocd9=ic6yFs8!i)on@&Wt7O=fn^G!%6#%Cch8Lf#6|-e z)Q>^<4z%+VJ4+#Xc}&{vmY44NanJi$nB!qCHkrs2MB1Ob$F2sN55T8=_pi9FAS6_N zA!4HnXoeg>Gsx0E#TFiJ+VfYT5qv>p`JgT+FAU{%LHDB8Lx;Usfvx9dHWuP|B`R0P ztAsq>gg8leF!(I{IF=1yq1xyHL-1Mwk!zXH$|7d9#s2f8BmXZN5oz4swk&K(mrd~F z>e(vSuj$`@n9?AxQo{Tf|AOR(3fiqh^-h-nlK~(Gi(mVRi_PF&?&FUr?1`oUNI@(I zB5C%jw!7(^&hu&7P`I0=kpPEY80;m`RF7bdRBHRy4Xt9J;-zQOLEyD}PsPvo;%zQP ziWDYQVV6{{?D} zp(CEY!1;<+mih99%h0lmt$vx!hf8f^`KwD{1AWBG4yAs|{I9typeh}13-hqC!&At( zOWU=1-vqkBG8#PRlixI8ZoMd*F9;ldWqzd$VWcv$-yxL&eCCSWdgsfBE&STC{Riz6 zmhD4}5vyQ62DA9lhaS$R(D7>c)MVUGxYcuoSVmwNC|$_GF*6(%By|K-iR2QIX{`BG zvzg0_Lrz6SYhG%cK)#G$J(d`+eQNo(lM1|#_8n&}N=T5_ITnhv&Y>qr>s0xhd6I1| zL;o>^#*-pe;C1SN|5diqibV9m7Fpkj62DD?_r{=8^@(z_6w+0jZLY$G3NmbfInXN- zOdNIO?ypx(;bBVFNzM-Tj);Mgh=u-??V{YHSxq+#)$QkXTj!E-CfHZ|r@7jH^nds7 zge^QE#sJeVj~1xA|D%70?|3x2KBWs2~L>rb~=) z1e6xy%En2^!%@EOS8#e5B`8|8(3O@~q*TJjjPlCHdnUQB@pS3Ons0mYYaR}y2)RK> z71U!QBgH@EBT!+YulvDjv28Ly7L=V_iCTgm1d|Wr%Pg0dBj{!uT+d^i!=t!?uHX4y z2SRxHl0-@Yn8M8#v_fp^em#oL@+y&LU_GUL=lwOe!ddg1j1Vu?uz0*Ct8;B^&qUdN zYBgt%*TZhzEDxl-gv`L0{BBdtIkUK<;XOd_0qqUIBqCk6nfRRdgl4GW*g@vbU4klx z2>dpwJ)fz{4XDe#US0F{BMM^VSssZ40D&nx{~W+P>T-KmQRHZ{*a*^0{G@Nh57xU# z1ZD3YbF=Fh^X2<-6=#Y)Z=7wk-dgWz%Tb!CDHRUa&9b}_MHVSaCRKA45Cu!%u>ueU zOSlJ!@>m#UfABLyPL$Q;mLqfPWJn-3OJb$*o+)sx30yb{xe=H?r1!f90J1fGi5-jL z02N2VuT(lPo@?|y7HKh`AE9R-C^fk_o)Ro(&`6J7Q^(tDS)x7ULwQ%MQ8FX>wo#PR9eZ9k`Vbh7*wAUUX6LSQU_`syA^R1#M160q>P`G;& zW_%5dGmsjdBSzVGyyQ>{U+;Ies+c$1R*m4|{K+Fiby;sdba`(5{iOculzN>E@-lv4 zA5>;7C>E*d0^$aeWA~(NvF3;FBTrm-T<9Z7 z6{boDb#RZ~CgY_*Yf%(~TUUO{iS*Ou*#9$VKM!t>2csG}w#Ua|#Hu>iH3JRT`n2x; z@{CCpIPKi_dDv55A4iUq^)dg|6=nzNzL6QzngOy3O(7nB% z`7tquZcoeS7JHJ!YOHtasEVjDy^u)|MGO@1`@|gv0+It%RttXBG6Phu-zvZkktLaH zzWzy8PL-{T`r(ZiJwcF2up2UX4lw`+-81T)rI`QX5^Hp7p7d7Uvn8nZA$c1xEz&*TC zDK||(dtX8L*8#ITT3DE82!a%LWGnwMk?g8?Z=3!aH8kRy|ZS_k|!|CH@Y4my zgL7-jcvi}bj-gYvj?-Zph*Z%l%|igvx(2b}O3YlvzqT|t0T2#{tu_PFhqbYV-5ggk zqReM3EKdE=<+nDbU#KOYPajMR%+`9s?X%;N7)iES9!X}<`27#Vc-I{E)%{Kt*-96h zcEtnvM{Qbc=qy?lMx*UXy7k3%@Kbi z=S;XO;@`qwTRDcS9B6Gl_T!`isb%EBu8D!9<7$$?8wjeRH4iaB(MRGcCJw0D9AgX`;O8Bpx-BBh7mVbbcG*j>>fuP(_sad{h>h10QBW3J% zhI)WJ9JRkf_=_VS3r}?s)bUWithET@6)ML0_pi|Vu>xIf;{1KRtMmEm-&M#;s4-6j zNBo*^mAXd*;ue!4#0HWDaYpOFEVZ#(gXf|_*P<>y%(x8AIdh3jg2J;4h!3JT>p`mDIdqt8%ZO?$Y(in?;96h z+$6mC9tX$4=2Y!acVAz7wn9?6y@Nx>)1iC3ct%gm&CU0P+?&cZju5ec9rO}!%Gs%_ zsj0C{x^F(X@CKJ7n1=i1S9nI>gT?SrhdvPj2IB_7eIf2>%7#iFx!DI_ay0~aFsubD7BMCR(a_4qpLJPmxp+?!mcalhqFGd z*i6Zv<2X*?+GVe>MNd{ULaS%o3o3GR%}fmq=W!v3h`czfU$_4~K;%E_i5`gY^E=xI z5zs6!DJI7|u4Tn#AMy4_Pzk(T?oM!pSB^Rt>(-B`XltKvLZGM}`#CgKOJcx`E{xVJ z<(Zi7+-Z1VH`jawK5nrJK9my$-M^`8bFew?y8E4J&$KUPT8(Aj%Gh`*I$z{Vgw01% zB>4RILEnw#7#b4d)@VIhg_i2Rp2ukV$tf1PEnw?NA$EDjo8qxm3%3PdnfwtPfzknd z@{r3nSz4eY6a`t-5f(b}3vx3unzR%^8$(|&e&fR~HaCDzRJ#6rFLQ8lI(r!jEi60} z@|Af5M}z*WVM;f$#ZSx|<&*78*kYuRGsp9i&lM#lzoa3l z_nXr+AQc8tZ5J+&)|UoP?yQO6o4u9#&)&h?N{WL+LlaNtgXlCOX+`P+nr>P@6}o%( zZq0V%X=sYi#qsm%NfDIhA$+qO76^<)r>RnEqXGwV>gw)@hBcxRoqqU>@?@~ZNW|ly z!C-ALOXjf*re{n5=Y?yRC)hyEa-!vd&1~cOCB$rJBTq#uY$6R3`9x8X_TFs~c{#af zi;IhFM8w3puV24bmZOxCk}?726y3GN^YZ1(s^E@3V`JkmlzXULP3!w|qNlBI{u4I( zA*kf7?d_MA7iY)c&rkL(#Vec1bW2}i6xXfiTX)5>>nz#VUf|4)zP(3_Hdk|WbhMbP zXqsA9R(3!TB>CavM>cT#wl&9_V8bSchG{dr;0t2OtPs_3rG9VN(Uaie_8nN5;y=_PUK!b2EY4=v(B64@6#)*-VU#k?bri57YFU zyiWtK)6oh6%N$D)@kn=A>CKg|05>oKq8YQRA{bkIeGQ?{hsTGo8x-~zNue1Pk!wLrwf$SuD+u9##yIIaPvNMNi0G6{;0l>ZkT@f zO+Jh5qn*X1ND6M$kdPCR>4>Jzt}bp6;~EZUJdEi)e?79&)}AocskZSdu^_A2!7pe0 zBMJek8rYup(XX%9U21{xY6tR$2jG&w+#D;F0wHj#i=)19JXa-SE(#twK0XeBYT$9N z(6-vQ*4DERadC%)zELd+Qn`Sv!oPRya&a=+MBu*DMqZCT^7N>>(RNm};cU0db+fb` zar(9#7YD}%qeSpbBq!)k6Ec*Y3#1ispF-^QiOHNj&{VWCDn|#^YxZCY`4p z0(LkvdC!iqaB?@1&(iLADQ;W49?;f66tSRFuc9P*cg5d#c*JN2pZoH5Td&06(JB38 zrT@dI_O~0w^)}mxwV|$qt!bgEbvrHjBU)xA@X;(X`~XGZuI#uX?qyGun_f@kA7+Z$%MWYTb(vP*8INs-B)^CM9xKi8F)=Y2`26TCwbuzEL`g|W2AJ+ZnX&wKo@Ht2iM3dR=iOgtdwoVN{@6NQ zot=S@u{leT*gx}s2?h$LFRpf_1z%%H30Hwa6900uADVHqFG`4Ro#P{*-+_E}w(VEX zJUtt{t+UzR5J(fwdY`f@itMY!buA6@HuAnPJ3Ucl-xj z&IYdIAF4`HPoH+DS(f?tp=Uo$c!NpnYM=#T)lq{%kLkA7yFc>C=NstSXf<~k8JDw- zA|Gb&evs!dcRyLnVc_GdyrEDkr;FZrablV5eXgy4V?)(NMNR97Yc_{-1~7|EBqI(AS;AoyGR; z+S9Fioi$2bJwJwDEx4R>AfD@~D=YW&+G(^@8;8zJI5?Ptjo@nPI;ppTu#=J=r}7I& z*l)VtWy2*_Jvusi)#xY|bLKYtZjTj81{P za|lA$Ms>~^!yvOV;^N_=lFR1UP8q;He3ZhjcA5J@@7hm~7NZuAS5i}Joi;{zCARQr zenU4<%`y2|QWLa?K^sr<7lEnmo7P)(VWI+Gf3VlhTMeJC7g!jHQbo{?J(_t4@}rC= zP$vrrbE1wkWIF;VB9oKpQn)wXzk;+~*rfg^x%&Gz`~Udy16$v7N2PFm;g%Ex}z7i-L9_>reyVt}G31g}U2 z)+m~Ga5w&d#XW6Sl;oqB$HAt_)K{wRV$JFzN?wz>WQX2kiste8`5Ul5wQ7dv2T&pB z7KkH8+rXq?P7#{H*sDeUMLN|S&h^tzZaLr0U1o9jn^Y7#N9X{Ev*-Niu9A@xQM`aH zZ6JWV*PFRs{)x~^?)g1pVPPo;`ycKuyD`@PI;(+*E8WrHF@}y#s)C}T-b15r;VDch zuIc#o6?~H5(>40I?SFP?L8PDaqPm0Wgnv;!WbV7rcEcXeIgaum)^*gZtSnhMxqL1K zUVdR=oJjKb9PUYvrKI46g@umSFa?T^B<4!nX(u1PUTD39)O?BCz4uQ^cNJZ|(Vsng zrqP%DuzUl6VRG+Ysa%<^IL>wuQ#3R+G@Qfkpo1&&);q418T2Ibg}jeO*hM+5_NTc4 z8zY81#dXnN18s?aaj`fYePLliuG(sxDDFc`$c)_@gKG9umWI7vp;421zS(%xI;Jb-59~YTlmu|NxjKC&lv;x?3#Uv&! zPJd*K|9R9O(S{y@o+x13$Sx#QuO3A!BC(Ap(kw9R`L?`Q&{FcrlP4>(UvI3XlY@vj z&F!)^MV63|@ILmmBus#l^E!>7qn(>D?fFcJy5R~v&7Vj8D?uCtVN=EA2d{sCG{5aY zLxxA~XER-Atp%XOIb;SaIp&wu!v!f@Y3XkTImz~&Qk;!PFJHK3Gut=Z6!kocedw^f znxs4%-tZe?BWeBSdAsoSiOP-o0*#^tA$Q5-c4V?!0imd|EHm?`KVdx)(cTSCca+SB zpL!$2E{=>GQ$^k1yw_yL65j1#sHbyZO{)Z8u-(5!)cVhw>nK&c+@yz0?CjT95orCE zpbZm3OfOZtySMjS2vf@QtsU1BnRN>};Fh{eP)bV5!$Lh+U4$Wps1q&OE}b86Bm)3A zJajomOCjF-`dwBbe`>iEox|Y)H7BQbMaMW)zX?d`wk%*X6%0a(S3p3(D9TY&2SbV2 z%g`Ayp3AcV89J}6&GGV~4+U@;IFiEme` zq6=;HBnd1oC)siwooql=Uc4w&5x*aUt95cPZi;PmP(D}5Srau?kfFQ#r}$V73#=HB2=5K!`n2!7%})7H*E zJly*J{kv9^+ukxU3dyxW+Q}bV)$b}b3wH#hXV5eavJaSjtNb8z&kIiH*p^|q!eJPx;lioXzf-?AH05FkZj zSxYun*0Qrfda5gy9zvL_grM~~+o?I2u;jLxs)0PeaQJA{9x0`vK~~(PM;B!`w?APC zogdM$ZTqMs8iIjKHrK%r)#{H;Oh`%^pzm`&eCi>|89Eb4WaI`Y!?##?)-w&qdmucR zosJnrWdZMIK_|I`y4`fy7)~Wn+_7X&bA2v{J^??0GIXYHK&I{6x5j}+uhSb)*nu`L ztRg}47dEmgI5!1U1fQ?vTiZ`$$a-_GAHDFlyV8Ss^knA=bG@|FF3u)bQx9aNLJB)M z1mT57aO|xdvQJCN2Ey^#BZHUVKr(&K7U{U-^z!>^7D%m7m4tU%t4_C@d~}dyVu>~8 zB(0P!y!GS4;$jnph`VDuC_11DB_Aa`dmL@7Ks~I>sw;3VZ@9~il*`Er9-QB_7 z{ufY!!APOj>THwP#c`jHyQ$*-Q(F@gMvWrvrE`}7DRMVcZ5jg1SPtFwB3_>hPiim3 zfF5^#lY!S_gg}Ym*K1bOb)J{}Pnnd0{S zZXBK`C>336}0M-x8oVpMN;HZrr}N+_T5=Xe8Zrti-TpJ-^`BjK{Vc#vZ79VG>8+-)(@M zSSM`(-a^6s5*@^qhpX)Tak|b4)xCfLiUQRKfx~k5PUu{%!-@o_q4#_M`NbXg(PG^& zkes?sl}_zObzRadM~i1t!5jT>;eYQO-N`Xf4)ub;&IhUoD@IoFN=3VLL()^z=)HF!tzrxszC8%s0hIiED@Tz3{iK)vM7k&bFw z0({j_3G5qzaD=`aexuGwH3AK!cwqNg6G1>HO6_xbItzQfs%5~g25s1tgZ2;KR;BYj zo4vdsUu=(((bA$!abIVFHuv=P9n$(beWNt}46?o}I24$5!^O#lE2!IHr*^8Usym%* zP}eRql}{f&po$a}nA+`I6*nf{H-D26DcHG@O4c>etADW@Cl)dT;7_@RkYH@x0;pS0 zx1L}=p`XG+M1_WS&eXg%@=W4S?!t=u2~3^%gm-xZV8{t)RX(ZhM=*9lK|vQlYJJ)l z570a!?B`{S)Av5^1SJ9jhYq@mjWltHHljC0WRmSZU@EqB^M%a^?}Sr79AtB^Y%ZFC zW@G#Ucz5xWwHF}R8eTph9q5W2h3a3{uMWT22X*iM!Z2@(PXcuu z3!OySpEWe}r7o+wGWsY^u6jWY%;_tJ0D5Jp28MCohNb>%mSAnEwt)O#^*nLX_fkbv z!P3SKkI1yjZphltt_T>ra&Y^3Fde2m?QltHm$}31(CgVdb0x+Ov&-v>gZV&(apQKjJL84J`kKk&_k+CD1%Fk%_oznbgij+QkB?;JydI2|jYN@z@$+lX| z%frKOJyG!le2+|kit^(sNr<06uGLt{t@L;K9xi+$Zr$ zTIS(ARVEL>C_0@6lJi%>^w%N?U>84LDwvLxne^P$Y4ohs%6)3<`WW%=Tdb_))Y*vRW%oPTTXCm`kD{1rrH8aaVq9Lja)5^-l5KyxDSb!Rvo%m~)9uZ4PpXh1f^BTMP zHIoCI+lO6^gyIlD6)_{KY^EntybiRWohMMA^R9wr*7)^(D!u?c^|$p-8_Obv`JX>G zdr-t=v&2P5yIwC0C4klGAA+z^4d|U|wRzaj5Auat|39ZQWWa(Fo+=oA6}&IIpA?pHd9@|(&KIyJ27(KTx7a7`w+p3VCC9a z0q?XNKK}xo)1e6d!`btxZR8T3Aq_g=*u8#1At4VHQ1kQOo+k!H9Omt}*YMq&yNmJl zx7TMFB(_Ha+X1$L*N&H)R_6+_d&pKa8wSxF>;WLy$6qc$46{85f}modlm40=It4tF zhic;Cw2S_>lSJt~K!xnFRss#qs3z_!c{keRO}GziOvZxFl!Desg^9>nr@oxLyft7} zXTD}5?kv~co3zNuUSvRwNF`a{Fmptu%*)F&j){rMi`eR*YDS5=$;Zm$xY|Ep!e4>n zD6qvr$ z07di3k&aB>6&+Y+hKmM=ezgtFwj*v|Nk1_wo!D$Re>ejT zlc1s1CqkzLR-(>>zPr2I85$a@_Tq$*@)Wx5Mda@D5H)Upb--w>e!H=YICkHq&+STZBl@X2!`nCj9naA_KYaJf;^C5IEcexTa}QihQlI zW;FrLb3xN(lrHlzTHlEC<>q^+dJIVQNXU;CxhH_S3m&3wTcjBx;-=*f&RLyIU z$fcSD%E$MIaJ8lWm0Y%@`IbWB{e%O6=9YNMdA^i!ZvmkA+8M-u4Rv*O{ILNvs4Nz2 zQk47Jt5>h4#LNdXH^G;{w7TMyYU^S!K8-gJn08eC4mhA}cKyaP30@ubQLi$Q-#X7l zCy-ZC@>v4BZ1?d{>P0JlV`LzbL#-6MJnqZFCFgjtJESC*_v4~5BYUHgQeyky_!Pum z#2Op4fyW;B!R78!$mDDa-%G-HF`q^ez(!;NMNO)yt5-T6?=A@>j&E>Vb+Z0*$`Dk1 z)z7MmioFgp)UO|(f`$wH2j~q2x{=)4-j5QyF#E`)R24+$?K+$;6~>yxXX$(hOnn>Q zt(tg3M8ei8*?MnpkM4U#Daet2MD&(40fh~0k)wK^>qFCKh6RH#hS0^TEj(n9wDTYvS)vtnvOnKMC{$ z%c1O^RS>{vcmd0o52z04_^lvnqPPry1Z}hJIE4fr5%bUaV}@L|_lODn;K@Ul7qW@56Gl+;jV+!Y(g4*IIlhK7c^!`qKN zo;P{J;7ykoVW9Ll$xOh#6cc(cA)Ylf11FvB+n~UIge+2eh}TS({zym zDkYcQHyF4F<}(d$=LdHl75MF4lFRkghdFxf01-B^6A+bR^T9wm+k{(WRCcDl*iLoZS- zdOAor$%qIv`ceG7yPNvFm00OMe74EwfjfyQCuqFsIkwZd+|SF+t!eH~33G7Jkoh+* ziqlh~wu4^$X6-G>!28JFll_MQ@v_5XKthiPkn25f@IF8BsBz+o=T8%e*WsyJ{}dC$ zR}4DI>Yy%|%>XC+-Hc)W)Dw&ajOsg&8*d;xc$|R9=MX4|63<`>J^tffHO<9tZf=X9 z+O0nX?J^fZpi|zr+nv*~VdR`uC}`I_c|nNyF*rEr2i`zdKjKmtiv^p%sT9$kTTxN* zNb|+(wuONV85bK+v(~uR)Czz`B6~e3`s2Dk2?{_G(mYO%3BeB!-E53l|4ygLJjZ9qP6B;ece?0Q-*THPKV^arB(47Tj zHE%Cqg;SBp&2pvp)5ozFoAurxWw6SGQ%xVfv=M(r3kxK%ih_YTiza*-)J($Z^HDSv5QL6 zTkv?Tp)rz$G?Qa+2=PvU?KcG(x>wOd#OuWI6!g+BhJbYTz&OYYb?Xmq@p`nFGnwK2 zHch`)Z00k)K~PN;0CsfdY|5dpvb;~UUJAfMqEQJvj}x$OIH-Pg&q3_W1Q*dbgo|34 zL?w)OQt+F`P+(xexFrG%DpiLD2Yb27KO(8JVbHFX2T50{e)i&^Iu>B9{;i%=vDs{; z)JE^Y%qImiK9}bsfcy&U?CeYtC@1fEx{Yr1=(hzJ$lQ3)(yeGaQL#n|SUV+=t(rxC zB)L-WxIH7nKG2q(B_B5dur@2e&WLH?v1M{{a{Sg~CG!}(Nv}-YXSw8w(6L}o#GOjI z7P|{wK!Sk+8>E>6+{->DwVn7d7*L5D0rhs_wDTmpBwasfCTrW+6!Te6AnmF!KfjaT zX37kthbm;LQfr@e4!W)y&!0c%@wqsQKu!|qf)1jIoLpxph$2q~%3ZoQt$B48=Hksz z|4{D<*-=CS%_l&r@q(_F3^J2}T3dS@$R7=cPjc3_>bGZ}UQcFcW-e)H5QZK_r=-}t zmu&?Z{}l)spd=C0skDe$o0$S*DpkmAxCHJU<#}l(EKP8GHhDvzIMGIl3(+I4 zy)9=EW9&FD;Qf z)k=-SkKbSii(c8quht~{)K|WHQ;pZ7V5|Ps^$;?81fzEJ`F}2DM5r92sifOG<5{xT z9d2QZC6m%m)$%A!{?CgMx}!98Zn#8S*vbZl_frk}rIBy#%>FYAp!1)l+J5WmdQBNP zEz=T{8`2Zp61EI9t^`!StpNoc6?gT#&fPdyPqV^gPXyM}b8o0yabaerx%kQWyZ$5+ z4d>8*^D)@AP_A!&182fGu?c@?H)H%b7cU!Ec~jzea?kb3iAUF)=4r^%s^=ppMWlcG z4#e+-)XdUE%x0?v(UayPeeN7GVo6Mou($ae_Wyl@FTiuZz>}?>tU9$kW%zR=C2w&E z-6cg!{Ab=>C!Ci2^U}(=;1v;JlmGoC@S@(Crh)WglB>ZL=>=&ZkmZMbV;;O`>=p9+fwb82=dWl0+iu-}~wF5W#UQh_Tp z3bFs8-9O~(N6MF=m6Z?^ujQ9;Ze`Nbea~ypXYbAm1-;fE5Wx?E>OTW9;}E!osQaO) zsN=4^_sPULBSFN#e4e39il;rA}rQ-}l>}H%BJ{F7EG8NtrepQ&I1X1=}h#$NuBi z61E+Ez{@)@nZ*yWU?}}#=)Yg|^9365wJYXlK-7J39wkT{{lv%qhXemsGR7)xngHZx z(jO;4s0=nm*gI_EuctY6eh z#{RxEAw44Sj2b+7`l32U^;1D26#jS-iy3SRYsG?B0?KCY0w;8z)*2%E!<>O{rJ^An zOxbtqhCABlQtUroL;?PcGXKI_de!MJyqHD;DrBiG@!NNQ+xP!CbaAu@2PFD<*(sPHoIt`P zKv$-CU7jC01HyR>ltAnP0;2#bZw6A?l-yvDcAY;=5925U4TW*(Na|W+kMm7XVpoO` zGfjX|AUG(VX7e`$i<{2jwj;<==lU9$6etCBze!gg$krC1r2n|TJ?qmod%k^n0oC<@ zSL&Qpf}(01kW;n*PWXa$V^^Ka_SXJBXx(+iTK`~zFM{4?C7>}{Pd!MdK%>Rha-t&7 z3$UR&BQP*3xCx{w1$^}QS=DmyC(EYe9@_>dBu8Q!4hCMBwwp4%_Gba(kfPM&eZC3C zYAR#cG~H71>RdBVnLYn5S4S+5uiQEOO#tZ;dtR6iF{Z1Xwzu2r< zj4TH> z_W4{a2I$djBe@Jf?dj>@q+TAii&f#ka1iTJsIW+V6rC`MZBT!14r}U96M+vtL=w)p zXTy@V70@c5Utcic`31$hs29E{Xbrg7IG@V)$Z3W^&(RW-#_?T|ev@eStpmwzFf6dB zYuAqvo|Ec5VQD#DW<1k|M-%o)aIYEGBd|M+?BU#IzQNxfWA4qAwzvbHHkwr zbSZiCHTdDAzg`^LVlKM`l))_V-w68x5YHACTYz$baoK~($T5Jm;Q^HZOmY6WB4tz6 zm!?2>u%SPPFh|*~hi^1Kzbs!3Ocr(k?E9*n+96!?T=?fZ$%c1XbNid+eeOX+wGO7I zqfHma`5cx)0b1%Jq%99>LQO}ZFL2J@7JvtIrD!ug1QLFAGDKzS63~d zExUlthVNHbNu_BHk0MDa9gXAE%VRp|ZedBrdU-al45n$o^ya1&C%vh$@pyu1iaD4P zNJ)!y0u6&1l38Q}r&n;JkUd6E7ahEzzSax0e^Lg(T%rXY45!&%vnJYQzn==uRu9UP z!z$2LAEUeYg~^G?r^8uvnRXi0P;I>~f>4I$djoIrh?!$b^&G_A0!`Lm-y~;l?g1Wb zkfVN+2g*~(1;r`X-#G_NgUI!WMPT7(oG`=(ffSr?yAJ?`2gALN>~I1T^qFNvXg$QDgec!H>~x= z9gILZgUWae+{PA^_itUcV1Q4Wsm-z|@3r(9aGOuP1S*5M5LgxtbLtBBb31L04Or@1 ztE@TgBz0O-`Ri5BRQwAf?x>5WY5EzRAlCFclnvBLl}Pn4NCWUNNY1rXy_lZH&tX&| zyKH*#f}o=XD@k9^JIDzA33!$*jF1Fg(+NP!8-n5LZ1Vobvt4=QY|zso2R^vQE$J^$ zZz-#P0lm^uT#5Xb94~76ak&$B3!4j@!t~Tgku?{r@nTKM=cEm>ydEH%m(UYV(k!mX|CB+Fp{vfXBm}?DiD0$r)KJ$sh5>p(Z%)lvGuB&K zBS2FcCl+T!JI6gslR%I_J>TiQgkf*$3^lyKE8o3XuI@s`PEE|F>D<{iwXs^_)0@w4 z@<)zAwbQ_rD_52O4cVy{F%5Vc(bq<1BC9brOky38$~vF#%BF0gU42qqIud|3 z{kA9!lc&E%O38NQ0ke313p4@4dsSU*2{6o5+v#sx3OMl0PHH%?RwdrFm^=ym$EA{aFs znZL_3@H+3M(yGEbG}nFnVOq7`m^sNmctkeRR|1+}bFvHGJQ_smu7+P|?!GrxY6QcX z0gdbVA}C8cPBG5Q={l!9)TEY95c}ckfC?@c<6+CCPVvAoJQ~RR2&zI6) z;PGDFSpAnuk{|&FbXDrmZL4b1Mh`FTTJs;k4?ES~FWdeu|*U9rIR3JgZUlqa+-l87)(op z=VK}vhQY&v&Z7Pt$Oh1S>FR=iw)6kj-noZEx&411HKl16%1{c~wM#RhwnH@%IY)yU zLrg{`=VKxoF(^es2d5MUHJD+hoDE~7gAy8zvm83e7&&FeXf!;__q+Gzdp&@v;e&B+aWGd1&cVAmq{mg> z-x)fFtOJG#yb5-tXf{uJOrsXGNMKEL8Z@=<)cso)$PrClOXOH{4iFT?)=EP>qe%rbBRgZs}U3W^} zVws=Qp*u~gBHrfNw<~}Hc z-krmJDpAJt=mozGX^VDhU5jd1->PEP{g7qX1sJwtW1(+BJvpZNDyUSg26C zq<%U)@&_pC8FXJqy5Jy)bQx{z{r`?ZbELcNI2}P4QJ&b)q60=7Bd;y0I(~mQ?upGg z3l#^n73Kl|1*IVe?n$@CsFTERsIrtI!g}1?TKx$e7b8Zw%3P*q$BMAr&JN zquF0W9-@S4cHAJ%dLb*w$v}#QxIuo0Jnof?f$PN{SIMuf{kT4YtV$k)V#!7Dt7E@M zNo2qa$VfzTVW8?}eV!MzO5(l*6`llT3QOVn&_Z$r_fR<}H~LHpRxA9F@R(i3K+#mb z3Hxy{t?)<6#CXCihBYhSAEG7&xu92OA=PloS&M%LojR;O0@8u(Z5oP6%6J$R7En$= zxpcPsVWxtmE`#y=(sh`4$q06aZ-%a|l*b1vjs6~~p2c}jo&y;$CjYhg`yZ72rR>nM4ib+i!HH@Aul**4d;4#4<1$q?vhNG^q<~V6Nq|!OI zLRkVSn{!km;Xx}*4f!nC#ER*oMFQc{j2+sY7jHJkmCt5!LObL|xj)h}W{c|<`7{Dw zNd3~*!bOsLd)r|<`~8UzXSAJ6Q|ZLq_}$3eP7pSrF0WB*N1L)qy#rl~DSk@DEXsWF0c;mf?>HL67g_ajY7!T$XxG|8 zG{dvOgENL5(#x@+MNmiCc+UOkxaiagmyT=cK}jTiPgI8;#9?tH!?3t(_2|d~0x$dZ zmkGNBX3W>KI#x_FX(aA#T*wW0!WtpGZ@)-JDL!0;|ADcDnPLiUi9d{JSATVF9nCov z!RdIBE$by3{p@8qkC|l&^jx2C?B(_bn5+#BC)jl-%@#-OYqEs~((~;*LNVGMH^7}E z@y_Z?;rcZS&cg+udkfeBq>gX?=s5YFuoHhandyPAt>_2|>ep3E^NqoUyW0)&hBQ$# zorj-HHbU<=1-IqcyW}{mrZ@HxRMUmU>(>hFid@(sp~EM_1$J&WuVbk^^5TyVuQoY( zu&N)&??f__(}Kj!My{)$<~-^WHIl2wqT@@Vih_Q5SyIWT!g4}@ZBGVrx?i$LD%$Ei zL>*#1-Yms1Ov)0_?px2iz*u$VCUS1w`gX=!ri@VfEU75Jc9a?Gf-DXSR~&u%$lW0F{7zg>Mhe<7>$HX>aNjM>Ia=n$aQsKCSCZDvL};>)Zj&R zV6?;Mf+e^ws8<&7B^h!~k*xN0d?#^?@@kZF8%!_Z8FkK+8#VjHM0sOo0e)AVzIHAt zeBSy4+el9KtZ46V_^M;RzCsnW8_KTOkQ@Y`BK6gK`l&WKWgR0Jb(q2i9V zPW;89`X^x#e39j=9F9eC-NCW2T&Q%1J9c-p^We3h_Y^2vJA9A5*pKxt+)Ml7>nLPN zbofakrI=_N?$o3D&SM+F@$x?3t`THL6s1;H&-ILn3Z#X@oS859zGIMU(rB!7tuATo z(XG15%h>s@%?xc{$C2m?@PbM|?=aV0;WX-KXt@*br!zYuS*_h&gQpbSep2SiBIod| zMLU#4&waev{<-C+3SrAcn{F58x0t&e@&(>|zKV^c&7by$_R{QQsxRSXs^*2~3lRJ0 zfkcW%hdgQ(3${J83~DVjDDy_c*7ufRV> z-%0!jZPRGeEyLTL_QcJpehqdYU_Ntc5QZxwpj22Xk`Pt?@#i)wNAbPo)Lnaj8^8CFE@6Q+*_&x>3fef?Yhd_0xs zp$wJ4Cs&crIaa>Q9Rmz09UL?4HzUlFolf-t7 z)iFjfCn3gzb|O0u^cI?D_&ByAf@8-NHTKMp3$3MXV+Hx9oVrzqRnNkCKRc{_^L~ox zhd$fm`5_DQ{l8dPcAbcczSG&hMlngEO8t>)yPZGUzA@yX6bEobXMskxc65G3ols!ZV&lh>lIEO{cG^W?yT#$0SA@di1>nYu7`Z&NFuj3-HJO37iKEYVBP3PQncGf!i zeAJt%I}tM8tNoej(llZti4NX}&Hjp(|a2NScSA9jmi}$i~1>moS`@oCKWnCls3k!~J4W zTs~Z@mCgEPB4zOb)m|QhYtggswhuNoy;j(v)t z_jbFPo(P3G2?-oDgq=XmV`8xJi9PyR8J%h6hGgYmun8^K-NGnz8_W_l-SH@#>BUWY zwgC22x*iaa5 zB$Ih|3jb0+4F`6Akd2w8$eU%HN2i=^qq>qWV#3NfD(5mR6h+B(r0DqmJs#I0>eCk?{uM{(#LP90XUvf(sp z3^BL&rMJSf*}}EXp0z>e>Ya06JY29+al&}T96`BuJrfF~$3~7B(cnC2amEemXmf@F zHSE21g?4%2j05{VKgbos=HF4*^RnX&GCGVg>+G z1=3)Sql4E90`j0m(JA_jYT>hjrcwPKJAE_1%CtDy=J9jEkXC~wJEg-I#` zBN{=<8x=u!KZxYn5nS&|JXUfl7MSmq9aXZ$_+e7ece-r-K;2}r1x;8qRS=MSwt$!? zT{K$QHfB%bkqyaV=gSj2Q_|!XKSv3PCMuR4Cl#57NmsQp9Wkc;edP5UpzjuTzUk2K zM-)9RFj2vLLLb8jzIruLXPc>Z;;${)hZksuajs4S{k2>y~-h|iXU$cQyQSFCBm!bWGx=X~zzq9kdlnd!n) z`O=gfOooYoNWM3&?_tgk)J{QmnqZ7#3VM~^Q$?J;Z2ZT|zLPT41<61L(&AGgX^)$~ zzjNPMrIPS*lUq@ekG(b^aH82Ef(7NFZ?td$R2D#va zI62hRLMYrP`k2wvn%DuVY;*(*vO@S$xV9u~<()VLUD6W&E2@b}{2xTMm@6LPVq&rz zj~gEY7vOIJ`F|7Gqz(S7qW*ucqW+J#$gjE?2q4@BM*uwZDDWdW05MOkal?gYXw{bO_TjKmF50}0|4nC2g+v!&}>taKrQ>7o8r#6y9u~mxjwLFzj=Ak^he-=zW82GaK;U2re1ot?483qgn~(D9B?*2d!W}JAeEN&Pv8hZ zA)3Ve$k(s6mjIMFBDM9;$DN5+K{*WJ0;2*Hgqa3KPoDF8T@(ZW`PeXH+&$QQG<=dF z_zpO*!3h};1`ty{7*w~}p34W%u3SZ>yDf;|Cjg|cGuLd~Ugh-Q^XK!r9OAW3n4BD( zmpC}Frs$3>NS>a$a=&yYIy$-%Xqou$L(iW%AEE=j$`@eP=q3LCOduZQy#K)xq`S{S z)(Ai~or<^j_l&&4U)OYREgKX~gm?o2AN!iVVeAhx@D$kIbzvg75{v{5AaQ?w@!Yj3 z?)2r$6{N$Hu5OlJdQyW|wCK?+@$=_TOJ^Wx&jOcBQhW~3R>uIQNBuCRdC~Vx(>fRc zr;^!kTU*DwXoKlsK()V7PDId_Orw0D%GO1Pg=OB}oUn{ueBUB54uD&Eszd2+#J~&S z7QWpd_)cvz5Z1%|YWqqeK7-+mD-9~o$AW(w>GLvHrf^#45HMhnPv)8Sd%5lW+@H!{ z*~iBcdK(W7MZN_B!^Mc&M*U!}t?vBy(vf1vaxN>h(pB_woiu*4`>2=o66{fS?XMUX z%;LzcIh9e_kn00C4n~e-0e;p6cV!qxI(V@5SzhDvEZ<-qIoh(pzqx{Y0MJw$h)Ngu zt^h-3htjEQ9frStO-@Tq{jq}*I&@n8Z#l?-*tULZ2jxaMKt;9<%>hS;7Z((GNU8>6 zs>_53XmIYpqMJnz9JatY$d)P~R?xab>wX*UtDGzr$2pW$R8>_$ZtxKXoMPuiVsVIe zskK{6hU-nm%iWg+IeZ;`oJdS_KB#8fQ;8L*hosLRECA~_0JI^94s5QK2sEJP0kb+p zZ)j+E?xqs~zF>F?Jn@eYqqwc_Tbh~_bU{S`ki(iSF3$ne2fMf+(k}ja9*0S8pFTUX zJQlhY^$WmUGx^&#oqYsQ->O_`v>|5wIAA}|U{9Z(Gj?!r_>R^L@u|VbStnOK*#%cqjm@{7^~ zixIyWsJS0JS;S*M8DWA7P%jGW=~);K_+TB-=O4gMu>bG7-H$xAT`%Y05tox`S!a64 z7|Klity{AI>VDtU+`O-)rA4%~j#;PoTnp|w(6?gDBkMI*TY-hB>cWc~8*Zj1Cw~R# zBIfJxu-Xz;{}K?9W-UHj-9E{HeUJOTO&#}R9~_{@eYl{%qPsrWv-AfWG)uP{#GoSs zWLqy5TG9Xv*5-m;5kh?iq}}V0;j6xev`ar8A2jEnkO%(d7y7HArzT5r!u|g%+Ukq< z$YsqMwK~+Ml8Olvo4$Q3I-|8a3g@PyHY6_p(7~$S7nUBj_G4F0DCV&}MOc zu6`XuMd{I@8j{qbRLhp{{%&@zZfUQorT+6o-K*6PXo~WlQYMGw(V@3CwDKLVvC2-62mJ~;oS&2`35^a?*JLi6lBNqw&zulqQ0nG58m6dLvu>PmopDn*1p8dXj3gYxS zTW0ao-QrMd6&oC9jm=+k}7pnL956Z(5k}iL-dm3`B3k^4eWiN#oRC})~4Lm=c|jK zBDV+xH@?dL&bx78$7hb|9jQmmFp|Sd@fPxm<;5pLp0;>&AoQ5(kynV+>-=>9rAK{M zcVQ%jV4YZF@u39&O>5(vmV>G9`z59aK%b+~q2v)TW)-ti>HT&? zf4$F*Z9%{52Sk+iy1#5zkIz&7&%>q7U;cG?R_oH?&l}r({yZ8GF+)~D{7Ov7(_eXb zed4~MLAwG1A$e%k@WR0AYp+Xm4gL37@GGryC%zPqOM|D zq;JNLiR&fq-h4&b-?t@@;JYTVdQx}I%VwI!29NQo%JiChlJxpKAP_? z|BAGCeYVbx6m5UerQvdT(bBC|w(JJ`SAKQqL?mnMcE*hrjOtQ2dIscCoZ{I^D;foJ zCsI?~kf!OSumU<=(kTMez|KI;Ds08I$4=(*xUoMu@RUZ1=xXJH# z&3~i(;hK`1EZZ9Q_)Q7=pNSmx6gnl+E!ufMJwhg6)0cD>!rPt)r*k&}Ze+mlycj$=JEXf__VP)5e|gTS)~lo6x5Rs!Q} z%@TEYdG6EAzBhMwjsu)#YN5s~N$E1!8T0`HOu1lvsr&m1kf|F3m0tjbD^nu#&o~&Y zk{g;2S*29Fg>J=35P!>u3t3Cxq!ON^5(XMz*vtd)v?o?(=Xc=EFQYLRdS53hdasw; z_Y~AF>Lx7ACHfJa`p@-#+Er!i7uj0-YZHQ_r)yk+I( z%>n}heSXbNUw#ge_wi(wm9_g-56jVVb+7#Rs+J?Tg`hs&Y<;8+aAz){;!~wJwfZ%v z+IDVJ%{mcDtw0x}9efD!_!idUze<07*TUm}U7lL)-ts-NE;j=$dK z|MgM8(a&13{{-4L*%DV5sn(*em8kmTbr(OXU7hW52k&hk90X-yi_!U3+I}=L&3P2_ z#~k2re>4Pum}{TD{-9sgr!`?<^W_U1$4fSwn#F~AU$AW=L4N@MyEn8+=ng_k9S_zH zgoZ{X@Fq9Te!OJe8dqg@d&BaSR45S}WN!Px@nue~el))Au=7$qJsF4t#y4c(%1FAA zKPaRze#dCzG`SjBDYf)gJF>I1QNkqx7N2wmhHLsMQN5*XUjC8$Xg0Qe!+}Ur z^<>q>F95T@nJl-wNEq$|?Mq8pNzhF>D6#XoAvyVtg-B3k(;y7syNsK*g{Q*6NL^uP zg7O%ylgC?uwI6Kvvdcj|1W<^;uLgkHzChi_J(&Z%vtSmDOC}SZ@0FcJoeQuF`}ZZ@8!>pyU0GR1tUQbEIXiaoCI*8UOWz#A!y71zXe>6$2}uMUon zyuag(@r0=|l0d+t?vkG_)qVl))R*lijvtS!Od#nt(px}&SWQ!touT8~bXey8pNN03 zcFFImVCD{-G%fk|WyjFLDZVGd-oa+e^SVtU7llH3^*abDVz6t-J#=q5}D#h sgQg!ogZy*ji%A@V%q=L|QqTDa7O|;V9VTrRT@3sjH?cCN8lH{*Uv3NXSpWb4 literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index 7972894..afcaf1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ -e . -aws_cdk.core -aws_cdk.aws_apigatewayv2 -aws_cdk.aws_certificatemanager -aws_cdk.aws_cloudformation -aws_cdk.aws_dynamodb -aws_cdk.aws_events -aws_cdk.aws_events_targets -aws_cdk.aws_lambda -aws_cdk.aws_logs -aws_cdk.aws_route53 -aws_cdk.aws_s3 +aws_cdk.core>=1.45 +aws_cdk.aws_apigatewayv2>=1.45 +aws_cdk.aws_certificatemanager>=1.45 +aws_cdk.aws_cloudformation>=1.45 +aws_cdk.aws_dynamodb>=1.45 +aws_cdk.aws_events>=1.45 +aws_cdk.aws_events_targets>=1.45 +aws_cdk.aws_lambda>=1.45 +aws_cdk.aws_logs>=1.45 +aws_cdk.aws_route53>=1.45 +aws_cdk.aws_s3>=1.45 diff --git a/setup.py b/setup.py index 79fd7d1..1a2dfe2 100644 --- a/setup.py +++ b/setup.py @@ -7,22 +7,26 @@ with open("README.md") as fp: setuptools.setup( name="once", - version="0.0.1", - - description="An one-time file sharing personal service", + description="An one-time file sharing personal service, running serverless on AWS", + version="0.1.0", + url="https://github.com/domtes/once", + author="Domenico Testa", + author_email="domenico.testa@gmail.com", long_description=long_description, long_description_content_type="text/markdown", - author="Domenico Testa", - - package_dir={"": "once"}, - packages=setuptools.find_packages(where="once"), - + python_requires=">=3.6", install_requires=[ - "aws-cdk.core==1.44.0", + "click", + "pygments", + "requests" ], - python_requires=">=3.6", + package_dir={'': 'client'}, + py_modules=['once'], + entry_points={ + 'console_scripts': ['once=once:share'] + }, classifiers=[ "Development Status :: 4 - Beta", @@ -31,13 +35,12 @@ setuptools.setup( "License :: OSI Approved :: Apache Software License", - "Programming Language :: JavaScript", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Topic :: Software Development :: Code Generators", + "Topic :: File Sharing", "Topic :: Utilities", "Typing :: Typed",