From 70270dc1dd04567896928f592a39f9295c7c9275 Mon Sep 17 00:00:00 2001 From: Paul Miller Date: Mon, 22 Nov 2021 09:29:55 -0500 Subject: [PATCH] Update to version v1.1.0 --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .gitignore | 10 +- CHANGELOG.md | 7 + NOTICE.txt | 1 + README.md | 10 +- source/.coveragerc | 1 + .../create_batch_inference_job/handler.py | 6 + source/aws_lambda/create_campaign/handler.py | 8 +- .../{scheduler => create_config}/__init__.py | 0 source/aws_lambda/create_config/handler.py | 42 + source/aws_lambda/create_dataset/handler.py | 14 +- .../create_dataset_group/handler.py | 6 + .../create_dataset_import_job/handler.py | 6 + .../create_event_tracker/handler.py | 10 +- source/aws_lambda/create_filter/handler.py | 6 + source/aws_lambda/create_solution/handler.py | 6 + .../create_solution_version/handler.py | 6 + .../prepare_input}/__init__.py | 0 source/aws_lambda/prepare_input/handler.py | 33 + source/aws_lambda/shared/events.py | 78 ++ source/aws_lambda/shared/exceptions.py | 4 + .../aws_lambda/shared/notifiers/__init__.py | 14 + source/aws_lambda/shared/notifiers/base.py | 207 ++++ .../shared/notifiers/notify_eventbridge.py | 98 ++ .../shared/personalize}/__init__.py | 0 .../shared/personalize/service_model.py | 351 ++++++ .../aws_lambda/shared/personalize_service.py | 111 +- source/aws_lambda/shared/resource/base.py | 16 +- source/aws_lambda/shared/resource/dataset.py | 1 + .../shared/resource/dataset_group.py | 3 +- source/aws_lambda/shared/sfn_middleware.py | 56 +- .../cdk/aws_lambda/environment.py | 48 + .../cdk/aws_lambda}/environment_variable.py | 0 .../cdk/aws_lambda/layers}/__init__.py | 2 - .../layers/aws_lambda_powertools/__init__.py | 16 + .../layers/aws_lambda_powertools/layer.py | 0 .../requirements/requirements.txt | 0 .../aws_solutions/cdk/helpers/copytree.py | 2 - .../helpers_cdk/aws_solutions/cdk/stack.py | 4 +- .../cdk/stepfunctions/__init__.py | 12 + .../cdk/stepfunctions/solution_fragment.py | 81 ++ .../cdk/stepfunctions}/solutionstep.py | 30 +- .../helpers_cdk/setup.py | 4 +- .../requirements-dev.txt | 4 +- source/images/solution-architecture.jpg | Bin 303310 -> 324249 bytes source/infrastructure/cdk.json | 2 +- source/infrastructure/deploy.py | 2 +- .../aws_lambda/functions/__init__.py | 2 +- .../functions/create_batch_inference_job.py | 10 +- .../aws_lambda/functions/create_campaign.py | 10 +- .../{sns_notification.py => create_config.py} | 33 +- .../aws_lambda/functions/create_dataset.py | 11 +- .../functions/create_dataset_group.py | 11 +- .../functions/create_dataset_import_job.py | 11 +- .../functions/create_event_tracker.py | 20 +- .../aws_lambda/functions/create_filter.py | 11 +- .../functions/create_scheduled_task.py | 10 +- .../aws_lambda/functions/create_schema.py | 11 +- .../aws_lambda/functions/create_solution.py | 10 +- .../functions/create_solution_version.py | 10 +- .../aws_lambda/functions/create_timestamp.py | 15 +- .../aws_lambda/functions/environment.py | 2 +- .../aws_lambda/functions/prepare_input.py | 42 + .../aws_lambda/functions/s3_event.py | 2 +- .../personalize/aws_lambda/layers/__init__.py | 1 - .../requirements/requirements.txt | 1 + .../personalize/sns/notifications.py | 17 +- source/infrastructure/personalize/stack.py | 78 +- .../batch_inference_jobs_fragment.py | 4 +- .../step_functions/dataset_import_fragment.py | 20 +- .../scheduled_dataset_import.py | 7 +- .../scheduled_solution_maintenance.py | 8 +- .../step_functions/scheduler_fragment.py | 2 +- .../step_functions/solution_fragment.py | 8 +- source/infrastructure/setup.py | 6 +- source/pytest.ini | 1 + source/requirements-dev.txt | 12 +- source/scheduler/CHANGELOG.md | 10 + source/scheduler/README.md | 218 ++++ .../aws_solutions/scheduler/cdk/__init__.py | 12 + .../scheduler/cdk/aws_lambda}/__init__.py | 10 +- .../cdk/aws_lambda}/create_scheduled_task.py | 4 +- .../cdk/aws_lambda}/delete_scheduled_task.py | 4 +- .../get_next_scheduled_event/build.gradle | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../get_next_scheduled_event/gradlew | 0 .../get_next_scheduled_event/settings.gradle | 0 .../HandleScheduleEvent.java | 0 .../schedule_sfn_task/ScheduleEvent.java | 0 .../schedule_sfn_task/ScheduleException.java | 0 .../HandleScheduleEventTest.java | 0 .../cdk/aws_lambda}/read_scheduled_task.py | 4 +- .../cdk/aws_lambda/scheduler/__init__.py | 12 + .../cdk}/aws_lambda/scheduler/handler.py | 8 +- .../cdk/aws_lambda/scheduler/requirements.txt | 6 + .../cdk/aws_lambda}/update_scheduled_task.py | 4 +- .../aws_solutions/scheduler/cdk/construct.py} | 14 +- .../scheduler/cdk/scheduler_fragment.py | 91 ++ source/scheduler/cdk/setup.py | 66 ++ .../scheduler/common}/__init__.py | 9 +- .../aws_solutions/scheduler/common}/base.py | 30 +- .../scheduler/common}/schedule.py | 2 +- .../scheduler/common/scripts/__init__.py | 12 + .../scheduler/common/scripts/scheduler_cli.py | 355 ++++++ .../aws_solutions/scheduler/common}/task.py | 4 +- .../scheduler/common}/task_resource.py | 4 +- source/scheduler/common/setup.py | 75 ++ .../test_create_campaign_handler.py | 249 ++++ .../test_create_config_handler.py | 180 +++ source/tests/aws_lambda/test_events.py | 79 ++ .../aws_lambda/test_personalize_service.py | 75 +- .../tests/aws_lambda/test_sfn_middleware.py | 96 +- .../tests/cdk_solution_helper/test_stack.py | 6 +- source/tests/conftest.py | 65 ++ source/tests/fixtures/config/interactions.csv | 1001 +++++++++++++++++ source/tests/fixtures/config/step_1.json | 5 - source/tests/fixtures/config/step_2.json | 124 -- source/tests/fixtures/config/step_4.json | 130 --- source/tests/fixtures/config/users.csv | 1 + source/tests/test_notifies.py | 112 ++ source/tests/test_scheduler.py | 15 +- source/tests/test_scheduler_cli.py | 108 ++ 123 files changed, 4400 insertions(+), 506 deletions(-) rename source/aws_lambda/{scheduler => create_config}/__init__.py (100%) create mode 100644 source/aws_lambda/create_config/handler.py rename source/{infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools => aws_lambda/prepare_input}/__init__.py (100%) create mode 100644 source/aws_lambda/prepare_input/handler.py create mode 100644 source/aws_lambda/shared/events.py create mode 100644 source/aws_lambda/shared/notifiers/__init__.py create mode 100644 source/aws_lambda/shared/notifiers/base.py create mode 100644 source/aws_lambda/shared/notifiers/notify_eventbridge.py rename source/{infrastructure/personalize/scheduler/aws_lambda => aws_lambda/shared/personalize}/__init__.py (100%) create mode 100644 source/aws_lambda/shared/personalize/service_model.py create mode 100644 source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment.py rename source/{infrastructure/personalize/aws_lambda/functions => cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda}/environment_variable.py (100%) rename source/{infrastructure/personalize/scheduler => cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers}/__init__.py (96%) create mode 100644 source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/__init__.py rename source/{infrastructure/personalize => cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk}/aws_lambda/layers/aws_lambda_powertools/layer.py (100%) rename source/{infrastructure/personalize => cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk}/aws_lambda/layers/aws_lambda_powertools/requirements/requirements.txt (100%) create mode 100644 source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/__init__.py create mode 100644 source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solution_fragment.py rename source/{infrastructure/personalize/aws_lambda/functions => cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions}/solutionstep.py (85%) rename source/infrastructure/personalize/aws_lambda/functions/{sns_notification.py => create_config.py} (74%) create mode 100644 source/infrastructure/personalize/aws_lambda/functions/prepare_input.py create mode 100644 source/scheduler/CHANGELOG.md create mode 100644 source/scheduler/README.md create mode 100644 source/scheduler/cdk/aws_solutions/scheduler/cdk/__init__.py rename source/{infrastructure/personalize/scheduler/aws_lambda/functions => scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda}/__init__.py (81%) rename source/{infrastructure/personalize/scheduler/aws_lambda/functions => scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda}/create_scheduled_task.py (96%) rename source/{infrastructure/personalize/scheduler/aws_lambda/functions => scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda}/delete_scheduled_task.py (95%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/build.gradle (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.jar (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.properties (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/gradlew (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/settings.gradle (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEvent.java (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleEvent.java (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleException.java (100%) rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/get_next_scheduled_event/src/test/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEventTest.java (100%) rename source/{infrastructure/personalize/scheduler/aws_lambda/functions => scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda}/read_scheduled_task.py (95%) create mode 100644 source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/__init__.py rename source/{ => scheduler/cdk/aws_solutions/scheduler/cdk}/aws_lambda/scheduler/handler.py (94%) create mode 100644 source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt rename source/{infrastructure/personalize/scheduler/aws_lambda/functions => scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda}/update_scheduled_task.py (96%) rename source/{infrastructure/personalize/scheduler/base.py => scheduler/cdk/aws_solutions/scheduler/cdk/construct.py} (97%) create mode 100644 source/scheduler/cdk/aws_solutions/scheduler/cdk/scheduler_fragment.py create mode 100644 source/scheduler/cdk/setup.py rename source/{aws_lambda/shared/scheduler => scheduler/common/aws_solutions/scheduler/common}/__init__.py (85%) rename source/{aws_lambda/shared/scheduler => scheduler/common/aws_solutions/scheduler/common}/base.py (92%) rename source/{aws_lambda/shared/scheduler => scheduler/common/aws_solutions/scheduler/common}/schedule.py (98%) create mode 100644 source/scheduler/common/aws_solutions/scheduler/common/scripts/__init__.py create mode 100644 source/scheduler/common/aws_solutions/scheduler/common/scripts/scheduler_cli.py rename source/{aws_lambda/shared/scheduler => scheduler/common/aws_solutions/scheduler/common}/task.py (96%) rename source/{aws_lambda/shared/scheduler => scheduler/common/aws_solutions/scheduler/common}/task_resource.py (94%) create mode 100644 source/scheduler/common/setup.py create mode 100644 source/tests/aws_lambda/create_config/test_create_config_handler.py create mode 100644 source/tests/aws_lambda/test_events.py create mode 100644 source/tests/fixtures/config/interactions.csv delete mode 100644 source/tests/fixtures/config/step_1.json delete mode 100644 source/tests/fixtures/config/step_2.json delete mode 100644 source/tests/fixtures/config/step_4.json create mode 100644 source/tests/test_notifies.py create mode 100644 source/tests/test_scheduler_cli.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2083e17..7807625 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,7 +17,7 @@ Steps to reproduce the behavior. A clear and concise description of what you expected to happen. **Please complete the following information about the solution:** -- [ ] Version: [e.g. v1.0.0] +- [ ] Version: [e.g. v0.0.1] To get the version of the solution, you can look at the description of the created CloudFormation stack. For example, "(SO0170) Maintaining Personalized Experiences with Machine Learning [...]". diff --git a/.gitignore b/.gitignore index 441f5ee..a38b3da 100644 --- a/.gitignore +++ b/.gitignore @@ -48,9 +48,9 @@ __pycache__/ # Generated test assets source/infrastructure/tests/assets/* !source/infrastructure/tests/assets/.keep -source/aws_lambda/get_next_scheduled_event/.gradle -source/aws_lambda/get_next_scheduled_event/build -source/aws_lambda/get_next_scheduled_event/.idea +source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/build +source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/.gradle +source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/.idea # gradle build files **/.gradle/* @@ -60,4 +60,6 @@ source/aws_lambda/get_next_scheduled_event/.idea # python build files source/cdk_solution_helper_py/helpers_cdk/build/* -source/cdk_solution_helper_py/helpers_common/build/* \ No newline at end of file +source/cdk_solution_helper_py/helpers_common/build/* +source/scheduler/common/build/* +source/scheduler/cdk/build/* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 19fd6c8..fcdd62b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2021-11-22 +### Added +- The solution now creates an Amazon EventBridge event bus, and puts messages to the bus when resources have been +created by the workflow. This can be useful when integrating with external systems. +- The solution now contains a command line interface (CLI) that allows schedule creation for existing resources in +Amazon Personalize. + ## [1.0.1] - 2021-10-01 ### Added - The solution now exports the Amazon SNS Topic ARN as `SNSTopicArn`. diff --git a/NOTICE.txt b/NOTICE.txt index 4498f16..2d4d383 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -34,6 +34,7 @@ pytest-env under the Massachusetts Institute of Technology (MIT) license PyYAML under the Massachusetts Institute of Technology (MIT) license requests under the Apache License Version 2.0 requests-mock under the Apache License Version 2.0 +rich under the Massachusetts Institute of Technology (MIT) license tenacity under the Apache License Version 2.0 quartz-scheduler under the Apache License Version 2.0 diff --git a/README.md b/README.md index 83d846c..717dba2 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ The template includes the following components: 2. Perform solution FULL retraining on schedule (and update associated campaigns) 3. Perform solution UPDATE retraining on schedule (and update associated campaigns) 4. Create batch inference jobs +9. An Amazon EventBridge event bus, where resource status notification updates are posted throughout the AWS Step +functions workflow +10. A command line interface (CLI) lets existing resources be imported and allows schedules to be established for +resources that already exist in Amazon Personalize **Note**: From v1.0.0, AWS CloudFormation template resources are created by the [AWS CDK](https://aws.amazon.com/cdk/) @@ -287,9 +291,9 @@ To customize the solution, follow the steps below: The following procedures assumes that all the OS-level configuration has been completed. They are: * [AWS Command Line Interface](https://aws.amazon.com/cli/) -* [Python](https://www.python.org/) 3.7 or newer +* [Python](https://www.python.org/) 3.9 or newer * [Node.js](https://nodejs.org/en/) 16.x or newer -* [AWS CDK](https://aws.amazon.com/cdk/) 1.95.2 or newer +* [AWS CDK](https://aws.amazon.com/cdk/) 1.126.0 or newer * [Amazon Corretto OpenJDK](https://docs.aws.amazon.com/corretto/) 11 > **Please ensure you test the templates before updating any production deployments.** @@ -360,7 +364,7 @@ build-s3-cdk-dist \ S3 bucket where the name is `-`. The solution's CloudFormation template will expect the source code to be located in the bucket matching that name. - `$SOLUTION_NAME` - The name of This solution (example: personalize-solution-customization) -- `$VERSION` - The version number to use (example: v1.0.1) +- `$VERSION` - The version number to use (example: v0.0.1) - `$REGION_NAME` - The region name to use (example: us-east-1) This will result in all global assets being pushed to the `DIST_BUCKET_PREFIX`, and all regional assets being pushed to diff --git a/source/.coveragerc b/source/.coveragerc index 3c91c64..20c0237 100644 --- a/source/.coveragerc +++ b/source/.coveragerc @@ -7,6 +7,7 @@ source = infrastructure aws_lambda cdk_solution_helper_py + scheduler [report] fail_under = 80.0 \ No newline at end of file diff --git a/source/aws_lambda/create_batch_inference_job/handler.py b/source/aws_lambda/create_batch_inference_job/handler.py index 9c163e5..0b6eb72 100644 --- a/source/aws_lambda/create_batch_inference_job/handler.py +++ b/source/aws_lambda/create_batch_inference_job/handler.py @@ -64,6 +64,12 @@ "default": "omit", "as": "seconds", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_campaign/handler.py b/source/aws_lambda/create_campaign/handler.py index 5e82fd5..4ff4bf7 100644 --- a/source/aws_lambda/create_campaign/handler.py +++ b/source/aws_lambda/create_campaign/handler.py @@ -52,8 +52,14 @@ "default": "omit", "as": "seconds", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, - status="campaign.status", + status="campaign.latestCampaignUpdate.status || campaign.status", ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: """Create a campaign in Amazon Personalize based on the configuration in `event` diff --git a/source/aws_lambda/scheduler/__init__.py b/source/aws_lambda/create_config/__init__.py similarity index 100% rename from source/aws_lambda/scheduler/__init__.py rename to source/aws_lambda/create_config/__init__.py diff --git a/source/aws_lambda/create_config/handler.py b/source/aws_lambda/create_config/handler.py new file mode 100644 index 0000000..6094f32 --- /dev/null +++ b/source/aws_lambda/create_config/handler.py @@ -0,0 +1,42 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +import json +import os + +from aws_lambda_powertools import Logger, Tracer, Metrics +from aws_lambda_powertools.metrics import MetricUnit +from aws_lambda_powertools.utilities.data_classes import S3Event + +from shared.personalize.service_model import ServiceModel +from shared.personalize_service import Personalize + + +logger = Logger() +tracer = Tracer() +metrics = Metrics() + + +@metrics.log_metrics +@tracer.capture_lambda_handler +def lambda_handler(event, context): + """Generate and return a solution configuration file derived from the properties of a dataset group + :param dict event: AWS Lambda Event (in this case, the dataset group and schedules) + :param context: AWS Lambda Context + :return: Dict + """ + dataset_group_name = event["datasetGroupName"] + schedules = event.get("schedules") + + cli = Personalize() + model = ServiceModel(cli, dataset_group_name=dataset_group_name) + return model.get_config(dataset_group_name=dataset_group_name, schedules=schedules) diff --git a/source/aws_lambda/create_dataset/handler.py b/source/aws_lambda/create_dataset/handler.py index 500c326..62aaedd 100644 --- a/source/aws_lambda/create_dataset/handler.py +++ b/source/aws_lambda/create_dataset/handler.py @@ -30,17 +30,23 @@ config={ "name": { "source": "event", - "path": "name", + "path": "serviceConfig.name", }, "datasetType": { "source": "event", - "path": "datasetType", + "path": "serviceConfig.datasetType", }, "datasetGroupArn": { "source": "event", - "path": "datasetGroupArn", + "path": "serviceConfig.datasetGroupArn", + }, + "schemaArn": {"source": "event", "path": "serviceConfig.schemaArn"}, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", }, - "schemaArn": {"source": "event", "path": "schemaArn"}, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_dataset_group/handler.py b/source/aws_lambda/create_dataset_group/handler.py index 6f9adfb..4c72647 100644 --- a/source/aws_lambda/create_dataset_group/handler.py +++ b/source/aws_lambda/create_dataset_group/handler.py @@ -43,6 +43,12 @@ "path": "KMS_KEY_ARN", "default": "omit", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_dataset_import_job/handler.py b/source/aws_lambda/create_dataset_import_job/handler.py index 158422a..7c8899b 100644 --- a/source/aws_lambda/create_dataset_import_job/handler.py +++ b/source/aws_lambda/create_dataset_import_job/handler.py @@ -48,6 +48,12 @@ "default": "omit", "as": "seconds", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_event_tracker/handler.py b/source/aws_lambda/create_event_tracker/handler.py index 2158e56..e46ea7e 100644 --- a/source/aws_lambda/create_event_tracker/handler.py +++ b/source/aws_lambda/create_event_tracker/handler.py @@ -31,11 +31,17 @@ config={ "name": { "source": "event", - "path": "name", + "path": "serviceConfig.name", }, "datasetGroupArn": { "source": "event", - "path": "datasetGroupArn", + "path": "serviceConfig.datasetGroupArn", + }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", }, }, ) diff --git a/source/aws_lambda/create_filter/handler.py b/source/aws_lambda/create_filter/handler.py index 943d441..654d20b 100644 --- a/source/aws_lambda/create_filter/handler.py +++ b/source/aws_lambda/create_filter/handler.py @@ -41,6 +41,12 @@ "source": "event", "path": "serviceConfig.filterExpression", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_solution/handler.py b/source/aws_lambda/create_solution/handler.py index a53adf4..d0a0ecb 100644 --- a/source/aws_lambda/create_solution/handler.py +++ b/source/aws_lambda/create_solution/handler.py @@ -62,6 +62,12 @@ "path": "serviceConfig.solutionConfig", "default": "omit", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/aws_lambda/create_solution_version/handler.py b/source/aws_lambda/create_solution_version/handler.py index 98c8af0..6f38648 100644 --- a/source/aws_lambda/create_solution_version/handler.py +++ b/source/aws_lambda/create_solution_version/handler.py @@ -49,6 +49,12 @@ "path": "workflowConfig.solutionVersionArn", "default": "omit", }, + "timeStarted": { + "source": "event", + "path": "workflowConfig.timeStarted", + "default": "omit", + "as": "iso8601", + }, }, ) def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: diff --git a/source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/__init__.py b/source/aws_lambda/prepare_input/__init__.py similarity index 100% rename from source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/__init__.py rename to source/aws_lambda/prepare_input/__init__.py diff --git a/source/aws_lambda/prepare_input/handler.py b/source/aws_lambda/prepare_input/handler.py new file mode 100644 index 0000000..159fde0 --- /dev/null +++ b/source/aws_lambda/prepare_input/handler.py @@ -0,0 +1,33 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from typing import Dict, Any + +from aws_lambda_powertools import Logger, Tracer, Metrics +from aws_lambda_powertools.utilities.typing import LambdaContext + +from shared.sfn_middleware import set_workflow_config + +logger = Logger() +tracer = Tracer() +metrics = Metrics() + + +def lambda_handler(event: Dict[str, Any], context: LambdaContext) -> Dict: + """Add timeStarted to the workflowConfig of all items + :param event: AWS Lambda Event + :param context: AWS Lambda Context + :return: the modified input + """ + config = set_workflow_config(event) + return config diff --git a/source/aws_lambda/shared/events.py b/source/aws_lambda/shared/events.py new file mode 100644 index 0000000..cfec0a8 --- /dev/null +++ b/source/aws_lambda/shared/events.py @@ -0,0 +1,78 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +from datetime import datetime +from typing import Dict, Optional + +from aws_lambda_powertools import Logger + +from shared.exceptions import ( + NotificationError, + SolutionVersionPending, +) +from shared.notifiers import NotifyEventBridge +from shared.resource import Resource + +logger = Logger() + + +NOTIFY_LIST = [NotifyEventBridge()] + + +class Notifies: + """Decorates a resource creation or describe call to provide event notifications""" + + def __init__(self, status: str): + self.status = status + + def __call__(self, function): + def wrapper(caller, resource: Resource, **kwargs): + try: + result = function(caller, resource, **kwargs) + except SolutionVersionPending as exc: + # because of how solution versions are handled, we must manually notify and re-raise + self.notify( + resource=resource, + result={ + "solutionVersionArn": str(exc), + "status": "CREATE IN_PROGRESS", + }, + cutoff=None, + ) + raise exc + + # run the notifier + cutoff = kwargs.get("timeStarted") + self.notify(resource, result, cutoff) + + return result + + return wrapper + + def notify( + self, resource: Resource, result: Dict, cutoff: Optional[datetime] + ) -> None: + """ + Notify each target in the NOTIFY_LIST + :param resource: the subject of the notification + :param result: the description of the subject of the notification + :param cutoff: the cutoff datetime for notifications (UTC required, timezone aware) + :return: None + """ + for notifier in NOTIFY_LIST: + notifier.set_cutoff(cutoff) + try: + notifier.notify(self.status, resource, result) + except NotificationError as exc: + logger.error( + f"notifier {notifier.name} failed: {str(exc)}" + ) # log and continue through notifiers diff --git a/source/aws_lambda/shared/exceptions.py b/source/aws_lambda/shared/exceptions.py index be0f6e1..1b25d93 100644 --- a/source/aws_lambda/shared/exceptions.py +++ b/source/aws_lambda/shared/exceptions.py @@ -30,3 +30,7 @@ class ResourceInvalid(Exception): class ResourceNeedsUpdate(Exception): pass + + +class NotificationError(Exception): + pass diff --git a/source/aws_lambda/shared/notifiers/__init__.py b/source/aws_lambda/shared/notifiers/__init__.py new file mode 100644 index 0000000..ae5ed5c --- /dev/null +++ b/source/aws_lambda/shared/notifiers/__init__.py @@ -0,0 +1,14 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from shared.notifiers.notify_eventbridge import NotifyEventBridge diff --git a/source/aws_lambda/shared/notifiers/base.py b/source/aws_lambda/shared/notifiers/base.py new file mode 100644 index 0000000..6749470 --- /dev/null +++ b/source/aws_lambda/shared/notifiers/base.py @@ -0,0 +1,207 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from abc import ABC, abstractmethod +from datetime import datetime +from typing import Dict + +import jmespath +from aws_lambda_powertools import Logger + +from shared.resource import Resource + +logger = Logger() + + +ACTIVE = "ACTIVE" +TIME_FMT = "{name}.latestCampaignUpdate.{date} || {name}.{date}" + + +class Notifier(ABC): + """Notifiers provide notify_create and notify_complete against a resource and its data""" + + cutoff: datetime + notified: bool = False + + @abstractmethod + def notify_create(self, status: str, resource: Resource, result: Dict) -> None: + """ + Notify for resource creation + :param status: the status of the resource (usually CREATING, sometimes UPDATING) + :param resource: the Resource + :param result: the service response (per a create or update call) + :return: None + """ + pass + + @abstractmethod + def notify_complete(self, status: str, resource: Resource, result: Dict): + """ + Notify for resource completion + :param status: the status of the resource (usually ACTIVE) + :param resource: the Resource + :param result: the servie response (per a describe call) + :return: None + """ + pass + + @property + def name(self): + """ + Get the name of the notifier + :return: str + """ + return self.__class__.__name__ + + def notify(self, status: str, resource: Resource, result: Dict) -> None: + """ + Top-level notification + :param status: the resource status + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: None + """ + logger.debug(f"{resource.name.camel} status update ({status}) on {result}") + + if self._is_create(resource, result): + logger.info( + f"notifier {self.name} starting for creation of {resource.name.camel}" + ) + self.notify_create(status, resource, result) + self.notified = True + elif self._resource_stable(resource, result): + logger.info( + f"notifier {self.name} starting for completion of {resource.name.camel}" + ) + self.notify_complete(status, resource, result) + self.notified = True + + def set_cutoff(self, cutoff: datetime) -> None: + """ + Sets the cutoff for notification (if the event is received after the cutoff - notify) + :param cutoff: the cutoff time + :return: Non e + """ + self.cutoff = cutoff + + def _is_create(self, resource: Resource, result: Dict) -> bool: + """ + Checks if the resource is a create or update + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: bool + """ + if f"{resource.name.camel}Arn" in result.keys(): + return True + else: + return False + + def _resource_stable(self, resource: Resource, result: Dict) -> bool: + """ + Check whether the resource has stabilized and should trigger notification + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: bool + """ + last_updated = self.get_resource_last_updated(resource, result) + created = self.get_resource_created(resource, result) + status = self.get_resource_status(resource, result) + latest_campaign_update = self.get_resource_latest_campaign_update( + resource, result + ) + + if not last_updated or not created: + logger.info( + f"{resource.name.camel} is not ready for notification (missing lastUpdated or creation DateTime)" + ) + return False + elif status != ACTIVE: + logger.info(f"{resource.name.camel} is not yet {ACTIVE}") + return False + elif ( + resource.name.camel == "campaign" + and latest_campaign_update + and latest_campaign_update.get("status") != ACTIVE + ): + logger.info(f"{resource.name.camel} is updating, and not yet active") + return False + elif not self.cutoff: + logger.debug( + f"{resource.name.camel} has no cutoff specified for notification" + ) + return False + elif last_updated <= self.cutoff: + logger.info(f"{resource.name.camel} does not require update at this time") + return False + else: + logger.info(f"{resource.name.camel} is ready for notification") + return True + + def get_resource_latest_campaign_update( + self, resource: Resource, result: Dict + ) -> Dict: + """ + Campaigns track their update status separately from the top-level status - return the update status + :param resource: the Campaign resource + :param result: the Campaign as returned from the SDK + :return: Dict + """ + return result[resource.name.camel].get("latestCampaignUpdate", {}) + + def get_resource_created(self, resource: Resource, result: Dict) -> datetime: + """ + Get the time of resource creation + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: datetime + """ + return jmespath.search( + TIME_FMT.format(name=resource.name.camel, date="creationDateTime"), result + ) + + def get_resource_last_updated(self, resource: Resource, result: Dict) -> datetime: + """ + Get the time of resource update + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: datetime + """ + return jmespath.search( + TIME_FMT.format(name=resource.name.camel, date="lastUpdatedDateTime"), + result, + ) + + def get_resource_status(self, resource, result: Dict) -> str: + """ + Get the resource status + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: str + """ + return result[resource.name.camel].get("status") + + def get_resource_arn(self, resource: Resource, result: Dict) -> str: + """ + Get the resource ARN + :param resource: the Resource + :param result: the resource as returned from the sdk + :return: str + """ + arn_key = f"{resource.name.camel}Arn" + + if resource.name.camel in result.keys(): + return result[resource.name.camel][arn_key] + elif arn_key in result.keys(): + return result[arn_key] + else: + raise ValueError("requires a valid SDK response") diff --git a/source/aws_lambda/shared/notifiers/notify_eventbridge.py b/source/aws_lambda/shared/notifiers/notify_eventbridge.py new file mode 100644 index 0000000..b61fa93 --- /dev/null +++ b/source/aws_lambda/shared/notifiers/notify_eventbridge.py @@ -0,0 +1,98 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +import json +import os +from typing import Dict + +from aws_lambda_powertools import Logger + +from aws_solutions.core import get_service_client +from shared.notifiers.base import Notifier +from shared.resource import Resource + +logger = Logger() + + +class NotifyEventBridge(Notifier): + """Provide notifications to EventBridge""" + + def __init__(self): + self.cli = get_service_client("events") + super().__init__() + + @property + def bus(self): + """ + The event BUS ARN + :return: str + """ + return os.environ["EVENT_BUS_ARN"] + + def notify_create(self, status: str, resource: Resource, result: Dict) -> None: + """ + Notify for the creation of a resource + :param status: the resource status + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: None + """ + arn = self.get_resource_arn(resource, result) + self._notify(status, arn, resource) + + def notify_complete(self, status: str, resource: Resource, result: Dict) -> None: + """ + Notify for the update of a resource + :param status: the resource status + :param resource: the Resource + :param result: the resource as returned from the SDK + :return: None + """ + arn = self.get_resource_arn(resource, result) + + created = self.get_resource_created(resource, result) + updated = self.get_resource_last_updated(resource, result) + + seconds = int((updated - created).total_seconds()) + self._notify(status, arn, resource, duration=seconds) + + def _notify( + self, status: str, arn: str, resource: Resource, duration: int = 0 + ) -> None: + """ + The EventBridge notification implementation + :param status: the resource status + :param arn: the resource ARN + :param resource: the Resource + :param duration: the time it took the resource to stabilize + :return: None + """ + detail = {"Arn": arn, "Status": status} + if duration: + detail["Duration"] = duration + + result = self.cli.put_events( + Entries=[ + { + "Source": "solutions.aws.personalize", + "Resources": [arn], + "DetailType": f"Personalize {resource.name.dash.replace('-', ' ').title()} State Change", + "Detail": json.dumps(detail), + "EventBusName": self.bus, + } + ] + ) + if result["FailedEntryCount"] > 0: + for entry in result["Entries"]: + logger.error( + f"EventBridge failure ({entry['ErrorCode']}) {entry['ErrorMessage']}" + ) diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/__init__.py b/source/aws_lambda/shared/personalize/__init__.py similarity index 100% rename from source/infrastructure/personalize/scheduler/aws_lambda/__init__.py rename to source/aws_lambda/shared/personalize/__init__.py diff --git a/source/aws_lambda/shared/personalize/service_model.py b/source/aws_lambda/shared/personalize/service_model.py new file mode 100644 index 0000000..bae7612 --- /dev/null +++ b/source/aws_lambda/shared/personalize/service_model.py @@ -0,0 +1,351 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from typing import List, Dict, Callable, Optional + +from aws_solutions.core import get_aws_partition, get_aws_region, get_aws_account +from shared.personalize_service import Personalize, logger +from shared.resource import DatasetGroup, Resource, Filter +from shared.resource import ( + EventTracker, + Dataset, + Schema, + Solution, + Campaign, + BatchInferenceJob, +) + + +@dataclass(eq=True, frozen=True) +class ResourceElement: + resource: Resource = field(repr=False, compare=True) + arn: str = field(repr=True, compare=True) + + +@dataclass +class ResourceTree: + resources: ResourceElement = field(default_factory=dict, init=False, repr=False) + _resource_elements: Dict = field(default_factory=dict, init=False, repr=False) + _resource_parentage: Dict = field(default_factory=dict, init=False, repr=False) + + def add(self, parent: ResourceElement, child: ResourceElement): + if child not in self._resource_parentage.keys(): + self._resource_parentage[child] = parent + self._resource_elements.setdefault(parent, []).append(child) + else: + raise ValueError("element already exists") + + def children( + self, of: ResourceElement, where: Callable = lambda _: True + ) -> List[ResourceElement]: + return [elem for elem in self._resource_elements[of] if where(elem)] + + +class ServiceModel: + """Lists all resources in Amazon Personalize for lookup against the dataset group ARN""" + + def __init__(self, cli: Personalize, dataset_group_name=None): + self.cli = cli + self._arn_ownership = {} + self._resource_tree = ResourceTree() + + if dataset_group_name: + dsgs = [DatasetGroup().arn(dataset_group_name)] + else: + dsgs = self._arns(self.cli.list(DatasetGroup())) + + for dsg in dsgs: + logger.debug(f"listing children of {dsg}") + self._list_children(DatasetGroup(), dsg, dsg) + + def owned_by(self, resource_arn, dataset_group_owner: str) -> bool: + """ + Check + :param resource_arn: the resource ARN to check + :param dataset_group_owner: the dataset group owner expected + :return: True if the resource is managed by the dataset group, otherwise False + """ + if not dataset_group_owner.startswith("arn:"): + dataset_group_owner = f"arn:{get_aws_partition()}:personalize:{get_aws_region()}:{get_aws_account()}:dataset-group/{dataset_group_owner}" + + return dataset_group_owner == self._arn_ownership.get(resource_arn, False) + + def available(self, resource_arn: str) -> bool: + """ + Check if the requested ARN is available + :param resource_arn: requested ARN + :return: True if the ARN is available, otherwise False + """ + all_arns = set(self._arn_ownership.keys()).union( + set(self._arn_ownership.values()) + ) + return resource_arn not in all_arns + + def _list_children(self, parent: Resource, parent_arn, dsg: str) -> None: + """ + Recursively list the children of a resource + :param parent: the parent Resource + :param parent_arn: the parent Resource ARN + :param dsg: the parent dataset group ARN + :return: None + """ + for c in parent.children: + child_arns = self._arns( + self.cli.list(c, filters={f"{parent.name.camel}Arn": parent_arn}) + ) + + for arn in child_arns: + logger.debug(f"listing children of {arn}") + self._resource_tree.add( + parent=ResourceElement(parent, parent_arn), + child=ResourceElement(c, arn), + ) + self._arn_ownership[arn] = dsg + self._list_children(c, arn, dsg) + + def _arns(self, l: List[Dict]) -> List[str]: + """ + Lists the first ARN found for each resource in a list of resources + :param l: the list of resources + :return: the list of ARNs + """ + return [ + [v for k, v in resource.items() if k.endswith("Arn")][0] for resource in l + ] + + def _filter(self, result: Dict) -> Dict: + resource_key = next(iter(k for k in result.keys() if k != "ResponseMetadata")) + result = result[resource_key] + result = { + k: v for k, v in result.items() if k == "recipeArn" or not k.endswith("Arn") + } + + # common + result.pop("status", None) + result.pop("creationDateTime", None) + result.pop("lastUpdatedDateTime", None) + + # event tracker + result.pop("accountId", None) + result.pop("trackingId", None) + + # datset + result.pop("datasetType", None) + + # schema + if resource_key == "schema": + result["schema"] = json.loads(result["schema"]) + + # solution + result.pop("latestSolutionVersion", None) + + # campaign + result.pop("latestCampaignUpdate", None) + + # batch job + for item in { + "failureReason", + "jobInput", + "jobOutput", + "jobName", + "roleArn", + "solutionVersionArn", + }: + result.pop(item, None) + + return result + + def get_config(self, dataset_group_name, schedules: Optional[Dict]) -> Dict: + dataset_group_arn = DatasetGroup().arn(dataset_group_name) + dataset_group = ResourceElement(DatasetGroup(), dataset_group_arn) + + config = { + "datasetGroup": { + "serviceConfig": self._filter( + self.cli.describe(DatasetGroup(), name=dataset_group_name) + ) + } + } + + self._add_filter_config(config, dataset_group) + self._add_event_tracker_config(config, dataset_group) + self._add_datasets(config, dataset_group) + self._add_solutions(config, dataset_group) + self._add_schedules(config, schedules) + + return config + + def _add_schedules(self, config: Dict, schedules: Optional[Dict]) -> None: + """ + Modify config in place to add schedules + :param config: the config dictionary + :param schedules: the schedules to add + :return: None + """ + if not schedules: + return + + if schedules.get("import"): + config["datasetGroup"]["workflowConfig"] = { + "schedules": {"import": schedules.get("import")} + } + + solution_schedules = schedules.get("solutions", {}) + for idx, solution in enumerate(config.get("solutions", [])): + name = solution.get("serviceConfig", {}).get("name") + schedules = solution_schedules.get(name) + if schedules: + config["solutions"][idx]["workflowConfig"] = {"schedules": schedules} + + def _add_solutions(self, config, of: ResourceElement) -> None: + """ + Modify the config in place to add solutions, campaigns, and batch inference jobs + :param config: the config dictionary + :param of: the solution ResourceElement + :return: None + """ + solutions = self._resource_tree.children( + of, where=lambda x: x.resource == Solution() + ) + if not solutions: + return + + config.setdefault("solutions", []) + for solution in solutions: + _solution = self.cli.describe_by_arn(Solution(), solution.arn) + _solution_config = {"serviceConfig": self._filter(_solution)} + + campaigns = self._resource_tree.children( + of=solution, where=lambda x: x.resource == Campaign() + ) + for campaign in campaigns: + _campaign = self.cli.describe_by_arn(Campaign(), campaign.arn) + _solution_config.setdefault("campaigns", []).append( + {"serviceConfig": self._filter(_campaign)} + ) + + batch_jobs = self._resource_tree.children( + of=solution, where=lambda x: x.resource == BatchInferenceJob() + ) + for batch_job in batch_jobs: + _batch_job = self.cli.describe_by_arn( + BatchInferenceJob(), batch_job.arn + ) + _solution_config.setdefault("batchInferenceJobs", []).append( + {"serviceConfig": self._filter(_batch_job)} + ) + config["solutions"].append(_solution_config) + + def _add_filter_config(self, config: Dict, of: ResourceElement) -> None: + """ + Modify the config in place to add filters + :param config: the config dictionary + :param of: the DatasetGroup ResourceElement + :return: None + """ + filters = self._resource_tree.children( + of, where=lambda x: x.resource == Filter() + ) + if not filters: + return + + config["filters"] = [ + { + "serviceConfig": self._filter( + self.cli.describe_by_arn(filter.resource, filter.arn) + ) + } + for filter in filters + ] + + def _add_event_tracker_config(self, config: Dict, of: ResourceElement) -> None: + """ + Modify the config in place to add an event tracker + :param config: the config dictionary + :param of: the DatasetGroup ResourceElement + :return: None + """ + event_tracker = next( + iter( + self._resource_tree.children( + of, where=lambda x: x.resource == EventTracker() + ) + ), + None, + ) + if not event_tracker: + return + config["eventTracker"] = { + "serviceConfig": self._filter( + self.cli.describe_by_arn(event_tracker.resource, event_tracker.arn) + ) + } + + def _add_datasets(self, config, of: ResourceElement) -> None: + """ + Modify the config in place to add all datasets + :param config: the config dictionary + :param of: the DatasetGroup ResourceElement + :return: None + """ + for dataset_type in Dataset().allowed_types: + self._add_dataset(config, dataset_type, of) + + def _add_dataset( + self, config: Dict, dataset_type: str, of: ResourceElement + ) -> None: + """ + Modify the config in place to add a dataset and schema + :param config: the config dictionary + :param dataset_type: the dataset type (must be ITEMS, INTERACTIONS, or USERS) + :param of: the DatasetGroup ResourceElement + :return: None + """ + if dataset_type not in Dataset().allowed_types: + raise ValueError( + f"dataset type {dataset_type} must be one of {Dataset().allowed_types}" + ) + + dataset = next( + iter( + self._resource_tree.children( + of, + where=lambda x: x.resource == Dataset() + and x.arn.endswith(dataset_type), + ) + ), + None, + ) + if not dataset: + return + + dataset = self.cli.describe_by_arn(Dataset(), dataset.arn) + config.setdefault("datasets", {}) + config["datasets"].setdefault(dataset_type.lower(), {}) + config["datasets"][dataset_type.lower()].setdefault( + "dataset", {"serviceConfig": self._filter(dataset)} + ) + config["datasets"][dataset_type.lower()].setdefault( + "schema", + { + "serviceConfig": self._filter( + self.cli.describe_by_arn( + Schema(), arn=dataset["dataset"]["schemaArn"] + ) + ) + }, + ) diff --git a/source/aws_lambda/shared/personalize_service.py b/source/aws_lambda/shared/personalize_service.py index 42f8729..511e1fb 100644 --- a/source/aws_lambda/shared/personalize_service.py +++ b/source/aws_lambda/shared/personalize_service.py @@ -30,6 +30,8 @@ get_aws_region, get_aws_account, ) +from aws_solutions.scheduler.common import ScheduleError, Schedule +from shared.events import Notifies from shared.exceptions import ( ResourcePending, ResourceNeedsUpdate, @@ -50,7 +52,6 @@ Campaign, ) from shared.s3 import S3 -from shared.scheduler import Schedule, ScheduleError logger = Logger() metrics = Metrics() @@ -58,7 +59,11 @@ STATUS_CREATING = ("ACTIVE", "CREATE PENDING", "CREATE IN_PROGRESS") CRON_ANY_WILDCARD = "?" CRON_MIN_MAX_YEAR = (1970, 2199) -SOLUTION_PARAMETERS = (("maxAge", Resource), ("solutionVersionArn", SolutionVersion)) +WORKFLOW_PARAMETERS = ( + ("maxAge", Resource), + ("timeStarted", Resource), + ("solutionVersionArn", SolutionVersion), +) def get_duplicates(items): @@ -92,6 +97,7 @@ def list(self, resource: Resource, filters: Optional[Dict] = None): for item in page[resource_key]: yield item + @Notifies("ACTIVE") def describe(self, resource: Resource, **kwargs): """ Describe a resource in Amazon Personalize @@ -126,6 +132,11 @@ def describe_default(self, resource: Resource, **kwargs): describe_fn = getattr(self.cli, describe_fn_name) return describe_fn(**self.arn(resource, kwargs["name"])) + def describe_by_arn(self, resource: Resource, arn: str): + describe_fn_name = f"describe_{resource.name.snake}" + describe_fn = getattr(self.cli, describe_fn_name) + return describe_fn(**{f"{resource.name.camel}Arn": arn}) + def _check_solution(self, sv_arn_expected: str, sv_arn_received: str) -> bool: """ Check if solution versions sv_received and sv_expected have the same solution ARN @@ -149,6 +160,7 @@ def describe_with_update(self, resource: Resource, **kwargs): :param kwargs: the resource keyword arguments to validate :return: the response from Amazon Personalize """ + kwargs = self._remove_workflow_parameters(resource, kwargs.copy()) result = self.describe_default(resource, **kwargs) for k, v in kwargs.items(): received = result[resource.name.camel][k] @@ -159,16 +171,18 @@ def describe_with_update(self, resource: Resource, **kwargs): self._check_solution(expected, received) if result[resource.name.camel].get(k) != v: - raise ResourceNeedsUpdate() + raise ResourceNeedsUpdate( + result[resource.name.camel][f"{resource.name.camel}Arn"] + ) return result - def _remove_solution_parameters(self, resource: Resource, kwargs): + def _remove_workflow_parameters(self, resource: Resource, kwargs): """ - Remove solution parameters for the keyword arguments presented + Remove workflow parameters for the keyword arguments presented :param kwargs: :return: the kwargs with the solution parameters removed """ - for key, resource_type in SOLUTION_PARAMETERS: + for key, resource_type in WORKFLOW_PARAMETERS: if isinstance(resource, resource_type): kwargs.pop(key, None) return kwargs @@ -374,10 +388,14 @@ def is_active_batch_inference_job(job: Dict): **kwargs, ) + @Notifies("UPDATING") def update(self, resource: Resource, **kwargs): update_fn_name = f"update_{resource.name.snake}" update_fn = getattr(self.cli, update_fn_name) + # always remove the workflow configuration parameters before update + kwargs = self._remove_workflow_parameters(resource, kwargs) + # set up the ARN to update kwargs_arn = self.arn(resource, kwargs.pop("name")) kwargs.update(kwargs_arn) @@ -395,12 +413,13 @@ def update(self, resource: Resource, **kwargs): return result + @Notifies("CREATING") def create(self, resource: Resource, **kwargs): create_fn_name = f"create_{resource.name.snake}" create_fn = getattr(self.cli, create_fn_name) # always remove the workflow configuration parameters before create - kwargs = self._remove_solution_parameters(resource, kwargs) + kwargs = self._remove_workflow_parameters(resource, kwargs) try: result = create_fn(**kwargs) @@ -468,71 +487,6 @@ def exceptions(self): return self.cli.exceptions -class ServiceModel: - """Lists all resources in Amazon Personalize for lookup against the dataset group ARN""" - - _arn_ownership = {} - - def __init__(self, cli: Personalize): - self.cli = cli - - dsgs = self._arns(self.cli.list(DatasetGroup())) - for dsg in dsgs: - logger.debug(f"listing children of {dsg}") - self._list_children(DatasetGroup(), dsg, dsg) - - def owned_by(self, resource_arn, dataset_group_owner: str) -> bool: - """ - Check - :param resource_arn: the resource ARN to check - :param dataset_group_owner: the dataset group owner expected - :return: True if the resource is managed by the dataset group, otherwise False - """ - if not dataset_group_owner.startswith("arn:"): - dataset_group_owner = f"arn:{get_aws_partition()}:personalize:{get_aws_region()}:{get_aws_account()}:dataset-group/{dataset_group_owner}" - - return dataset_group_owner == self._arn_ownership.get(resource_arn, False) - - def available(self, resource_arn: str) -> bool: - """ - Check if the requested ARN is available - :param resource_arn: requested ARN - :return: True if the ARN is available, otherwise False - """ - all_arns = set(self._arn_ownership.keys()).union( - set(self._arn_ownership.values()) - ) - return resource_arn not in all_arns - - def _list_children(self, parent: Resource, parent_arn, dsg: str) -> None: - """ - Recursively list the children of a resource - :param parent: the parent Resource - :param parent_arn: the parent Resource ARN - :param dsg: the parent dataset group ARN - :return: None - """ - for c in parent.children: - child_arns = self._arns( - self.cli.list(c, filters={f"{parent.name.camel}Arn": parent_arn}) - ) - - for arn in child_arns: - logger.debug(f"listing children of {arn}") - self._arn_ownership[arn] = dsg - self._list_children(c, arn, dsg) - - def _arns(self, l: List[Dict]) -> List[str]: - """ - Lists the first ARN found for each resource in a list of resources - :param l: the list of resources - :return: the list of ARNs - """ - return [ - [v for k, v in resource.items() if k.endswith("Arn")][0] for resource in l - ] - - class InputValidator: @classmethod def validate(cls, method: str, expected_params: Dict) -> None: @@ -552,7 +506,12 @@ class Configuration: { "datasetGroup": [ "serviceConfig", - {"workflowConfig": [{"schedules": ["import"]}, "maxAge"]}, + { + "workflowConfig": [ + {"schedules": ["import"]}, + "maxAge", + ] + }, ] }, { @@ -735,13 +694,9 @@ def _validate_solutions(self, path="solutions[]"): self._validate_resource(Solution(), _solution) def _validate_solution_update(self): - valid_recipes = [ - "arn:aws:personalize:::recipe/aws-hrnn-coldstart", - "arn:aws:personalize:::recipe/aws-user-personalization", - ] invalid = ( jmespath.search( - f"solutions[?workflowConfig.schedules.update && (serviceConfig.recipeArn != '{valid_recipes[0]}' || serviceConfig.recipeArn != '{valid_recipes[1]}')].serviceConfig.name", + "solutions[].{name: serviceConfig.name, recipe: serviceConfig.recipeArn, update: workflowConfig.schedules.update} | @[?update && (!contains(recipe, `aws-hrnn-coldstart`) && !contains(recipe, `aws-user-personalization`))].name", self.config_dict, ) or [] diff --git a/source/aws_lambda/shared/resource/base.py b/source/aws_lambda/shared/resource/base.py index d37d873..741cf36 100644 --- a/source/aws_lambda/shared/resource/base.py +++ b/source/aws_lambda/shared/resource/base.py @@ -27,6 +27,16 @@ def __init__(self): name = name[0].lower() + name[1:] self.name = ResourceName(name) - def arn(self, name: str) -> str: - arn_prefix = f"arn:{get_aws_partition()}:personalize:{get_aws_region()}:{get_aws_account()}" - return f"{arn_prefix}:{self.name.dash}/{name}" + def arn(self, name: str, **kwargs) -> str: + if self.name.camel == "solutionVersion": + arn_prefix = f"arn:{get_aws_partition()}:personalize:{get_aws_region()}:{get_aws_account()}" + return f"{arn_prefix}:solution/{name}/{kwargs.get('sv_id', 'unknown')}" + else: + arn_prefix = f"arn:{get_aws_partition()}:personalize:{get_aws_region()}:{get_aws_account()}" + return f"{arn_prefix}:{self.name.dash}/{name}" + + def __eq__(self, other): + return self.name.camel == other.name.camel + + def __hash__(self): + return hash(self.name.camel) diff --git a/source/aws_lambda/shared/resource/dataset.py b/source/aws_lambda/shared/resource/dataset.py index 755bc98..f32cc46 100644 --- a/source/aws_lambda/shared/resource/dataset.py +++ b/source/aws_lambda/shared/resource/dataset.py @@ -16,3 +16,4 @@ class Dataset(Resource): children = [DatasetImportJob()] + allowed_types = {"INTERACTIONS", "ITEMS", "USERS"} diff --git a/source/aws_lambda/shared/resource/dataset_group.py b/source/aws_lambda/shared/resource/dataset_group.py index 20f8c29..4cc9312 100644 --- a/source/aws_lambda/shared/resource/dataset_group.py +++ b/source/aws_lambda/shared/resource/dataset_group.py @@ -12,9 +12,10 @@ # ###################################################################################################################### from shared.resource.base import Resource from shared.resource.dataset import Dataset +from shared.resource.event_tracker import EventTracker from shared.resource.filter import Filter from shared.resource.solution import Solution class DatasetGroup(Resource): - children = [Dataset(), Filter(), Solution()] + children = [Dataset(), Filter(), Solution(), EventTracker()] diff --git a/source/aws_lambda/shared/sfn_middleware.py b/source/aws_lambda/shared/sfn_middleware.py index e61af68..89d5428 100644 --- a/source/aws_lambda/shared/sfn_middleware.py +++ b/source/aws_lambda/shared/sfn_middleware.py @@ -17,12 +17,14 @@ import json import os from dataclasses import dataclass, field +from enum import Enum, auto from pathlib import Path from typing import Dict, Any, Callable, Optional, List, Union from uuid import uuid4 import jmespath from aws_lambda_powertools import Logger +from dateutil.parser import isoparse from aws_solutions.core import get_service_client from shared.date_helpers import parse_datetime @@ -35,6 +37,8 @@ from shared.personalize_service import Personalize from shared.resource import get_resource +logger = Logger() + STATUS_IN_PROGRESS = ( "CREATE PENDING", "CREATE IN_PROGRESS", @@ -44,7 +48,18 @@ STATUS_FAILED = "CREATE FAILED" STATUS_ACTIVE = "ACTIVE" -logger = Logger() +WORKFLOW_PARAMETERS = { + "maxAge", + "timeStarted", +} +WORKFLOW_CONFIG_DEFAULT = { + "timeStarted": datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") +} + + +class Arity(Enum): + ONE = auto() + MANY = auto() def json_handler(item): @@ -57,6 +72,41 @@ def json_handler(item): raise TypeError("Unknown Type") +def set_workflow_config(config: Dict) -> Dict: + """ + Set the defaults for workflowConfiguration for all configured items + :param config: the configuration dictionary + :return: the configuration with defaults set + """ + + resources = { + "datasetGroup": Arity.ONE, + "solutions": Arity.MANY, + "campaigns": Arity.MANY, + "batchInferenceJobs": Arity.MANY, + "filters": Arity.MANY, + "solutionVersion": Arity.ONE, + } + # Note: schema creation notification is not supported at this time + # Note: dataset, dataset import job, event tracker notifications are added in the workflow + + for k, v in config.items(): + if k in {"serviceConfig", "workflowConfig", "bucket", "currentDate"}: + pass # do not modify any serviceConfig keys + elif k in resources.keys() and resources[k] == Arity.ONE: + config[k].setdefault("workflowConfig", {}) + config[k]["workflowConfig"] |= WORKFLOW_CONFIG_DEFAULT + elif k in resources.keys() and resources[k] == Arity.MANY: + for idx, i in enumerate(v): + config[k][idx].setdefault("workflowConfig", {}) + config[k][idx]["workflowConfig"] |= WORKFLOW_CONFIG_DEFAULT + config[k][idx] = set_workflow_config(config[k][idx]) + else: + config[k] = set_workflow_config(config[k]) if config[k] else config[k] + + return config + + def set_defaults(config: Dict) -> Dict: """ Set the defaults for schedule/ solutions/ solution versions/ campaigns as empty if not set @@ -122,6 +172,8 @@ def format(self, resolved): return json.dumps(resolved) elif self.format_as == "seconds": return parse_datetime(resolved) + elif self.format_as == "iso8601": + return isoparse(resolved) elif self.format_as == "int": return int(resolved) else: @@ -226,7 +278,7 @@ def check_status( # NOSONAR - allow higher complexity continue if self.resource == "solutionVersion" and expected_key == "trainingMode": continue - if expected_key == "maxAge": + if expected_key in WORKFLOW_PARAMETERS: continue if actual_value != expected_value: diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment.py new file mode 100644 index 0000000..4388231 --- /dev/null +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment.py @@ -0,0 +1,48 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from dataclasses import dataclass, field + +from aws_cdk.aws_lambda import IFunction +from aws_cdk.core import Aws + +from aws_solutions.cdk.aws_lambda.environment_variable import EnvironmentVariable + + +@dataclass +class Environment: + """ + Tracks environment variables common to AWS Lambda functions deployed by this solution + """ + + scope: IFunction + solution_name: EnvironmentVariable = field(init=False, repr=False) + solution_id: EnvironmentVariable = field(init=False, repr=False) + solution_version: EnvironmentVariable = field(init=False, repr=False) + log_level: EnvironmentVariable = field(init=False, repr=False) + powertools_service_name: EnvironmentVariable = field(init=False, repr=False) + + def __post_init__(self): + cloudwatch_namespace_id = f"personalize_solution_{Aws.STACK_NAME}" + cloudwatch_service_id_default = f"Workflow" + + self.solution_name = EnvironmentVariable(self.scope, "SOLUTION_NAME") + self.solution_id = EnvironmentVariable(self.scope, "SOLUTION_ID") + self.solution_version = EnvironmentVariable(self.scope, "SOLUTION_VERSION") + self.log_level = EnvironmentVariable(self.scope, "LOG_LEVEL", "INFO") + self.powertools_service_name = EnvironmentVariable( + self.scope, "POWERTOOLS_SERVICE_NAME", cloudwatch_service_id_default + ) + self.powertools_metrics_namespace = EnvironmentVariable( + self.scope, "POWERTOOLS_METRICS_NAMESPACE", cloudwatch_namespace_id + ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/environment_variable.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment_variable.py similarity index 100% rename from source/infrastructure/personalize/aws_lambda/functions/environment_variable.py rename to source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/environment_variable.py diff --git a/source/infrastructure/personalize/scheduler/__init__.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/__init__.py similarity index 96% rename from source/infrastructure/personalize/scheduler/__init__.py rename to source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/__init__.py index cc56cd7..ef2f9eb 100644 --- a/source/infrastructure/personalize/scheduler/__init__.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/__init__.py @@ -10,5 +10,3 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - -from personalize.scheduler.base import Scheduler diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/__init__.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/__init__.py new file mode 100644 index 0000000..8b086e5 --- /dev/null +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/__init__.py @@ -0,0 +1,16 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from aws_solutions.cdk.aws_lambda.layers.aws_lambda_powertools.layer import ( + PowertoolsLayer, +) diff --git a/source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/layer.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/layer.py similarity index 100% rename from source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/layer.py rename to source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/layer.py diff --git a/source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/requirements/requirements.txt b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/requirements/requirements.txt similarity index 100% rename from source/infrastructure/personalize/aws_lambda/layers/aws_lambda_powertools/requirements/requirements.txt rename to source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/aws_lambda/layers/aws_lambda_powertools/requirements/requirements.txt diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/helpers/copytree.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/helpers/copytree.py index b162f73..25987e5 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/helpers/copytree.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/helpers/copytree.py @@ -22,8 +22,6 @@ def ignore_globs(*globs): Patterns is a sequence of glob-style patterns that are used to exclude files""" - # globs = globs + tuple([glob[:-2] for glob in globs if glob.endswith('/*')]) # ignore folders - def _ignore_globs(path, names): ignored_names = [] paths = [Path(os.path.join(path, name)).resolve() for name in names] diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stack.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stack.py index b85ed84..bf75bf0 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stack.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stack.py @@ -50,12 +50,14 @@ def __init__( self.metrics = {} self.solution_id = self.node.try_get_context("SOLUTION_ID") + self.solution_version = self.node.try_get_context("SOLUTION_VERSION") self.mappings = Mappings(self, solution_id=self.solution_id) self.solutions_template_filename = validate_template_filename(template_filename) + self.description = description.strip(".") self.solutions_template_options = TemplateOptions( self, construct_id=construct_id, - description=f"({self.solution_id}) {description}", + description=f"({self.solution_id}) - {self.description}. Version {self.solution_version}", filename=template_filename, ) diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/__init__.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solution_fragment.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solution_fragment.py new file mode 100644 index 0000000..48e0c0e --- /dev/null +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solution_fragment.py @@ -0,0 +1,81 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +from typing import List, Dict +from typing import Optional + +from aws_cdk.aws_lambda import CfnFunction +from aws_cdk.aws_stepfunctions import State, INextable, TaskInput, StateMachineFragment +from aws_cdk.aws_stepfunctions_tasks import LambdaInvoke +from aws_cdk.core import Construct, Duration + + +class SolutionFragment(StateMachineFragment): + def __init__( + self, # NOSONAR (python:S107) - allow large number of method parameters + scope: Construct, + id: str, + function: CfnFunction, + payload: Optional[TaskInput] = None, + input_path: Optional[str] = "$", + result_path: Optional[str] = "$", + output_path: Optional[str] = "$", + result_selector: Optional[Dict] = None, + failure_state: Optional[State] = None, + backoff_rate: Optional[int] = 1.05, + interval: Optional[Duration] = Duration.seconds(5), + max_attempts: Optional[int] = 5, + ): + super().__init__(scope, id) + + self.failure_state = failure_state + + self.task = LambdaInvoke( + self, + id, + lambda_function=function, + retry_on_service_exceptions=True, + input_path=input_path, + result_path=result_path, + output_path=output_path, + payload=payload, + payload_response_only=True, + result_selector=result_selector, + ) + self.task.add_retry( + backoff_rate=backoff_rate, + interval=interval, + max_attempts=max_attempts, + errors=["ResourcePending"], + ) + if self.failure_state: + self.task.add_catch( + failure_state, + errors=["ResourceFailed", "ResourceInvalid"], + result_path="$.statesError", + ) + self.task.add_catch( + failure_state, errors=["States.ALL"], result_path="$.statesError" + ) + + @property + def start_state(self) -> State: + return self.task + + @property + def end_states(self) -> List[INextable]: + """ + Get the end states of this chain + :return: The chainable end states of this chain (i.e. not the failure state) + """ + states = [self.task] + return states diff --git a/source/infrastructure/personalize/aws_lambda/functions/solutionstep.py b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py similarity index 85% rename from source/infrastructure/personalize/aws_lambda/functions/solutionstep.py rename to source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py index 7805672..279e61b 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/solutionstep.py +++ b/source/cdk_solution_helper_py/helpers_cdk/aws_solutions/cdk/stepfunctions/solutionstep.py @@ -12,16 +12,17 @@ # ###################################################################################################################### from pathlib import Path -from typing import Optional +from typing import Optional, List +from aws_cdk.aws_events import EventBus from aws_cdk.aws_lambda import Tracing, Runtime, RuntimeFamily from aws_cdk.aws_stepfunctions import IChainable, TaskInput, State from aws_cdk.core import Construct, Duration +from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.environment import Environment -from personalize.step_functions.personalization_fragment import PersonalizationFragment +from aws_solutions.cdk.stepfunctions.solution_fragment import SolutionFragment class SolutionStep(Construct): @@ -37,6 +38,7 @@ def __init__( payload: Optional[TaskInput] = None, layers=None, failure_state: Optional[IChainable] = None, + libraries: Optional[List[Path]] = None, ): super().__init__(scope, f"{id} Solution Step") @@ -46,6 +48,7 @@ def __init__( layers=layers, function=function, entrypoint=entrypoint, + libraries=libraries, ) add_cfn_nag_suppressions( self.function.role.node.try_find_child("DefaultPolicy").node.find_child( @@ -86,7 +89,7 @@ def state( output_path = output_path or self._output_path failure_state = failure_state or self._failure_state - return PersonalizationFragment( + return SolutionFragment( scope, construct_id, function=self.function, @@ -105,6 +108,10 @@ def _snake_case(self, name) -> str: def _set_permissions(self) -> None: raise NotImplementedError("please implement _set_permissions") + def grant_put_events(self, bus: EventBus): + self.function.add_environment("EVENT_BUS_ARN", bus.event_bus_arn) + bus.grant_put_events_to(self.function) + def _create_resources(self) -> None: pass # not required @@ -114,14 +121,13 @@ def _set_environment(self) -> Environment: class _CreateLambdaFunction(SolutionsPythonFunction): def __init__(self, scope: Construct, construct_id: str, **kwargs): entrypoint = kwargs.pop("entrypoint", None) - if not entrypoint: - entrypoint = ( - Path(__file__).absolute().parents[4] - / "aws_lambda" - / construct_id.replace("_fn", "") - / "handler.py" - ) - libraries = [Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"] + if not entrypoint or not entrypoint.exists(): + raise ValueError("an entrypoint (Path to a .py file) must be provided") + + libraries = kwargs.pop("libraries", None) + if libraries and any(not l.exists() for l in libraries): + raise ValueError(f"libraries provided, but do not exist at {libraries}") + function = kwargs.pop("function") kwargs["layers"] = kwargs.get("layers", []) kwargs["tracing"] = Tracing.ACTIVE diff --git a/source/cdk_solution_helper_py/helpers_cdk/setup.py b/source/cdk_solution_helper_py/helpers_cdk/setup.py index 98a095b..d830dfb 100644 --- a/source/cdk_solution_helper_py/helpers_cdk/setup.py +++ b/source/cdk_solution_helper_py/helpers_cdk/setup.py @@ -49,8 +49,8 @@ def get_version(): ] }, install_requires=[ - "aws-cdk.core>=1.120.0", - "aws-cdk.aws_lambda>=1.120.0", + "aws-cdk.core>=1.126.0", + "aws-cdk.aws_lambda>=1.126.0", "Click>=7.1.2", "boto3>=1.17.52", "requests>=2.24.0", diff --git a/source/cdk_solution_helper_py/requirements-dev.txt b/source/cdk_solution_helper_py/requirements-dev.txt index 6ee358c..31540ea 100644 --- a/source/cdk_solution_helper_py/requirements-dev.txt +++ b/source/cdk_solution_helper_py/requirements-dev.txt @@ -1,5 +1,5 @@ -aws-cdk.core>=1.123.0 -aws-cdk.aws_lambda>=1.123.0 +aws-cdk.core>=1.126.0 +aws-cdk.aws_lambda>=1.126.0 black boto3>=1.17.49 requests>=2.24.0 diff --git a/source/images/solution-architecture.jpg b/source/images/solution-architecture.jpg index 519f953b2578577e43cb3fb0925bda9ccde6dfe3..7634f22bb2da94ea734ad0aea7693a3a25d4e9df 100644 GIT binary patch delta 215842 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Ľ}Lx^cjG`afEFKkb-@t&K1Z0m)a0_*bjVp1 z$4F?5$&qznPX%<&T(CgLx5F4lVh62+NcFS3P-HhQj@i!@@0e@zZD~fW;492JPYrn8 ztSmP>UktIEl$352r8z2NTI4!Lp^+;la7<0}z~r%d?J)6juWY2BmG`xp+C9^)TX(8; zk3%Z0Gv;4>s`wabQl8mfhQx(Z@&<>R?vDA$SN$M)>q0p((wkn_-5q1h?*AG++U)*@ zT}j!_^NKV4^*JzpsXmz7Jsvxg5ex|XxVj?lD)k{nGVDzIvnp>^z_;rs>Pccl_3X+2 z!`XXAHT8GvqM;}P0@6D{KtZZVuMw3d(xnTCNN*w?0t-Pvx^x8rrS~FDq(pk}O*#op zdO{5)@vQ&*?!E6m=iD=H`N9}skYt3lGJkVEZNklKv4yMC*-9VGuuhNPd6rWyJ})UR zgOkkvZro(*HIE9~yA{trX|nXHhD~#8OG_YCpbyAif9>Z3UNw`|>=Mfw0aq;PO6ti3 z<9`jX&m!>3wSiPxMG9}e5+;TA_5ws`4Ls>eUoHr_D9bZ5F*Jy~saw=t<;A8Ak!KI7f!y96|3AFaKQqN)WhAhCXwpgn@(=nThC89z z1@SNcpY4Dnaf)|+iI92zpos34yjh{I|FRi^#XPn<_kK93>!2G~r;S?zQ<;_uO00p1 zmoQi!iYKGy&weLKqtcer{Mi06(MP3_)%5fIJD|$Wq%2BKLIL3 z6=gSD@cm1?=;kWlGo)0syt2!;j#V6;@vNme#*KY9;HrF+lj@{Q#Xa*!FOS_^-?qME z1xs0DV9s|pr{k*;Bf;Y9C05%{7qn>kkZz~ z3o?MXR^anXBSmh$ZmXp#7a!Bg4&X$zJQY=>@M>5EBPk7EHmY5zQ) z*7w6E-j?@2Mow$o9PqE1a{-1w_(v9HY&4#Q{@-L#Ocv5;+ei#i`wX52bMv}}9QKr^ zSzb%VgznVl<&6uEgoGEqSM-8CRDu#V`vd6RnRL1DyY2NxQQ;z#dwdAQ&mc|c-B^wu z?H{$cF9>@VBmL^};~hjD*2BCO$b1#KdOJmTFlu1^s*_MfXI-5TA#-}akZ;XzA^p8M zQhc|(E_1?gZ+5HtBD*2ec;JI?kZAH+xgLdiVUy#K+!@}cVl!7{R}P&LY=dEpR^#T3 z9&lB~6U{W@zpYp9jU0g93@-TD)f4y>Fz^%bM4XF#$C_VCTq$BTEBgSH3*n|wORF!- zB2+j19x;EXD<6$`$1TyMtOs)GCl9ii+FqYz`U`6rHZ)H(_k_euU`wV$Yol> zZ#&`}+#XNu{E1H$a5VKwtt>QmkWKkH;t)VSKSJx7D5Y0c_&Y>d+0?z9R6qTvL*btx zzNt*Igi_5pU5CQia@`d`4wUh(X*XGL zZxox&PF#XGdf(?c#O^E_y0YfIeCz5lYJ^6aGTwuIQdHZ=b=m$itN)Xds#2Ld{)BRe zkh@jo9n6YPQzcu}^RK5j#g#jzBB~n&*@o>DX=yrJzcU_WX3vm z!fL5HZ?sDJ;=A_XQu&RR6kB?Blm_3wXAORhNyYO*0;jr|Ef^5g)`l*zhWSur&k3kD zojT_n7X1{&_)9*GKwvr>!@DyvlVi@9(Rkbyh&=z5R{f5EF7dM&KvFVFEal^!%J6+@ z*<9Jhjga|Hy3vh-XYf8!i?x zuu^K5(votb6ITPI1D5fb@8gj(d#tEyN6i|T?tv$ZeYInro(YVb5o`cEAVXyUAWqvEPp_SA3a*=rFxyj_cH0xY#Y15yLDq9JpzuDW8Y`yH@)tdQQhHdExi~! zhYG~By?Ss#H9{2`8k44qb8H?fH|OO`8e8U1TL~q0NaHjoBnIH%A$5&4$u1Vo0o!JT z;o~YwSjCPiB>qBt^T7Zou{Ee7cmf*=Zq0S!69AWzEuSP)3zPnu`4RGD-#pO1!~IbS zvJp`c^FzyXt&qgF-frv^!K8Q#Kbme(A9ktq1TKZzF-~2Y=r)BjoBUc4nskH}EE$L1 z8g^e#QLqB4PpAjqm<-a(550YE${*>5zk?Hb{0nZE@6gJEu_#62pq-L)d#Tx;r|50N z4r+7$FMg3BDigm$ly=oN>NM*-HZbDWk7|qCpQ_85J70|ifgBFU&ho7wmTlmc*D)fp z()knCNxQ~3)u~u}F-u>7Ur>gf^kxSS_2GWMS0iAaaO`JC@x0s0CbBrt=toM$vQMDU zIc_GLYBstp`{tNe{G1r7J}Fm~X&D!qJ9L_<0w{MYKD!9B>Kzc#2{00QM<#RaCKV#x zP#3@Jm4t7BmdIE}FV{ZjxQD*7ys5~5^@4M+kW2O(cQ0=-4pKfz6DA#gL%GnvEjakF zOGTLhKJNeJbE^AEl;!ibj@dDDS%ehc^t|2|k!|_{?aUF#NDeK7e#b0c!mw-?TTXu= z-z45lVrMToKxI7FoMJ@rbB-Trw*`s&0Rn6Scr7V#Nv(JBL)q+=2zx|W{akVo?X*NZ zR?S7iMOqR^qVEUqiF=fLrm;ygCQ9(8O)O=NC|2P84?UU3-r`C7 zb2P_{xpHHv;j^CHn``{@)%_vMGWv2k3OIgzB>+|c)@(HBA%g~BwDBK+FaSVXd*JCH zK*vGQJy5Stg|5WkbOqXjsX?&5A)7wxe*8+r`;M1;ygH4XQ1@UYtV}!^L1_J1sj03HP2DX=5=)o)ov{*>pFz1^LwHi3qhkDS`SDK!GHHx&SB;@o%GQt+QACxUQ3H!^Ms*sW<@L*EdkP&SG5a5vq&2L>?!Q)RZ)dI+pdkynL6Ur>hD-ArnKe6A ztw(3IV4^!Y0Lk=&PX7S%fcn`&hYZf9yK;QSHQh{&OB%@2+czROufU=7KcEH<0Kbg@ zg?#Y3&C7JG{;Den(D+r@dx-+OjkqO^8P3_>v%(tl@p;5&35L#pPPAc~-z&KjxUWplcmDYCrfDFqE^p#Q zZO>#$csfaYEn4W(5AI|#j_U4cH+;a!DbX53u(|jtp@$8m3{VH2IjxWJ3!O__cMk8L zjLaLI#?NruTcX%fb?>h`JG-nXT*&q`1Iy;6{GXQ;3%;Bls|SU942vdBWGzK_P1!sif#F%LpeD7(lC`&bq#>uajiSyA@ax565xpMr`gMZx z%ebH8mrD_;JD`80KB;&qoAQbcom#v~VPV1L?us#sKmE}2>EXf0P+ZCRl`4~dDabzRUm3thr|2T%+Ml6aVX%WFs4jw4gw zAQGru19Pv@Z|@Z!gW+9@!G+K)+6Z@(SeuyP^}*>;GZ8a~L$ed-3Dzzq`I69I=wliE zB@hqKKRIvtd8hGM{(grWnb~|N7&!9&i85Kq*Ow3y2mz>(x+SXnRMb%I3a{tks^{Yz zD4IrHLGTvHeNZIW3AwJW5XO3ab7U|*Vow1TP{gvmF@f|Bd-IZuk#7z+&X)A!?QQ)K z7!?Y}+byBU-zni;_0f9*ZvmdU^_4s3ttYZ@_VAdpEXkbKpO<_HT;pGeDVU43h;(8d zVY|AEbt^Qt_>)e5wEeNqEpOQ6p)o2fiNJSdvj~9swP?zpk&>d&#!OOVZkeXS^LMP? z>`Qv0Wx}m>?@h(m_@r+v_JMarIdwH?tg2k8ykKkg0ILyL6+t`NMd5o;a9;;Q!>}aK zi>+Ajfj9z9h2_Q*I{DW}miVTvAfoD6qgyoE8&q5rQR@R5UIkA*XxBEq7k_d10!yWH z*;GcwkMf;Msqe0I%0Nhh;pZ^F?e*0z4;_RxacnY?jw4Jp)?)3Xg1Z58G zR^+I{D8BPJjiz`?EiV$o#_=_7@CIjGC$**a`!2oi(KxQ?=GPVR3Ee9@FGXoPj0}^_ zvrlICn`2G?$owmnC=Hzh%Kcrt2t@!a37$;*?g*Q&v|UBYfA&qS;&=rS)$?q2KhPj$ zxY-o}+NB^Iz~ytuKfll+se{YlikcrIslwAY+d=kc2t`HZ$e`C%s4}BXJm=MM%OFwt zyt!&wR7>X1LqW>71sNNvjUglaNOVox)QeFkP3MV|y*1yOMR{1Lhv`hLjz?Gs!786a9|_yXQ36OZ&u={@OOi${`bEl=)0uH1AWv# zyl`KFpfrWt-l49lYev_D6=3Z_q-eWf&u`y+8QWz=BE+X$+DFu@qJ|-X@E<8gurP?r z|23*|7+~b^8d7bDOEM?eg~N}pR0jWsI7(sXvVoTPK78h1NFi(gf1LOKpdTo_Ae;vc zbjRQ9Qb&rgc6bSA)6ZFy%xk>@BbYE2H9C4_BqWH`-YAF-Ey$Q) zvb2~nt31^9IVPW8n)hqF2>qWG^z*9wZ*psVg9NbJ z==tvb3E$i{e}m)ww=>rvN!ew^5jdyeu5~6hk5;ba*O&8_El$|13Uc|ux9iSA4$9sw zGYk6&qIb=>cs$9AR;)PdrBc#iN70}vUeOreveTV?-G;_T$7x*qq;Y>@b#R|-yePZO zXzSH!$|XOXd^{6W2fY_xQnUZjVZJoNDScv&)Lu8!MGEFbBfd8sx!@TaR;(;zYkqDo z%?9dd*gJxGh~pFmVn9olW(fmzFniAUGBT&y>7SLk65vLH^*2u+iFHGcx~NI> zegEOwTpDusk7QD_C=dKB-N)37gto?;B#04>b@c;xR;BlpR zu8oM~RlI}K+UGrn8OgKMG4r`+NlLPU32G7ie>!fz+rJ@GSsarqU97w)wpvK4pY%Q@ zeJt@oX|bwKe+M!3M`eAZu}I0xF%*nM=Sl`!x=IzwJfGiB5-CnMLOtK#W)tZ#`y!a+ zQZOL>Ts{S`wUszTRQ-kcQOWU~S@NDs24)3;-N5%|MfcO0{cXRx61OS0?S#FPlZdVl zYD9N~x$B$inxjW*8l!ysxZ$qMZ)IbmAZcSN1hh$2M8u5TA*iKoQHfg25Hk-+Y$%~p z+giXVadH|`gz)KqN8X2kI=kIsl*k-yCksPO4z@;XoL0RiacX><@$GuHx60B~9e+u> zqMbr-iS5a4X-(A>GyM)2a*hN1j^|{41q&L_RN*wEb^Bk49@xH&Or6~ag|zm*NBAi3 zP*4t>q2mj|MP6h&2-c%>Th%20>2Nl*kA|Cdq&>n^*m4F(XhvGCU z|3Y9!3mtg&Z>u90_vWbZgqo_;j4%Ixz0?2w8=3#Vd?&Fz%h09kO;`tXPe&^e0ug?? zs$9b`{^Ci#-Xl^O$RUZuh}6*Ck@|tE%4=ca99sMQJvMsY{~4kCQ9b+8ESHF)Lu zYPRw?F5hrUTz?&;AEK-1x&-Qz_u{Qza^?+;4_nh2}9pEDR|52n3Y z_{B0OvgcN~1EZ8cd`Q4@q4;XusIYbxmjaf>4o1cyGKNCH{QUxv`-E?NtigCPsqDJ? zCMVx7(5!088sFbv3Mei}pqkxltJ9&JNytlk^^LqG zSQY%>tYx`5Kpcj)r*7AE~f#A4U$s@n-EptcV^)!?8NAuVhjFRw#Q~+GnA0)Haia zrRa@UUDh^w_JrDD%e4m|2{*>(V#uI#B3%|lwb%%ZHJ~zD>qcpBbn4wogw{-Lyu2)SoAzn+ zD-*VmkM{MZ=%?iQB`poH1y6+SWeWIXFE3O){Z7LY8#=C`(qyOLaCl%(s_Hk^arm6r z=_a+G^Z1~d8_A&ewf6zK5Cy7JUtICANWAQ6BT{Ymm^{!-d1Cj(7T<<3s6g1B&WP{> z{1RP12<1M^(eQkTlj=73BKeTy=f8Ufg8wrVzz_DoNrh8?A-maI%NOp=|Jh3Xi!_nW zj#-j##k!+64KTv7cGvdnyV+q+YCh4VnPyHFU(0(&7tU{+BK0z~R2h4_Q<9t5?& zzi)YOmUZCw@FC=BapA71w+s1PsYTZst`yJl0viSf0VrV%@SkzOx4f-!Z1}vE;rQ1i z{g5Ry2ES+T>eYmEUcA3!9E*I+ExK#t1P(PMwMKV9q0;>w1X(`1r6u+>?W=?T?wPJ5 z$GxfJ(#%I4>W8a_kvwsv+_wvCZ=0_@w-l<$x<CZ%-n4oupX%BhrlFRUFn;@6y_sm@bO@8;c>ocQik|`su*Z@@I zmD`F41$KBx!$s01K&@tSt;TN2CEeceF|(^!ZN1pLiLYD6eQ)byx%MkO>B9e={lbnp zSUqh(=XSNSjf)HMYPC*Sshz|#zj$$;aXdtSz*$49T>GxMPp zibuA<=(^%v`~>LjA6xj<^9JO9EFQZ*C1{Ubj}S>~U*0p?e}Ko8=13c2wFoFhW7| zWD83c3##N1sW&*YCf~<9xq77bJQ7chJSdWBaRvn-K|D^D)W-h0kY)pG8xJ-^ zmd&uW*HK$6_Xm^an!mTy_WBop+`pMepsU9(;zUpC!Kio(u7$ucCE4;bZ)afeId!A-r@!( zHOW`li-PTi6U%FNQ!^((vVP))#QWhS(FdQ9!ep+GZm@Q|fmS;e@kb_K5nWng^$pPq zS%_8ehQP*mq;0mp!G5XhPS%?@j6c&I^t@8r<2>4k)P*`F2)?$_+P)ta%gQ+NhTd={>>{yh7d!#()!@3BtxurYe|h1nI#l*CDT zjpfHvt!!6Hqj+9l3mCoqttGQ|S6Ao4`sP?Y)ZE6BUATRofvtHzrs~`{w2e4~!jA8U z+=EqN8&A&@?j-2a8$e8vo1g?gwoLB%`PY5pdB^m)iLvbd_Ut0Qf>tvz+`eCE!D-Z& zR&Vg~2gd2);;F1ldi-SF&n+!mH*1Y1v)TMMd)zn{uq4R6^`(y{bwj!{8e=Fe^ZkPJ z=S3&FjS)ZJE&bzMxu0qtl3WbXSnLKI!44PBuug(;4)HMn=mMbRI9}E=-l?qU3HuXt z14Ea^gwfueM+LO;eh|06jgbm-J-cO&W}d#w8_Xm8dFQAX2ya!T3a&t(d>4F-Tcat~ z#CxAF!k4Z0FfwuMuk76!m`#)C3l037D-UbVg*Sg(DPSnmFg+{BdQLjwviZqBZ7WF# za(_U2M4&*s$5G`QX3aobv&h11o5(3k8u!HA)1b_mUToP6xlx3ecM&4wsZQYZ&P>gL z?W>?+)BRkh+<$y{>i1ooe)-sG_c4jhvSs)^gtZ&J`}0j~y3yB(T3Rp5%>44$Qlu3D`PQ!gzluPFG>m|>uif}EKvEWfMO7?y@rRfyYpvIi4nZqoZ(a}r+QhnFb; zz)pm95&i-z6zH__X(U2Ie6pFN53n2{Q40h>Tq{Ynio zjkesYL1j~8VW`0T#48bub`+kyB1InXAC5$Q*lQN?bAYc0O$iNkBsmM6KDCLd5uN)+ z&Ok@dy^~#s=!}m5vc&~edChW3A|i>$h!DlsXU*fI%k!j% zt9PK~W9AQmhb%uU_d>z1VZ=0oB8YMsTZ80?L|u*$&0%xRG0>QkfM`t~Gob0*upWKS zN_^%mf7;s@{Cl?N4cER><@|-%ip0GM8U^zsjzJ{4)XL7r*LDAR=n=U z!Rltc?o}twtv0r|H+sL4{{r2Qw~Rk;7see;0kz|NUG=6LL?#$3kH`bwr!m zA?@van@HjvlLKL1<6oVxW5?8n2NFKs6k+&hZwPa%G+Y>KvI>nbjR+Hu{-Y?Vgk)Di zjUS#I;|z^$Y#LIz!r04f^@e!dQn_vAMoFwLRf0gV!7cnXY(&?0U||3qKA?rpcqjyy zD#1o(cV=v4ZEZj=N9C(!=J;+Vml2@u*G7w5HB_iRPKRVX##Q5C z%W(M&l)_nu$n|K=fG*7yD&pB5z_Vui=lph}sVdkW2naGnvgt8Gnqx4ElJ}S(bC`%d zO$>lonFOetZ)2P~6vl5d%IhjF7jeBsWR3HC>fI*p77W_ z>W3VkG32W~ALOB{BM}yK-^Y%(_O7gvmef0EZ#59`2V2wA-#ZhK)pfBQ*L!)%?yHIu ztSb`WKy93TWV0AL33bp3ek+zpYS2_&JPI1mM!Q-Qd^|5IJ}cn$#`jf{&a{aWT?Sve z#@f7S`e;wI`!wl(pbG$g1k?ej!M_HlkLZ}dc3OVN#v4Yfhwi!yhA;apH)ePU&-->u z{pe#&Bl9r2A+yxk?doALg1Jg1M| zMlGfp_27vCn!-@p?w{noA!^F)4%~Y0m&BEQNQvbdQs#%~oc=1!pxS}J%Wf^K7p zx*3xkmT15=BEU&glfCtibP75BkPWq*kA#U2x;sm%I756nWAxpfJjv8|hsnEuTGuqB z+bt~ONjx$;wl2esP?`ARw78(-)#-F~pL2s;NS|j?FT>qFQd($oFcovY)|TA9MrOu@ z^^Y2C3i|ZoOm%z(Qy(rtb<3ou$iZ6#pfR^FlifGr0>VDY6l{EF~Cd`^@l-6Q_Cf2PVqU~Ypq5-)Wnj$RJCGD3Zl*Dc&B zUTIQylZa+#IbGU5T(l-mw(ktJMn9x9$~vBskxX-!$;;sRft^54sHY!NG5Gd8K@ZGr zGqQL(mF+MXZmfU1VZO@YE~ombS#pN?Jo)wOwIr!o5?Qkh7`h664Odx!1@N?iM3?%; z4Q4H}J(o|RwH6OMxQwkAe?H-)VOC(q+OL<|x|x*lp%XwKkO%cGF`?^DLv6FE z<(-Or<=XwDwp*cSm-V4i2Y;`2F>yJQmu8=>!!kT|2gGvd^$6yg9*V8wVy@@|@*P_l zr&>9Ehc7#fShN~)|I7&{nFt{wR;H|dD>QS3suDh1Cya|ItW%fL6A_P+*r5JGW)Iqe z#4*>FBPBGoO@XM5!EnD!Iok-Rgy!TLk4)yXweN z1gZTJ(`4%%?0{B%bcY4|sMkCN%f_^Tf~h9^Ez@DV3((XLhHi7`{0zH!gM^bkuI^G4 zYo!voNGj%z?yCJdI|# zg%V2hvB9L<9>t&&t>U-*j)LJR!~5GgAaC=PzOsKr#?T`X@5@thxOcV(zAKy?7`}vT8zt6V8})~ zm#Al4juAJvS^>_aPD4f3h6%|Y7x}qo>4EKet!`xNiO$Z*lpeXm+U-V_5dh0m;_aj2 znt#dn!UD^ygpMuj94IBN6`{mND`{N1$mhj*seeryf@r2tpR+zCN~3(!7v)%<;k}0? zpWSl$(uRv^HlvJ3Wzs&xrXWRb{>F}OX_Pt4K|@T(fo_cen6yGp5Bp&+*O#X`h7Xuo zDW$FjmRii3OV%GTeh+xDCsFwhN>{PJI^(Gw6~sL5Rf?e0KniJ@x!veZjV<(xIURCw zI~})5r;>b`*ZXJw{2?7#n4X-(W}_BWK$;-JHc(0(9n`o|4(%Lru`1rlVx4mFF>#ap zxJA8{0OU8~R*r5)=f`KS&u`$S=Z`4M7nieFfg>tA8|w;eZ3T+H# z%_JD__WrZy%~KUuN-loL3CVchvqP2jl`6C3r^YN$5z@4z+Y2%;yUQo_7HPgt51wR) z2N6%;nXVMk2OZ$@qkvLNDu+=A%!mwbEk~Tpyopeam+0LWN=Vtdu%FBWRHH#$dY)C% zm^&?i!V7>=o@`gD7^#?M1I(lLkgzPehy!!lpDk&` zsd$v{`@Z$@{x;S8LT~$y*6?oeirZ|cG1eMDCuyNc-YE)fGsteFpa?3v%35;8jckni zYziEVsOtxoGe?=`Sb59eynB*mbZ&T1Ox8GgDVv{2616Z zjd9Hmvg#9MIU2@m#_dnW`*+s{ZaZ*1GFxZT`TAVTQT@($Hf!7Y?Y9hFj?64|`NYdT zQql3d)&a;C{|O_G`{?}G<_Gq)+BV;OA8ug=FSNckxHn17eQ8dNmw26UCMx*;O$PJN z8N-I4mcHj@J}i~HNL-=I9mLgL3qvrcnca~?eB{*I{j`)d+Oh^Hhx+WXUJYN0*4l9y zXn1bDYJ`cUM4UdwNR}eav~T+EpoZ#8;8IB6W(g#Cgi+x>Zf(fyxzZfkI7P~zKS^Z> zg>>gzTTS)R?+gqQs8c@q!L4J-Pv^FLe@qzUM|q*KWZ{MXaEcAr>VEJ z$>xR}PFs%JmwZ1KCkGwg@8Mo2EJ;axxYf|4AhrT)4?u)++-yR7bvG6|x~rF2Y@%Vl4Q_D4 zzXZT)UyuTvBm=eE%7C#9lXr4oL9k;PPMr=YvEk(6O}9vM5BFU*-u2QdJ-9}hk-Sb9 za$gCudh9j}^l)%tY&JXN=MYhanShqp$Dm|u>a ze`aQ~j(d0?u5<~-?)2ax7={!K_p1MPE8nzNIj>nwA_cMX*R4XWw88LM&l*vh&b5Y& z)@Qv>Y0s`f1YzH);_EVj9@Dc{=>k2j*#OzC=jgG5ZHP`N%`$8dpHaDC@sNv%-7|Z>=JFiSP zWM1zMlG?zpTaDyBXnNPep05Bya-9~mXuCaYhA<>R?UY{e9c|*v0^JjEYtTAUOR!M) zMjA8zO6u?Jen`Ru8Jhw3mI(yX!TB4p?5H8J4n`Q6=RA&EE8Y^EW*dZT!;c2Uw5*T5 z-ScRg<$r2`gXV3~ru+5fBDKFtPQ zCXIdG+fL%cdZM^De1r|LFso%IY$7zOm3b5|ZEByzh^;GnvBUC%gI*)kT-Y{}{;S!{ zkH3(31x%@*g~du(uF_afO^4ObJhSpKV%~32zz)kYzemfb;BHn9#72ip& zSG(IdHkfAl$5t~8XqUvWz5ki04Po$Fo+avD;=tH8ps0djOndUoE~rHj%?XL7K+uO( z)*El*Twk?hs&-hI0{a+M#`K^DvSu4BXC^W`Z#W9tQKhjU5FRO|wM33?>3Q#Ef07{v zc$CjRMfclCalB$G6f-NW*?Z%2i%#cyd=Db{JKQp(Jqr*(BVt|QjH?KOw4bzDB6gL!nJ1s)JQa*7fXbi>4wE)oYLbv_S6qNaonnC zaAlcQoSEONPY*t?C4i}o2?8?DjTa4JOAHVOTUU}-&gZY&1#V7}9W7lR1BgJy3T zj?873oKPLlOgB8V@+)w-VecA~NfS>WXX3hwETJW03Mrz)I!y9nhP2 zyaOXE$Y#SV%=44Jt!eVx95CO!W_E*K-$7B3U=Tk9tC8x`roQ6DvO>EmqsCugRohZ8 zw47?Jv=JA#~J(Hp6!XcG%|jlIs+oJ$)cdkD3u3KMaXXrm!yZI3MKi$R^OUTDUl{Eq%V zwI3xCB5&h`;YH?Wjzmku-@s}sgBf+Z8;MS(0t2Dosx}@)W3NNowF)1;_2hR!0eLg_8rFCjBOLj_4eP_cm-E_U!{q_Tby4d+bw4wqZB*|r z4mz4DLm2^d*sT4LJqN*Lts9+&>nU>j!fqm8ZB4XV%+|PK`R%@r4~+^kMK32!vIQ7) z2O@-8$a_xRC$3yD0jP)%c<$d$v0r_|1MfKt?ZH*Ooa~B~7_!+w6I@;GHV>h)KxA6N z7aMJXxwl<>`-&QU^?u7y;OAY2zJOa|h`sS_7>eQ8O5fba)3rE_XFtY-cAd)w6DO~w zo2(@1M5$3U`MJ+$#+6;y_k6$LsG0%?EAvc9a9C%(It zbNZlCUdlbcKKE8*W2>22Bv;Gb=|jo-Ea^EJHd%p~m!*(^Cu=9;z3^C}7*ae5+$6r; zf)m}{h_=%Pb;TUM+^VBO66BN0_%{EI)bS=k~T3bq27kC6p6NR@Z_qe6l%b@%e?)1}L;>k=$?^E$Z7 zYMOdY-n|Jk|eRDR+7R@*qJ+qq&*TidG6o+0D=^)zw&Si)q*6Ayo~H z`AmsKs=MRd?jE#HlA7&?UOxUXS_D;mqT+74Y{tmfkRN4=_~rKDXJO}%;R=?!bNZ)U zB)I7d=Ra&FkV#0c$u&1FpuO6oS;TYb6fcEX%Y)We5{I^&{_dCz@r9xP~4f{{)= zvvvKhNw-&41pSOU56%Ub!SQAH9_WhQDnJD*82&S~5cPu(*S1c8OWq3~{1-PqqN${W z`MWh`wJfBmjzq+FQ9f6xZlS;_-867K;r@`L^j{g*>xRIg=LG_ngp&gY8mZ*8_zME= zw1;8D*8$C*Fi-MIy5HDoREKv>!S98w`UXX(c;PuD%1{5L9OXvC`?V02r5nWSi*xPY zTZSpMXOmW{CV0k=m&q0!$mZ5(KCEUxopKYba5*7QrXJxDQ|52x|3mm`w9FR*V!(6# zXN`)(cVhrZ5LFjL7;z9u&2#j0T0qWL=uq03B;XUpupxGYNB4@Q1&*Y?y&>FL=^*3< z<>8V@yyta44ULG!c&yeU?$uh}mUBUW~Nxk1g zVBh)+gw9|zC&pd}v|H&R+qx!-pJvi+tNjXw6p|!2>Ob$<#0-!+XU2ad7iH=jh*ehw zp7351GU;&yz87}106kLv!wm)5SXIUjS)=e>aWjfX`~7qcb*WoR%&Q?!)K{#;nc^OH zh_c1`w{{{ZEZ#^zhe#n@06$v%X44Di;?QKi2%A=KJCs z(%(L+t>-F}XBILWwC&k)4D-xn*Ff7^9&LATpFNzkl{BpPe*5HN{Ybg!vHg5`>ZzFI z{D@$7+6bk4@LLF7fC))WL)zc{2#{BviWTzmaZSNUC7#o)*psuK;&xO z5&XR$QT-w91}|@xLx8$jvJ7`XdXNHEZbdtiB`mxKB`5SqNdH-!nvfT}fZ@*x*Ky_> z4<6apB}&=P+xLrd%dc?Q$1He0cGp|znL^ltr~penxJlL1_w9jZHF`a>JF~DNsno*Z zP$+Nx8CpK|Fn540U+K;6e4P3kkZ4-UGf(^8BD`*-%`%+P)S@A$4{4m*(J7fK$r9Ua zM5~fGKU*iS9gcw;F_y*4E=u(FFb{QHb3&^uek2xbrKF6*h%E^AAQGkku@zn~>1d z9jcnhOEl&+8;A~XG=cZg6&V@CTE!Z{M5XvgwDh>O1F~X~RA7Af6}g{N5!R_Asm;f( zc*#k0LA%6!@###14i6)3(~!Vt4z*PspQK^41Da<~^YR-VNu6KK;|HL`E)|LE@?u2~ zI_USB84jK1Lg&rQEL64t54W2XGI4ybX=Loj9&I{w8@$1lb?u^FG~zPwG>Z<)vwOLY zkz94}6P8sgmdlSH%qsIpj;p@Cd!#!q^IEh2*Y`$>1l78aZ=cMe&)P;J6zL_wJ>9;) zF1*wHa@)8qw7GGq`(8g9DTmj351&PItDS$W*E;yL@nY?aB}!EpDTV+E{afh$6eNst z7MJvN%=zm?nafpAQeQ*3D4RoLpKW^)^DFfpo^;Ln{qB^MgnTutv^Ew3JD`>Tg`mYV zIHkirY+x(V4VtwrlZ6K6J`Wp}om^hsX{v+P*$kJW`E+!V zaTmqH1FGh4%qnP^b>J2iKtGmh%WPq{uR?bi?3&`0>`j>_F#^= z)k%NWifw(<$7cKBwO>KR3eID7xYVf(o7BPEj-c2rWb~G0Os*O!?N{8LA^U1|Uz+2~ z&Cu3n7AF1$C0&C`?OCg&pW(x9&CQ7Xg$sEt#fY2ZDQpVbPSJ_Ag#ksv)zN`~`Cj!g z7ne#jn?3z=NXd072vJpKt@@qUaB`QX>d8(}e888sD?$kSFE!h!-H>}kbB=>yek`o@p~zEy_X|p-In<-iP#J^Rls>_{DEcph@BwnW(VKd zn<<6mb^orb>@8dONVEV-G2$}&deA8TPEDN_cf|ABcwK%4DKWm|KdsFk7J9v+lj7~G zwrLJ`U_+I?r%;;p-$17OXE@u>9}QG(yysveMA9nZ*x~j8VUN>Mxsvy{ji<-}e!W&9 zcHh%nYb zoL`}R+89b1LqMR!{z(|QW9}==`fFu#FWrmxY63oT)#}~I(iM*?>tWaat@1PzwjQ!> zxp~;q9RIA+_q;02N`qLiMrTcp03d^)q4S}7%@cUSC9`?U0SAWM5@Qqd=C@#$>0#`) zu_y6OsP0GKyTLMTm-6^F2t+lE3Pd#0pjRmm*`zwCLV)V}N?^69*-&0)ZEH#3$M>w<#5&tjZz z_LrCW*B14tzM{a~E`2lgjeVI#ngzbcF>k>t$`6&SSOacO3cXT!1r_E{X$@H-yD zg)Ua5Emt%zcm`FTSNlP45?08p(8jpRKrZ(G4Z;2^!HTHAl^5u<#e&46m8Kmi>6M}* z*#4KXox=cp$g5onBKEKe#POr~MuKRt(We2Sdf0HU5eTk825#+fCm1|g2`@JL>LgRYS+~zgC56*K@6}fg zdk6peNQAQcfUs+9wn8sya{p3PE>panasmb|f={55rK(zh08K1o&z5B#S3jnX{oS4j zQ6T@{-ZUAPDullQ4&jX9oQ97{to zI;1RXf{HIy?3aYG9vHE8@ZW`>%Q=UowK%$~FRF9H0=`>OM^Sctrq)Jt=^m)W-zR z(tdp;;G_m`xV4-sQ1tTs9e95fRfPU?TYyCg+*Zdx%%9f!ibx>o{z^SSaMZImy`N1) zzxttfr*bP_=N36s>Rr;WAqYr!qHBZ1fwAfx1XkYxdKDBbzq)eJDH2sLo81{P5d_&` zNiz4H-bQV4&m8|A`m!-R%EZ+COYWM1%RtKcFA~lb2sR%>jw;`bRb(0g&?LrH=@Xra z0}ezx7tZ0QkFDMo+yP#HdVgC(@x{9^ZW&G;l3Xw$Z45gG#(DIBVTVY~JOFb6Iafs$ z#x(2a2mNY=ObC@#h&KhkIuv>UqJU<5l2J)do4?6Du6?;DE&b?5+gGZXu_Y-J?pI}b z^e^8iv3Eg&`mP8tlGUrwkfoa#kq>wLyjb_RNE%b{$HR;cAA!qyDQG#wU>X2>vUgdx zrz#~Dose0vejta`GBnkhQ0-^qk!n4nr!G#Qjc2wz1wAM^n%{usdXP*mf`CR1BU~2n zC&}LOY!C6TP1q^mn3;XSGslR#bi?z5whbF#b`u{d83u;i&V#5i==S_?FQf9W*CDcQ zwpInFZGU{es^A4&5Ya^zYc}Wf2A*#|mo_B-g=Kf{5{9kpJNiWxg z)83$o?xpq_x_RZu$*nM z>|Y4Bf8-+XFXUYBE0r`B9I_~wafKM}OAu_-sK1mIDlifp1 z)5h@sAnwiMq3-wo@exJYvhSuMlr3A7WwanZQUCITot(nO>3I9(g?32_}Bn2k&1)6q-i%HSiqhnn4Vl zlQiVVT8qstX7ciW9?R9$vbONa#5|af5I(<8{t0rlH4AKrVquuDh{iO?5LEf@*TGDJ zOk%7rq#||$V}8m1#QmWQOZM^sF-a;AADO?{*}pl4ll{$#^S|W{LK4VY@Vc>Zv9ZZp zt+v}wk5o9HJ!$UX9l6g2x2eXlrnZd5c~KW z6HLE;wggG9O`(q@X;UyMfPPXLey2U*4KomMMmy3pEs4*wDewdukG|*TfXKL}A)G>* z$`Gr52XhMVaco8PrREbk?B}9|$X86`(JI2A4xJcs_7nL{P@MN&Y15x;RM4*&^q7+S(zQ)7DB7J$l+Z+U-a zXU8=omNR-G?1A!1A17=$ba1$yGoQ_a76!5x43>=UDv3qT*eC*gWms4nGVi5r?P4aa0RUcS<1n zH^?tS6u^T*fr-R3f~6z@2osQ+$d1DIK%m%7ls{~TfaxW&w}QLm7tbu@1rg*_r_Zng zvu)xeDP^dZ@%Y7{gt}MGn~DAWm%rbzJp-YbBUkX&rP%$&1n2s*ndU#Set+e?lyAUB zZE=B#X8?T=iuyUeT!&-k;~_5`zd!IEwmto9Er1ICRNL+IjC}Iq-a`|cLxfje*Ji+$ z_%;aGOdpybkq$8$g=TBX#;2ll%jHwW3BvaWivZifJw>^$x&phX;<9%q z+^%@P__oBdGC!8HUqGp^xA4v^GIK2RTd!NpIzl6x<5`oQeq@1T&H=bS_s-#8V&&0G zEs7(_Y0(wetK`u?%6>v7Em{TKpx(#Z^aqXmjf+}^$SA{G8$+jtzV-sefIrZ~B7=LTQ=e8GStO=EK9Ds+ z206=G6sz37Ge>}RH?V?Dh&Fr!y!?GcHCE&^yBwLU z1i^3vOE{iMJF7GdReQ6>q5^E&lyfL z_HW9-f%SB{$H`7Zl}9}v(-ppS@Xcg*s@dGV8hYySkzbN{z}^EM8>DXf?TR1P#kE}tk_xgSIA$uUiX?3_hjYvb8-k@LSV(}D z8h#82m42dl#;WN^7*WD$ZiZt#G-vyX!nKn?d2Mz6erKVDwPAJGBSxv1x>US0*#@XQ znv%N)BG`xqSd8>wR^Kjg#{T+o)X?QNhphUlA-?!$n?n5?rJ;q)+@V~71yyoDO7ZmkZv8Ht`aq?Q1(yBz7i(kK z-aa*h_|y6t?Q38wve8AMek?$sd16y<>j9_MV#vkpijG>@XP~xgMdGuY3mvg@K5`3` z^WQGgsj5TTup#S;*l{iku|dqPH@wBZjQ*6p-2Elfu-)EM zo_6{;!(xFhMai*E{-wkQv>M^=)6B{6#kNPevrl(g?&BYUI3@C>;*pn)L){Wr0I4zcUu`0HZ{ zcLz^%55Mzre7N8xzF9}o^3#W@W+do$;08NB_omQ?M`a2U*Ia~i4qNu&VQHL$^;e1K zUfZ*A&DWPl81_3;(jd>&V6Kq>MmOC9vgG<4>857IE@&~ps2{j9JK>%Awa6Gp&+=oAmmN6%yvw!rfDu$$ z^;_&jtKoW_l5VB@hDOAXavqv?t=6h&xQ>@``V|lVgt*-yaMG{66E|d>4!YCft*Pm| zQ97Qt8Wa6%($e84Ml{7i{Xr-_FZODT&BvaHI7$oDpv*NftJsghLLwKdZk;x|Zz#?g zU}Eef2;sFSr_-582lmwi^{;F7w!`Sn%rAXar)^JR)>9|H-oYGKLJ@s8^U>7@n^*2# zd2r`cJ(6Hf`p$`Zxkcg|2j{YL*u{k0TUl_BAD~z{x|x0Mc0B*5B6g>S5F090?Wd>T z)50Kr1L@>Sf}?9kF@Z{aE0wyQrqHrAfd!iqM=sy&mTl3dOn^!w?+TKOiXvE=OsxS{ z?y*k5U0>5H#u~Lmm`$XLYx`Q-+mwDS_YyJKvbb5=7K~wNvySlIzBX@7-h%*RagzQx zy;j~4?K4AxukLD1Jemo6-or_9lymNOXRTCUR3OBiqUX1_5+Doddf`Ei4ktrJS3o{4i{ z=1pG@To_#)udB;Nv0_fo5>!5&e$iWSqcsaZGAkZlFMyZ574E{YLgJ5a zx-O6)n0BFNNKCvZE&2Nvx1DuXyGVHLhb?_uk28?nUQWLuD23nI)e1ivd@)W_eX#cS z7t>>uQ$9(hrt?b4_iu%XvELfQlTuY-p?mMC{h;{V&oSfVE$Jr}w0IKv`!*0pXrC18 z%g)l6rt#HFZY|Qq=0Q%K5Q)h)i8mq*yqQRG2Qsz)mcFNy=I)Z6VEk+7Wc$A7-2>+=E6TDy#^#1WMVmHzQ~;4IpqA9n@Zn ze&rFFi%Qryk{TCY)rnS+pSy)>=?%5y#IsnhKhYGc!>Ae@Ji)k3H32g&-`@6W9Exnz z4sxjpg*4-az-oOAF|*uTj-D?;oJ4E_;AnmYRE6CnY3`{2p^2<;dZnv)k5+~W)g$p1 zs+gd?E*9O}B`nkwDe~C+>f}NCu<%UD9GXg*>`=`EuxaEWGqu6y(s(E?b@y}rj(K7Q z3u^k|O!=!C(fOi-@ToQ43LFN9f);d9q&% zRYM}%zoxTUTa{GIM3MQpnEz6jHB?Enq+l$J!)B0G})xHowjGQGWKDt`-kP=n#ZG z6Pug-*oVuHRmy2ktt!fDd`aBgz87IsnyB?!HG4GQeqEXSB>ybAGB>;x&hA;X5Au*- z8KBt$NM{f?`)ZX)LIZm-ERNGxu*7lEx{6e*r$|+HR)nVXK<%A~V)sIIewx>0J2M-i z9}tc>py(Az9#1EJhNxTi;{`pV<+KAY1J)zpL1kaLCe?gc<_~e1^N%gJ9jrpU?!VO- zxS)C##2%z$XlG*LF@o*K9*yZ#FJc&aK(2y&pcgt+#)Vc#EEO@_ZyB~BrfZF0de;9Z7`~}qmM6fIhd$XaC@LGzp(0j)SH5)nr7|6!cD9!lA7Y4S!V-*2@Hl}C?C}1;fx%#T5kS9k zq!fhC71^-7bk(vTpl;obvA>=3ZrifhJT(pv}g8VsYJ^ae|;va-5! zqJor|9?Mn}O@XV}4zCvuG=^QvY1V97&`)7?V9Tj9{$&g|@V|F$`hP3Tk&~DWLD>K` zava!tIOPr}hAC4Lg$m3_(UcfapKl#?$O!g%^-L>|x#?TsM2<0F8@LFzTmFZ+)bDVT z`rXe2{H=%j4?dIAKl?%bt!HYAzkyS0)`>``+g(-?@$72h1i}hm$jcKHoiXE{;VKVB zmb(@jBJ+7==6RENmdR_8uB|7802&m_<9$CUw!XONG2658@s$cTw)kG>@abe^m@QYokv$5UPZI-v~OG&}Me z1_<*VFNZg9ryng3&_9NNONZ)v`ElTJ47f$9TcD0Rh~5o2pANxr^0)_OyL}Z@-WQ$T zC9|DV8TyDC+~UKz7f={#usRxJ$|Nk)w=_&o%>IOoT#fY6C3E?(synmF7Ux-Gm?r!~ zm^MGwlqx^w;Da#44FJ|M??&V)CQL(>J0-FxaC+qkLDU3R||NhJjHgXL<3L6ElP3)K28~Z5PZqko!X;6p8 zsdsW>--XZRxMXTenbld^Lc{S`X&c7&xKG78!?rwH_!i3wX*c`582#}x`}T4O>ycPC zEh)oGFrwT6Q>XuByo2R^_&3m=@4^>KK~lAJ;JCjq@OQ!n{LZ-jQDFmqZ`}T%umQg_ zZhutRfZrLnKPqg%e|Wq7&#(di+3ogMVFUiN+wHHy2K;l!*4@+aDD+;Ga7t|0-^ORM>v?sQ!(&+dm8&@Nd1{{&CoVf9vh`kHZH1&8+0# zw%z_}w*h}UEBS|U`=i1J{LQT7AII&F3LEf8CX>G-Y``CyO#bh~2K5^ZhutRfZvLM|9#y4sIURQih$n{w?8Uuz`szt{SjdU{*~J8j|khp zP`mvRVFUh!+U<92x4+tL!2b^=_g{t$_|KQz|1E65f4=1YZ(#%e`Burly4{T0pyQ^e z;$abUxKlOKAonw;CPfn{3-624ZRPMSG(GinqmsPKnnO{D{4+S2W_tR4GICDtJ^i)% z<1{~p$Cip!_*eiP<}4G@QUAl0IXQLgOY+kCj4eTP^blp^Knz?s{3uHBoKj{+zzA0m z?AWD{7h7i<(Hm;Ig9!D^bh;U}ytR>|2wGvflW!!=ySH4Oqti?A>ka6YA|0$SPlUz z>{9wHjaJHhhAA-gy+Qk`NHqS|i;IoPtS?h5q@QHbh+mdaW-IF*vzm>L<<=YXbksi{ zu0p7Cx}1G1CBG^PJaPJ%G_BsHzI4+>K<%OcO`Gm~yfViyP~6+VCC6V0r*(Xmw?hA( z`~~>>=nsPtZMgBa0rTQ$xit2)vv0SPJk-j=_)mZ)1|^k^d8sVVlb-hg8|t~Kc7&&% znFgHwbywG(45Z%C+0T5-a6x#{Y4mf=QwW6B8D#@PXAew)=hxMU=S$4S8sCFWHR&(o ztCg>XjlD?TA=?XF3+i1O@f9}e9I56~jLO&3Lr^pbK5SFdQF~Lx!NQ(dvG1qpRnye( znLa*GFGQc`ACZ+*7kQ<|Y)|&&@&|ti`+K^=zd*zPLYMqc5VSusmPiId4gZ9KegNbW zG*+%hDzOgu@!3D+ctL++_4OJVrHSav6E;Uy4zTsyq9lyBR5UO9s>XKaMR6n?uXN{V z`fQQ5@#iF84|}7CkgWQyW406!1kMD|0Zkz8fSPh+j=)BM5p;UD=GSKi38Ql^=D6Xy zq9Wa+oDr8zw;g!r2!U9WM!D|}@U5YA7mk-?MQgWQYI-Yj-jTjv7s&p!l8geecg$w% z!?n+zJNKK#pA5jca4x-X=lkJqm+kpZ>Sngavu+&qO#apiv;yORlEo%)Lo5ckNuT$AX<2B4)cgJvXWnCy ztE&0@NZRwz&;fqFrDt34`J7OfZS_YtXlWfdfYi>Io1w)=w3k2TfyJyU%WB&G9wN4tSwR--|&&EKeis}#o8v-0wr#Dl}vYY z^#~>)r7mZikgQl;{{{c$PY4(CB-}xB5j8?{Q2H3!TH!W1zLH!Q{nfd+yPu_|G}ZQH znEvS#xApH~0dG#o%BR0z5B-LVp#evXn$7hJ*ouN#6zj^@ z?r3_72Y~@p7yQK?DkMNzr@|DP(k`B?OD7bz%St3a2c4YyYAd%8!@q4$&UXtd7>rQF z01Td8TE!3Z@t-yAhBoIDvbF>1V(q* z;AKD_$u9*|kVk^dxBzlR9=s$I6nW(@sSSBgwNbd?!nc#Z`6x{x;TI8#M^y!)sZ2%1 zcWz$yMBfQUNQZ*%38bC|MJMu-CWR1y51sTR+&s=wI==!+oP>^xF10|zj)0&X9QV=v z$ek7Ut0a~PgW%zb?g$4Dk1K5#r^6{?inC*r8mz#D^FZ83>`%i2xAvfQwxb zh~POLn=e@9uf^Uy=hpU#HbhKqTOT_+lVDxFQ}MiL(;fZ1q3K8v>0{67sBR=M}Do za)NMADr1Lw$&^M`C3auQaic`32PJB5CDqbxxX9qeD}d|5Il7QV;3*k;=8T^JQ4_lq zKxBIO(X(yHQArU00^8Q zi+D`+Rtq&ez!1uYcxgAMXa%a{)jby$tU;Ak_B*+ZoP9&MKB=PErTo1(v^Q}^U{)D2 zj{(E2pp=e|VOq5#Np3$Eh2AZ7VVJS^HcWMATz)*_?$-NxO!LCjEe8eny{*(E}9tHQKk9r0XkqLvBncnbcv)NWC>EckYHvM=_%!v#*yHJ-hREQEEN`2Sj zg|0@j&(;oS=PV=4-E5II_S@9AnaZ7fcRG_Nyar(@(VUsJf+*p-Lz@KP~va3abZmbU7sWDV=pwEAHa%6;B!i+BgXCUhZ;OFt!Q)2yE3jZBDFua(s6*y4Kpoa^G}c zT{O;2d@Z-8$UfNvR7L~wHmg{#VbiGE#L^tM=xl9zt6s|D!dexoN4XkN_dPqLNp`GU z3}NWX{{y4$0raF3 z)#ZCYafzPkMVk+V-hzTw4nKj+0yFWzH{~u#t4DRw7oULs*{j#L8d1SYSH9w4kq8TS z=kh5h3`1*Xlj<1H7`5MSfX_UmMDRF$T!zu<>q(X;@^UTnYL^^^#0tibenQMf_fs|G z#-Eo)c7|t%d?=u@+{~Q1=;LsBWPZfe0D>QHf&eESZESE52WvILO!muRwwv3wEa7fT z{9eQC8Nw43xu$5d8m*c=Nj&c`oh)flrqui%?gi-*?CW#Q4!-w=&HnKjX|dIKom2NUYbu8~7Jbw$hc_SHQ?^2`f4}=!@@4$uBW(y@3Fsh~*yYO) zWBBmHk13sO5+5e*Su3q@bEypmJa9ynMBQ#A73DJdrrRPi)1@N&PQ`4n;JW6DTH%54 zGMV;ffWq?ziOW-o7H9ZhTuYuvmSuzhb^Mw`$~Huq?r`MW&}hpd3_dlD4oqHh7{LDA&s8f;*T z0lrslXpdGJPYsVd(&F9xK028qzw74V5@y}z47sD(9^~dzkr<{#8kHG zoW>h9C+mdfi)_}yc&|(Zpjhugh)jg2+p6$}HuaPwN8J9R+<@HDTgh}um1v<@i;_kwWvVj} z$kz{^Y>i5sq#h8l@~rVl0C^ns;K(v{UsOrRuibh*dLO&4x6snV{6z0*R@1;QmBX$t)rhp zNQ0jMcKGEde7aF7oJq89I=WN=yE53y>>(=1%BlXJB{)w_$!6egzNac zd$^p?+Kbq=CbsLrnO?r=B9^Hb`d04Bf!qlXr>DJVr7~HYm)w-RM3s3c!%1`if)6*H z<5=K1g9cs^Z+tQyuZt=e&doHN(xfd7;AD0nBNNQ-C(C;<~})>!%Rj2JE=rxwrn)bUWwP6#h3Z& zkD6GHs%1=w*7a1SnY8GuGC2b0lD!trsFHWCpE8ak19iHBQFO%fHlPb83sx{>*T;gY zd)(ac`jaaK@pq}-G_j~9nln`uQd=)clco4zZ_R$s&RyaBo`FqiJYX9qv&uGC|6nGa zsFE$2&i|&Y}HFl%v>(Km~WZBHiWs5LC(qd29$I3X{|_lOU+2d54l zIT@3E@py5Tv|*gHA5()m?hOWYZq(|kV9l=C)SitSUz_FhM&g8iLQJv^G}tu?4%@o6 zYUuYgn9EzN>|4Dwwl;y1%e~ot{pl<(D~1c7_;D^Xxr(qFP#0($_qxdV-J6RH$g+Fw zZRnPnW=q;I{IWZ}qU2D%Rzg&y<%5Tm=y->FCg<6Xh%oI=5jSgxm$E_U?e;n?O+3o> z=zp$RVgbubRrE-+|37xGN@u-{TIml((oX^F68XT&hef38V{#E79UpWIVkW5Q!vOT< z9aJ$;yakliMjocyY{PpdVlZPhyt`z5AbW}zjw8%wp*R&_9|R)is6pslW|QdIpMKCdql)Y5n3z7=sieC>@><^>!HV)!)-eav$d?=Q7%rrj8Jph-S!bzC(f zYuOTdT`M2p)2}4R%x~A|NXOMQXq@c)JpUS0ttKiRC-{M)`V&plOgPyMHXsi#-f&fx z(<7wJN0i-RTZh_Av94-pQGL`R^ukk?`xgwYjUJ{!PAwFzm+!j-p(vC%!8-m$4T*WW zGRqO>MCxweFB)~q3GCV?&P=-O95_!2OjR}8odd|Du<7fLILd~_UnXWjeu{t%LTIpj zc-ZN=j}Cp7>5%(|o3nPx@u!QuW8L`;Un1{V2`J1ZL+qkakA6aU9Zfx2=^=RPxiU*O zJb<&(tsCBRl60Y?UYpRmxxuypwf!nf#d_LW#V#^KPVCb+xF@6zlRie?UI~&)!<|V0 z6yXil`AX+Jt+cUA;RRgHuG2c#O3NMZRh1`w1eMEgw%)mnUp+0a;eQP*dt&Gx*K-$R zk>RzjN*N!`i03Ns2G=K6Fw-UqNtp>U%vOA_9^fu9usopRu;o5WTNDD6_O>ZXwXi0G z*Fww}eB7W)|K{`5tIcXy%nE~fhx83UlUme-)bU5%^cPC^3@?TV_$$iK#x%q61^`v< zd2A8EkpL$#SX`LVAOK_az5D5xP8_CLE{(mj<4WQE=r8XbpYg2`$;M70R%v^QOy(c3 zBa0Y7A&L-UA}kQL&++3Yh-7|&#J(nT1M$O^zv?kjKw$^d+e*+0;tUBC-P$!NDnQ)N zH(!a@qP`aQUG8~T*J8q>J5x)KM8c^?D3T;4Rfi$-3W@|9;+Y=`r_uD>j?)6jX3$BJ zSJ7EmevK>sVpQJMw&ayK$qMXg@^e+M^F_-N+`0Qs6@~qTIEsTh!P6J(upwbed^ha! z97e2ktiA@Wv$=_Q*s>&v5?ewulJAVN&(T=kdi^r=97NNXaF0kah>*&{H*K}QcQ76D zcxKkco8-mU&XWIuD_87-ab#jHO%{L5^$(fc5}=oz;U%jy{$vi;JM+5VR^8ZnZ%|gf zr$p*WlQI1RAho8<0;rg$Sc=a;pH{f&Q}no6ka$;mN`FRgfEZJ5I%PcF=;LyMIy5^J z<7mIldsY7{`=b9fc0y3G9K9{V?u2A-lyUU4!OGGxsaGFDk@eCfU$PAwWCoR1f7g_T2F z&8FB|IDOlYpC)`Gni&rFOWXRpcz64MjJV0uKq7SkcE%Jp6QRVumVAHe%~g=(u5k4IeI*@w{^f4o z+Lhvy`W(crs_*Qd#@Uz7ogmx(avWAKZRY8tKEMyBeXKu8R2(4juJ#MYyf_x?oO-_V zjM>A9HsIEACQYBYvyXbD54U7~w#BHwk7D}z2!@L*;#I)fEk^^K*Rbnii@h4s&UPwbC9QgQpayd;#w>%X-pAKONL*Ei4Os*f(g$|{eBr*ju%dP zn;SFTl`n_4MI+N*yz5vf{eT{|?xP$rRVklJjJL7-WGOl|#ENTGY-;0U{Pvk#?ncvm zz*9MnR*2c3Ix)XmD@fsEvNQC5k4nrR-@w3aLl!d!Wwi&5l+udlTo|>aO|=89u4el3 zU)|PTp}1qI%FsrAl|{XQG8=*z!;tpzNqJg2<#AY^+MAQ4`L0rXPTBx%w&#t>*%e?pFIYbyI>$exoi z{9Qtn=2h;TmaR}jfSPz-Fv8_Ij}ge>2@6iio>&=&$L{Cl<*kj16wKXn715*VdB&^x z?K7L8f-a>na>n(94szP{gzo?SL&<_B1#=LydEUn-W=s(CrdT7J2dazB6TZZLE--&K zKwg)!0|t6L{J^-3`G6El(3is1uPdEVJY!_jbfoEZYcAd)Yfw7Jh~)CXH30k- zcWW(vLLPAagd7WcuQvRI)PX2zMq+=kFrH}_h{aEV36Xdx=(G7?2!$2X!4-kI(fTBm zco;)Ef%-;zK^kZSdRKs19bz+y5zg=v($|;^%$ldqsqH({lMwBo79e~cI&R#1cq~pl z{S)#QXwt;Mj@V_yi2!vx(Vo-;@?Co0f{u5>#6W^32WU>04fe`mJBkGG=-zX>wIA7s zM*R@mv@(-lJT1LG3oWhsetNBgxP}3Fo~J`H;;-okL@Y*kh<_u{QMF=(7i-IH=f7HR z)KB#eRuiurzXBuJGu?`}nZNXZ6d^1DP#I4OaSNkJ-{h^>8>*m|+nBSmA5rhH z_~df5KizzM137;jgkqTeofbEbplR)!N0m>Oo6y2@d8)y@Vg^1JkNp(sS*r1sxLX)y zlX@;=hiN+OP`4qJ(ew3Z_2-m0vxZ5A#q)Xxhy+hHkSEj08>RqCLLL6Ke9`96&B_98(xi_aKGRzc&T#|^J$;inMft2L5l{@g%H!7 z4A&vX4AogrlE_)%jHj*AZ?ILLi+2pFL1C^pschL$|F?k+en_OAasyZ@Oa1Z$$bX2cSweS{9ME1{~5J7euo=`js z9ws^@je|xP0{!aDkxkvNv&5DRpo4>Y81dq4z$hs4@)M#yUHt{Nbr0OjDCvjcB#L9` zyq}oSo(XZlm+d2w6>o&8)12)|5BfL~uY-$!?38a|xvrU;IABnFL0D34JM;5woZIOR zOuay%*IV?ZxrI=@#q5sGgmLpO&5C%Rk05b!ZDk(MeGjYmexb`KB?+jp*sgmL@9<kha`e=Qo{xWCb&9hX@}yTjjbW#zR*Wb&R{t)V3Q z_?VyMV`quTH*pL2!ej@BOw$*eUSAWTr$u-~+*#?)XzRH;gW4d;4s|bd42q8ff%$o9 zog*9y^b>b4;%d=3yv&HgsqHEHk^h9mRvy=g_Z_PiHPVmY1m{D4S5-n57!oTi$ak`> zt4)&=-I#N*NmM=aDO|E!eHlWr`0-hk+}DmVQ4`%)pVe=k*;q!pdkO|UcT69@)zXd; zj1-;pq}E8gQ+q8-{WWqk|9;^T9K%6za$mR)gl|VT=^~jPDb<9-*Ksa!upXE4i${o> zna$#=P;4R>0Y>!`GHx3WuS8KSP?f? z{%zl<;1j3r1)Ne|1}879yupGJ4gG~hr zqq7d%6~*XgKD+M0#U~{vyw{)g=1RJ=biM9asQNsnsFk^O>$`YP2T4|5E>2jcv9I@1 zf%VtRUfPO&j@Zg)zp?)nTrj=OPG%}Y6LaEC#)*Tirrt?1T+)=C!%iXM+@W%k53CtF z+7AL^w-4J)^noCqjk}c^{dlB$VJ;}|6R}skcgX9a$)H1k%6MdmUN{;=s-`<1vt@5B z!|7|EWfIk}(W!H^w4s&tF``UoEl*R@b6Hh?;hPEU3*RqU_Z8`-<<{cK#w+C>@t$%> z9PV^|)?_p-?zfg{`Dr$EZ(YRn(=G+2pl^T znm;_EH#mM@oVr^0dYRjMk?8zeWE0t7M;*dU)&pv$3kE51jX9WxL=;UA&^nqFbM%Rf zo%8aTp4PY!AbFN2Bh`P>lSlo8|8-O3*+X&`WE2mXBK>8c!xAqF5))44fk%QSQsM&n zYha5L@epr?-O>wD$5lo>@42v#VR**P#&~vv;ZX*bY&PKmy<5&ea)b|Wn?>_snv>RW zqwX6_YKo@jmP;%5PY6+l5*+LPgs2$Y2J7D_@TeG^M_PdsLj9^h(n~u$@D^y+_3H&| ziTlzz1pEw9i#u)|x@vS5=o0}O$gmyJm6K|5`-*C+T(5_q#NztijN zc6^%nB1C<-xoV~kx!46_fU5n#o30Q1SDXiws3K;V^Ie1NL?x{T+1(RB3lNn~{XsPm zU{#^HL#+@u(H$Coy;KTR1e=djCNJR5cMRT9msd@gvBu1F2J)M=wJaGY1T|&%*zUQH zHZz3==pySo#S8Sw@XMK$Nc<@z%SYLn|<2;Pu#FWU3(qlDK4NV~9T? zU~Y*QcfnVsF8p@fXG()OGxYNk^;#XOh9fNe|@z{ zng#FU9z6JdVMizu^#su#bOw8t28aOIbEL^858x@#Y)b_5ob+Qj$a3BwNdqm{dSQt$ z{8_;Hp**SzhA#C3Kh9nf$fpn3{|VuQVYrvlyP!lWSml+}nMvqxZ{aWA0~AO1tal-X zL;;<6tG%^JkAE@~0NVjQzrjbUL$$@=L#{1tgL%{m`i*Wuj{{2LRhLHS;ZF!y*J3Sa z=KlN%Q2zD-MCI0tdcrWhO*%ZQO7v-~cb-W5EZa&oJUX8|_N_tDvxtx5wG|*q<8;Qe zB|cuuWahK1ai?*&qPqTa`n;c-(QX)V(Zf`yO^0pSVfJBmK_nwWsj8}KV1YrxfzyGw z#ANfO>s=wUc;}t99xs^^a~Y4&eWc&iMN#*Xl8R)H;iI%{kr@WHQ8yDG)=59-&7(EE zy(~tj)e-z0D~?nX6(rt!J_GLbwJdgN^*f~rF`A5-I%pr*inN}6(LnW{?yF??2aYE> z2ICr8OOBmRvi9p`>LwPuQ^mH|vUW~Vxv8EF+F;KUuA45XsS&T*of(xSY2O0(Y|(Ei z?Bhce18iR7C!`fRbj17ry#E0B-?D6P#N|Xu!^rGGxx%v@k|tSU3=(Q}m9pBGGuZ^K zU9E7tZsSVRb%!oaGLv9@@Q+I+y^CN*?5{=g^ms0WTKfQ-` z$64_(C3bwQ!wX*!1H>QxT_qfTor!}+RaI&vJnTmEv(c_+Pp&$i)}fh7j9zUw(Dz1W z#FJl*|G5tbm{AVChNQV>Q}Jde&e>+)xdKxj{%_aY9bel5AJ34Jx?~XT%=h1YGHB3u zPjC<-R--yeMoKlWga(5J3av~mrOz#`iIovH_&B!Z5nX1WUkK!yt4-u*tK3*=%`{D5hNOAe`4s;{%WAUWKxQ!M?KPStL zlK$od4A{PEUv!%3*8cjvbuBHS^{oxN!U%Hh>Ec3N`QwrDea8~{?d~mT{51s@ZA(Y^ z?JdWN=Y-O>g54NO#2Gvi4X_dw<4$4)BVYEp^wjNG+~8T+sej%5eBj}DF6T*b`br{4xwuJes9V;r; zj0m?KkR)DG1AYv><`J)lwHcM|fKFEpTFcwsT8qRjbM!vm^^ALio$iu(U$-g_ms1?s zE1yuPX9kIO6&=O&lZACNmkPi(VZnOrS~8%W_ps2x-Fq1Mv~+By0eN-1NG)neGQEWX zuBnw3>x2Mv~TMQKT&@sVqC3bc8>RSl%PLx`797VLtQW9>4Ub&t*FCpX3zFO^>K^Q{bY)h z<3DnaL}Vq11BxXTfU)y=nwOsZeUx0d5Oi=lm^zNbno*_SlhC4E#(1Cio^&n@4yJ4o zMBtPCzQ=gh(JPb_$|oV(uI8&U+2|5O7^ez3AA~vog<}j0;vD`G=)o)NiqNhauDyW? z?stNrWvf9s>iv`l7WZX%RL%5U5fx3!vH1=a2JBXKCn11qvREv-mzzkAyo(ze7t8(V zskWc=r7vH<+5BtW;PWW~uU(vCU3qt_#RkgjiqQkP4s+cT_#+;l*72DQ@;Q2Dbu^EZ zF;+0lr3|3+j4;K=d|F|fW*98^5JQkO6IGfXzj5LE##n!}-sF`?9GMD@lWjJIiUU~j5BdC-L=1>-RVK9fH z1FxAsc^cwu6OM6+_%;r;8F-)Wg=#zD90MlB)nqF+^qDEM{7f+NBnybdyxJ7Wyq57+KOn*QdX#Ku zR*`c|f`*p7Nx4VoZcXtnjL3u}skyL$y_C!J$2@vQRy)0Hz~q;iIUx2R`;i|Vs7XM6 zzkjE=*-1|BTI23}Cz&cX6&u5+)bE4@<7dK8^sHWg`3rUtpUuEPOO>Bbeu;wjl`gHB zswlFbkK(q0pDYoa9L2TI(G#e3enK*gcH3!wLcV3vlb8+| zI34&OK7FwD^OU4u;hyinJlu>ZzO5*deoTtN#}W+ST_L;1$nza!j(`H+{(uvTd{gK6 zb^&ac5AJ~;A;$2LICxb(NXy51*qC4i2@H($aaRW3NVR%sYl`#6$?8p~a7p8RhrI-ka?wXV428hLTNol7)qS4$?HfCk!XLFYsMpOE)p9|q2s zN04G6M#9jk^>ScWpkI0z*b4fU#|*E>lLD-c5rKdH`oH_j!0-Fz3$^fBfE|Bob-K6B zlBaIKwmP!w%%p%qD0-R((6NPfpj2okM}wv#$UFW&AVLGA2FbO>ZD!?ix%25rYWE2U^ zgKf%|m~aGc%|dPd<$|@T$okYDfP1lhWa;la%Xn!frf0G6_A>@jFP^KHKr@syPx0$#ikpw2Q4K5yTAWRF~? zzBK3KSG41pDp|G4DP4M(>GSexSmAU`QF2D&b>4}PHH}qQgO4*i;N{9v>3E=2#E_D& z^9A#mq4d?e%dLR0w;#RVhPz_OY;X7+J{Mc?q*l+Fe@JvL<>p9%4WZ2%;5C2Qo|i%6 zI~x6C_fSB>cFaTIyiX@K*>^aalm}1RHox)GOU$7+*gddprF?9)V0rhD9bP(|>4o+| zF(7D13N1Bzsivf>tvU`0rN3$~zw_P@ks5WmzhVCFn!cy{{Z2=icl-fjb{`OLTH&d^ zufaa5_kCI1y`-#Qz)`gxa32I)2|ilB>bUj@;vH%mf*L}wK4LL$lh$(ho~7BNi3N2Y ze=%)fOlEs5{cGBTkCl%d!2u6gNBKq@5We%aH?F5%t@A?7{$_~%6r2|SUnra#2?pXy zMlgjbbTK%o21_VTE1@>n_}q`(<-%bOs>bI#%th)6vvQF5>r3T>XC5Aoxu z#Fj~_Nqh5q-=%&~!k;H8=P!=@YIFZJxApblRVif##0|VAwg%IaT9}TLM{k87?z>o> zPaIZ+Ylkl@F9jHM8wKcr9QM31B0 z7yE~|B_vrQ$=aCzKib|atf_|G76n03Ku|geL*^iEU+1O%ivAu1wGnp7cVDhMc5 zYLu2wigY5qC(=7em)=2oLJg3_GhP3`*V=pSXYc1+oO2N_m@}j>=Uc`Y?|2y$*4rq| z6XwmEw7q8>G!qusL-bLLw<`5)htC+My0F&IBA-dUni5P@(EG97!ST-p7Zj!1MgP>+ zIv}b+0~g*jnNgp^Z#dyBZza}HeT5&kk!17f6ElGZchpAVmx%nzN3G9HxdLMamP_EQ ze<(^LO+f~MhxC_0a6#VvOf3WiLtW1z=?o_An&hpDlGp7S;=Xmg9>2r+)gtWc>n9C% z$R`fMN>MR~mC*O3%@)TI)aB<{I>dgj%Jg(nnwZ1joWsxD#U45cD^VPaY__7W%d08( zFXFEl=C3!tUUPqwJsDN|>O#Lk{_g$34VSlt##AZyo^QOi%oXX<(l>tz?2uNf4`OaL zC}tAeo6&xGMAf>Xt@EK#Rz-DH(FL0JwNtzxSzy~9`=v$d{k_l*stX0j4ppT|c>f$) z|4>CQy{7x#=~=}UM@B9)Wq$6qxdn(HuO0V@MuZTF@UJO6V%zE#9g@8t-Q28u%$A8w z9*lw+tKYtLp18A&I<+(?Ixu8!2BPwaqMry=hZS(0Y`)l}Rsejy zTWsyK(tG=13Vg+kSTAmM?@?}B-juY`Q74n-Ivt&>MrqaoBBKdbM-%DTW*#ny@Hv(W zC`juw;Kon@XiOZiJh6eff^q-8;c@?G^%o!K4ce|?+P>N{8>CnUOQqIF)FQ7-(rBwu zv7%=B!&%nM+WF<2Y=QLR!S?Pk{hkuRcF5%^XWKi3xTUMl7Th@}9IMQF3f3o+TC2D) zZ}}>jgud6@?)=*F=IdL^w7B?tEE>P#rdkMHhE4O|;iIDLn@8(yIX{h)1UH9H6is6ApXmqjNcnRXVQwCGG z`cJrOo}Rmp9m^*dY1@5iWmYN8(03oH6>bb5&NB#ITPmZk7fb!htx~9zB%&cJkS@y@ zDrRD>JL_g;y{ni5LMv)KPE4D47bx!2hA-f>lpMVDxZ-_dX{>p9IAZy3vusaq=N)J` zrRlkPK>}}T&v(m4({PU?59Fv@=1ltUk)%pjn|12r&3eC$!vQWLY*5A871!OeSfo^e z48VlaxvV{`V4CU(bcYW$KR4Y2OWDj0Q3toX9vt6QvJ=cpSDuwkXEDD|b}*)$PXvUq z4g<>a`X)n3Q>$lgQsdT+3DQ2X5h%_ZgAn}+{HtSjUY-#Z4VQws^*ThxX%y~PZ{+2! z^0We#>xxVtJX%(+$E?H+hIj5cnVSk~+%l7S!Y5?%SU=l3@y(KN_V&*Hj9UP-%^;Es z8?1H>B$#Hu_*U1c{T3V@5^>GMs{5vMOrr%yvc*W#wRA0LPg;zsMs<)I= z&$P$bl&;YvOV;OG?6lFkt9Y!~AX$#++%P0AlWt&L`F*tFLMd+TgDhtSp`lPr^UTbj z371csV^~>7pf$z&tC7BI0cxGPKx*b zUctwYiecs9|rT+X?KLX$5no< zoTzxX%jSm-PY9ifzu`w!9Xaj8Qj>M9%zH+#B|~83 zpmVW0!hC#WgB|6V(C6bWbQ9=^x3eij9QV#d*SLy&3u54-49WWP6yHr-v}W|)-ghYH ztPWn#Ragsu(vKfb95CREuO#6 z{e%{xW<*kmJY|m1@3YBxDaG-`jq>H^I>g}c`^j*b%GFjAy;|6pyhC7YXJe~(j0}ap z8kR8~KA6#&GaZ#Jv_@}<-523VzLq?Mc56H6e)o`cSac?XT5yh6c^*G!q5GD%LjSRPsDd|Tp@X)jdgjL^k@*N8p{aRDaqhfrT)($Hd-l!g@&!3CvJw@RE0J_GA&2w zY%HNZB2bg83n-_*Aw5xjWS1NE-i{-%I-)GWx_j2$U4$Fif<56qU_85c4{V7ew*N;Qx%-94tmI3@wcxC8V`r%>&F>J;UT+8F-`76d z)tRHSk=@vOS<18=>66ntTd=D!jrGFw%3$#C&E_iBVqa7ZyT%dah3c_1rk}rx#z0p- zU4EB5^#s?Ark75()KHY)X3al~IF%(%y5#iu9Htj`_biNVlC@Ak^CqDY>{6`TAq15= zaDZTQ4rDU+Vo%TKf&i};K#YMFF9K`JnB%`aA{~11n8fPN0nG1hf=pl*3Dyn$&4waV zLiQN{e>n~KfBUKq`G{eqoTGf)=KNT00nFfypU=Yjt&GJ*b^iF;D@?t;+RuaZ;2ktZ zx$1X%^M@j$OdrJSbV&rh@O!D(AAL!CW{MbV1y=Qd8h8y7iq^j6(2yf!_W;aoP<{g9 zbx9|IfC=nxSxKo27YT+X5k@X z?a)0@U{R|D>nJ;h(1AG0P)-o#2{wznAIIYbLft-DSf$N6o9mTLPIxBN)&pr?6)%nz zxJSC3uVs9*Ojz{NmesHld83Lv{I$z9aO6w~ylJ^H8I!-XZOAZzX(ww0%a`X<$ND(X z@F<(uW>@_@QjJkh##j}PqpY1Ejfm)U!EF(|@(+0g(-4B!-*BDug@j>nF-H8Yj3p|6P<;eMz(wRgq2RbO<#uk2R#;vb6b z75h_^eT&QH7dNPcRacXq1Wp-?RiE74h>cc}ZptOIt#!ECbqtaB@8Z}<%$ht#)?<)$ zW`+(bzG;rmb=a-ozayNp-z5J~Fof4KDhA~Oq&L#r<&1>;o$K>vNuF$JXL+R0#N)#{ z_Qg3O*acl9^Y}}*{Gq5DJy1l!Fj#{|mlqc=NB}Y=LyQH!0#Qq(a=GRO$8`lY-!rXh zQe*YZv?b*1f^Jm1`B?gc{C3rqIr2;+ncUoidOufj|B*B#93j}s2kdARwhga0$ODT* zyglmRpaOgwdgAwiD1aTnyLWNZ>jvf z*zQVwH*qu7VcBFNVZJb4D@wZr^+3SqEm! zU-@y*FMFenh5LT54yNU68hZLV>-7Jj2vmt$;z!4KUoBSX(z8*h8f~=FVlU7JHC6`O zmYJ4QSMdHKEywK0%cxq_e!Gi7y>8J1uTIUAOiAt^^4paSL)^!#taqG%j6T*warQz* zciT+eA+EVyJ>F563Ec5t%{t{t+yeSM@iHFL<{mRX#|+q^c!`#y%!?VNEJ|Y`IUQc^ z?rt}#M4WraJw9#p?r09T7IyVh(2QWH2*_;s4Lm5M37!O6{2QonQ*FF-BRGZ0YV=C3a8m{R{L5tn3y5*>}nvW6A52s(u{Fq6^Q;)&f&pe@sw%{!VMP%myJWHrU=qY z5}^m^tCrX}uQx9r1W%NF3R>p8Tp{UNtJq;hL1;O0Af>5M5PX-GI$8sP_jTNpsTIqv#=ZW8O%ls#F6X?NtjNXu=?{g{Lk{Ze5frB` z^X4Bg;{NqNJpBuoAmI(l|7-*_6b9z5r^xn>L+pl^!vtcKzwlGYc6y1E-d?{((MA1< zQ9a>txXxb*axsWdxzD3XQ31K+&%3{ok3Uk1A?Zsg?}2mn*O4!cDFuL&f#!fwbU0kY zzS!2OCy2EWc_;p#<$TKV*JV3K>Ln}o(^;>-cgh_?H98h@B3rtnZgtPWhBebO`umhs zxdLMFVOYip741Ouy{BSq+euMxj4XpGhUD5zXSe98Mh$CYD7YUdg(4Sh=`M3IA>}2| zK}_{-U5+JFW#+F=6Ue}&o<9_P)sdiZla+=XQ?#*#08`*u@-?FSp4vLOb4atuXCY@2 za7y)j=?)LwCq_>Z(bCtd+-_B^ajtO|8MGiu%_jmG9J&JcW+sm+w=1`Ej!U)SJPR!8 zZVAnGO1;&)1x3N_*P9QKE>A+L)tqeUQT{Q~`dbsL938w`fSK9Ka}9sB-kzQKoyf%@)Ga=QS*wIZ+LSFX{sJ$ zJGq%_^JsvMs6YVbzpT~afM9P`w!lwjPOn67zMJt%&q=yB^@(9NV`R$REB9LJDrcgJ zXVRLXSYr01E#T`8sJ4UkE@AK;VKNn;zuu z@UEzM+y$cZ%rwJrwt^2__(W2kwV6zqAN2wWBRF71ojRT7}PU#8)0W z?fVUNZj~HdrUmtkHW$D$@_X$ttDR|C$^2C4N+4M{70+!Jq{V3>K>nTrI@iP$-)ynNU1Lix5)P=!PjOvr`w@WRa zyyT-YHEZr`b}6zp*nPkS4N-wC%$0*x8FWDp^62blZ!$<5mPSBs*$dlWD%JeCDkB!# zVCa=b>-VZb(=MuQZj;qsdGa{HuW~!E(bdmw9Pphk07VfPUTwdexvqHw#>)P1E{$(G z-oN}TZ*pfg`;xbWme02{^0C;Y6fH?U&etX;{V-6i?qKxOTh&3wmCjh4(b3VQ5h&hil4|fG# zy*hwgay*4Ay+EtRTU%;zR44t+>ii55DU%j@)+Q|#tG-dYwuu%)O1L;ZOBrtgb}0!lL#rLOxR)RE?gh&D$zPqec(7=h6{Q(3 zXO6V^ad+l^6a)C>vxfDR5)|8WXon&roUHUL<$aUuO~0Y7GMmf;;j(QV0vxr*^)OI_OVDq(Zw7iLBt28JDmooHju_gS%D zMdfbTGR@c&lExg6p>3v3X3+r*LoowdC%<3BTO>c%R*4TB*M9ets0pv1`3P)}LCx3EqAQkiCzkX8)GC5(un))v$1H(t zu&rka=V{g|_E+%lCcK|fx*Ed1N>F5mB+CGal|jn_PpD^#4n6Q zIln0A?_{Dh2te{03?Ez z4uCuh=;2KW(cfBV+U=}U~Kx3D1Vj?xd_0js9q7IUxVMYBpr-&ajXKtw^GwW)`$gX?zO;wj&t2WSCQ}BMFfv6 z(HQ?;iT24gJvC~1tEAm)uEoe*IRj1=%8WHt%KWri)&b+&J-1SxoAPh77NK5v&#@z< z+KSt;moZ%J-dJj<_tsNQZnRbZ{MZ)_FTCJ(SJnUJ2~hXwhI@c@`U%FZ5S=$4GeqM9z|6B^q*%VZn1wY6t!#-vXRb}wWGJ}|#aI(zw>0=jJnPIvr-(n8-Xo69q3 z&8*}R0T9Q6{)`9SVJ_oWLi_V^d>uVS1I}UE-l}>J*uMh5_-_OH?|{hxmOw2?G=n7r zJA}qqsJMUpS91J`D^NG zA-^ILSw;t2l2$9rQc9xj1KhRmk(E?YYa`AHOW`f)%1vD9;U9=MUb_+b799~2+5MN? zMX%7^Z{1JoY=6_#)Mb`)$xB4FBg5VsXUb1l?b}yIa%73~S=K=Yn9%(SH?Vx`5kJm8B|KA=L;l_WRXzHe(*8U{#N3`4h8FnM zRRF9z>%xcsHNW zy7uoS-0kNF5J7MqQYcxH(9$sp4u#5`95`-QDxbLBuoTBUjhc3d)gb}5m$tAHKZct7 zncRdsR!-38?Ia1^pFishX-}_W7iM9g#&T`{K6vA!$&~?6JX6`+a@-oo`m1$-ir3C? zqH@m?KqNcD2&{i7#jD+xC;7U{I3A1_g+;?buI%8|f%TiCzOcgV8!`_YR zCrM`1OA*T*giz#j+~{phCVlNJG}Vt^)8YcGD+-ltUrt3}(U1b`7HR4-BGIPi7B~1yj2NkYQ@HPYAmUrfV?`m(d-Cn{}dlJa#REt!Arq2JTP1tSf!p(?pO$lFYK381;`vA9I_VM)s0-*zzcaSkxi_agi-}rZVN{-d z``>@4y(i`HcQga#Z3!=mAT3Q86!ceIVkNpmI;D(9-#<=cDp!-$XJrb zbx>fViP36fJZ^vzV5@%11tTlKMvx~A$@_O$`rnb`f7@?;m$&dgk6k;nR~rcS14RRd z*9`t0H3-;$MGf|CwZk!lzHbH_p|ZeV>qo{doPJWS)mQwTXmMEzizlMGV>kL~KRxjt z=%4+CyjC$_K=)+?Xny9r;M9iL8xL}w8U`A6u?|6e_i^OtMQ99Jyu!MW@uG0Yx2bCF zL-zK8v*il^1MzDFXJNU?V6d%%-cRODe>8pt%>~fzilcl{L8shqAAw?`FQF;t-r}~c zw%~Z>#f6N!`Zq#+A9*tPzOAKIazJ~TY+wBSX2j;nzGN`a+GAOPir=b#!;3}}{cz#I z#GpW2chCG<7(Xs1DNilIi#r)%zdUPw@>+*Go+Cc{tx4&Rlk(2KdtF{I<2iKtGn{|z z)2y)YvdEPHTmAmNIr4epfFfbwt9+A0)Nz&VrIFk+Yxb}+1MkEyVl8>D?=v1!$MwIi z>3{7!f3gJRFFA0yZ@ua^Y_2MPSzoy6zWHs<6d!aj7YU`4LC0Fzq?t|jr8obc6^vk+ zMBkz6K~aqhO9ehGB(i+n(Tz(#sS9&?7v^%_DYr-24;25Dtb23fntu~D2vke4;eH*x zwYUlZ7w_0wva0du%OF4>70~a9te3rEk3LfuLYstS9FPdaYTeTf{~!|PYmQCVvjWQ} zmNO4L`Q@+5yuQ)?#*89j+-qe`-#e#EW_asBt(Elt#lDK*V#s3C@l0Tm0 zRyHEcWjb)*ZeBSZvm!OJm&X*pQP}kvn^S%j&?~L($u!_Dta~l0FzbRD?>{k>2cGfq zbpa5O+cg+V5y^wrH*>LSDnw-*AK=gf_8%^rM)JrDaXFM;KK$vQ3UKdHyH|oJ(ad)i z>Dg>gxNFyjBuD2~fa=~;`tmR@`o2I7ULBV=Ju?wDhjYuXGdKu7OV9{4!3xkZt)yRB zL{GJ0hOxQxY^hRl(IsX;kYZ{5Q_X~}FS}UV%bE{M<)-$KV3fw1`0nYj-AMb(Un;4W zMe7CNL&*t+qbFKrlbIggHxAZtRMd? z4yyc5ad5BgZyZ!U=@`TO-mldU`2o8EZoV9EV?#*b!oH0Kgr<4~Wal9cUQ6Kly!r4P z*RQsGUitNE{IaOHusjfXHoQ|WsgfF(>sCAR(5E;#nOC@hkr3}KRgw4@W16a)5`_&1LD_Yw10uBLW&Orrm=y11 zL`5BX6o7r)mq-h8IpFJEVN^K%$^CT=Vnvmxvb1^kHR=Ik7?84C5d0C6fnftk7ibB# zEZ}F)0INC}J9#=lVe~9T4YlkuSLzp`CgX@;#WTT03C0V5DA=cZ@-*F6Mu=9E%GNcp zE2f4H(9ajU`pr3h2aI8KO|c0&urEo8WdJ zxW2t*-kqo<1?&}oi*YReU;H&HM$M3;(po|efS1~w*G9DjK?i<{dEWwm73=G@@?=0h zg+EdYf$^Qm(UeR}o2T%D;Nr+5V56W(W(5{{Mt~Cya48QzPzfbn#fHKNuMZ()!^f1! zEEaGe2flCyC#sWjRK#%fo5PL>4xCxJ3)%^bi-q}{FUsY3=1n_`-MHtf*zifW_nU8?diNZ)eajDu_mdrsi2k0@Kwgfb zTzR@%>`2t=!2`rd2qA8<%5!Fe-NVA-$38GU-TA$R#ZHW`v^kHbi=TEADjX`Wc)QJ} zd=4~R>BNZR7~dAb?Q&+-(5j8!kv8Qql68Zn$^NA%DcRp0dF?x2rV^d=l_92IJaXV{ zYziOLULqUn1N?N+b5%c`{xR0397smhQe$-VsLfUIR0c=t?>Q>ZY?(3-D=W z(-;O;{y!9U-)2za*`tZJvgsl=u`Nfu>=OmfT+=lZ&p-GHtp#^D{Sf3Z6MPq99RF~C z?-8a%;_9|gPrM9iO?*ss%8GDuc54Q%8QaE=p&IcKGrDw7X~g&5(|C<`fjeI`_axK% zco*J9jp?u|x_WJhI4IJWX3UzTWdoSe3Tz+@W>)gXd|Pt9&sNn{?T(dyiUc#ky`AYK zIVDY~y!&a5wI2+$(Wwkvl%qa(u`pLmF_4%2%6vf}Z2RKCq;Y;l{Trx8=E1npTvKE? z>gWxj&pn`H%k$p1o-2Z}3SEK*@N8HPGhxvw(Is)oPOKP$*(Fmpp2?AifmwDOLj<@< zFf_Zp%GXfed;9)SpViZ{J8!Ok0!=;bjq6A6x>4LP`L?_hQvzA!;^2arw{>U)%2bY< zq0j)frxhaze>h4OBup9MV@h-ymp=znOj!G~e^`Rt8y|@e9E*#HC{uHnY9Ekl?YU}P z(o^<3wh(w~DNj&u(;7Bn4F>@&GuC6RX`I$sBh2&!Vq!)!^|$7eLt>!d!oPrGum_4> z1Ze!t+mj&^7!e#PCf`lbwD!HC`bP1nLCy7Xagp=k9qJQ{PqQ1wSEiPIpW+Ec7{oB0 zVbck80NcASNJ?rr?;rvVRnc35(DN}pP%PGc{g<#nQx4%WFcElnM01XXs6z-r^MTp6Zo)AwF#5}S$Lv>J4i|Qp zbz`7wz@X=lYf1P3JL56pbkfd6_9ZgxH$iC;yl`b>&;bVseN}YJesY{cO26f2_cBJc zJmTn%q9z?!p!s~uJli23`})6$@%#J|O)4pj-!3~#LeBdnQT@(%oX~Vt1Q|o1ymu9> zu9u78%%HFj1^&rlhOB?No80xejzq{(3E$z?ibg2Fc?h0GS_``nxE^jiyi(CM{!sLg zN9VSHBhL)mGw)yw!CpQ5sG>R#`H0~H z1#PV5vyB<)`5G!c=al$#47HWjwYd_0vKvl6rg29a9#Z-s?=qhg0vOYa z&tY3tKtTxb=f``y{R$z--4g`qIPR9hncIXjnHJF+N zFuJXODE`%7_}c6IMCKofA#4MhKv@Pe&=JOIC7;ai8(Q6NdGGcwk+|O;tqwoqX5Xr? zAI-X=&bg(MGkt;6tAC?^0(f`+R>*3ES*$PWkCuw@%u-aVeKa&18|jSOSd zkgu#GqoZF=f@nwYjPh9}R3zt}8o&tQOYRYnvSo~91)c{)xpu=UBMVjGQ-F@0)0X&L z?@5xuX=5MLyPu-3l&CCk=k<4to3}4|ai>nTyJs*tn{9F`lcppdJSF%0o%*$+C!O~$au_KE$3h3oYDCGN0J1!p6p*G3=x zX$hhNv?Vs5z3%H0+hNu&pqFeSGd;VCu-4#x`I5j{qOVkU|hY_^4B$%3AkYBEx z6jxIkesDPJX1Mp$6Lt6D@E6;TfeuPre}^dDY5j35Y<$@Z>d0HsuMF`3(L*&95Yk`~ zB&z^DF!eH@fn<3Y;DrD76`1d8)Bo-(^d-A(f##Cx#K!adgAbHZ$j7Yb%A_fcdX9<+x_Q%c6YHlyYhhuu z_`DTNgRyyzMR~ISdCW7G0vTe~=Wh{ri$lsDxJ6x}s7{D;WI>|9CnNuaQ@&7C0WYdh z!qfu5O*}_H&V$6e{9^G=JOzgg0Q%Ujt&LS`5#Kj9>PUTkHb6QA(Wy#O{<> zawsd}<01_)QV6_lI}y1kBzhLB37=UD9G~WvQ!V^4SD}W&p%*T%g<`n<-E5>gl(vS{ z3kfOOrZcZwG`lMV^fjizHV83wr#8)`05XN{(duRIQ)Ox-7uOV6ICMzi?AOMM7 zK)}JPB(rY`MZArM5?1UQV(t zvosiT3JiR>w1ee^J6oxOHMJB3>O5*+(w!Gn5#zYmD8Z4vz4!2oNxrO;Hug(D2NEmO`5q}&sBkw6tu>vTlTi67SVH>+41hyAnxyE8!;R9?S zOkH`JwI8xgHC8v!9cu!b=8La`E{)b+c8i5TB|)o=twU$L_=BiQcqh{~cYJE5$1_D^EWq4)`ea* zvZ0W@`_KynV^9RhhremDlo6zEG;)ibD0>LGDx*kXH$%SLKj_3Dk}(;N;|G&>UQYu>FU^{v3$88`@kzej-we zB0Y4ed9Df*%0V>%Y#uZyRYH+J%L*e66R7$D5RK#H6~eoR5m~c((Ef!MRl~2I&G$&# zdHI_1SjI50oHNMrdX7)LBa{B>LE`iJKNMD2KJ@u_tuJMh=ta4+PbJ37_8k&C@YRc2 zu^-(-V6>~X!!hti7Z`G@Y6z%^3+5c3mJ14ZPq*Ar2BlDOw!C=qo3p~@YL`LOV*=nO z-hI$7scmo~RN^FIUWO=K)g^_VS=hXjnN`4vg#_&i%ywHcLuYOcE2wI3W~7-0!9J`N z7VE2N%b#Q-%A0=Efd!Dtax2Hs2NEz0mfN)5Ztuqhw1*vJD9S^Myz)q}iTBj^)dm z#qKxFz)#UVy$1!y25F#FR`h?3zsZ5%kPAc>Xu5yReG8oT*pRRT?_SxUj&Rg}K*gR* zhneuyvvb<@O9B6fuNfb&O5ocmFb;^u-7^x#^6BEgLnz~z1c#s|m`w_DLO z!w0HUknE~P2C?zFVDrVVzslIr?U98{XeMpOWY(?gABNQ?io>8OuE@d`&Q?E` zs^3^C4a{0ONACaXe-eAk8ghr0vVUzu?fGk}5agty*X{YBZ2PzFNyWy$&j9m7RlXw) zQu-D`)&+^+!)q)9xM%{xgk8?F=i}}zzSr%Z-CFPV<*7-dYog_5xdI;!75w;P91KT` zNS!-%)l*$>NVC=f$3F}vz;dFFoK*sUK$yw`CY&@kv|Uw{7kU!%ZHhELT0~9x(bJ2s zM)~#7zl_h9D~fA;f)Z;nNqVGzeJP-T;D%@01t5Ty8U1>E29oJv{;xWR{9SG2-*t}8UoSl;%8yGy zf~{P;AwGTV%?Q4PmeZLdl&5>Hb3U!ks}d_IzEr#5BGwQ5S34om6bOHE6NClpf{sL0 zAY%>;KBPo{3(xj9HV;zo@SJq|Ubqp^>~wT$!q(g2msszOSG;=ud{)9I&U0@OiGM8~ zHU18Iu>s44a3-)HEHwHt3Dw)P(OGiP1FcfOI;ut7*<`^b8(Qt$4kizGfAms z*4;^5vc9UHR`pt&IKj%lM00BpLIa{)jzo|OMQ!tegQc5f*|Tw?dQt2Nc>TSD@F08R zC>N1sag-2XX$0y3nN(jiZ=L)2$=|w--74@`mxD^Hx+9-!Cna|XOst(7lKf)AuI-sg zVq*O0oY&7DX%$V8t6LkgE^6UptS6EO;mtrr^>#I$xfx)pl%RskvZc6Ig-BaHnSNr; zzxPBx_ww5xp^yDJmJijEPO)@2ImSCbIyhro_c!7J-!iD|qMB6eykvayvzv`GQ)63u zFoL_pq&hh)S{{7wa-gS=UXUPBy#ZN)#Yqro-jOjc=Vm~J2bd2h$|JXLfCai{D#!z; z0%QTuW#x^86EF|MQIX(%STF>GVW3GF@_w#*pv@j%mFj?JvKE7BG^UH?TD)2cmbXy9 zLjB<}{ii_QiwB#wW?l`+B zIdWl$@*c2-xlY(sC%7-@?y_y}#ko7yJWM3+cO#Y`Dwp%BhH1|;KT(W%rX|{<%WQ3b zTs~AOM&x}h+%!t|8}ibM z?$?E7J}2dHo^}uBrXTjQUP_UYeMxf@xkt|%#sM=T83EJ%;$ZSR#XzLXCKIC+xtO}? zFesjU;#9asgn{ag z)|$nf^R)31+Jcz@zRIP9sB(dpXLh38&N?}_`>H1WTx`iXC?10Rl6edC^1#xz$dY&| zu&Dl3#Sh74)0e{U$F?!weTiQG+;WBzRg9=sc%%HTmy1K%*@v4`IO!fQ z`c#Eh(eqEZodR`p_@|sIb2Y~Cs^cvw)(P&%|9} zd5&{=Ycw?1mp-{9NOgPE7DF9A#58iv1VGn$YW000y9&-8$y5Zk5v2}cC2N;)9UZ)) zxbR4=CRijp%4GyzF88h&RbMbwg3uDo?xs;xR8Z_{fyO1-;T1#;j?A9#iY@R%*KAP7 z$itblH|;i|9ScC!>l{bh(Hq3rI`SsDE@$iTP_W}K_9cwW0I2kl^Ji3QNa4S0M5tC_ zgxevmq*OE`Nn;B*5J1=9j>%e}tbjh|ky}Mq?p`-SyP-z9OOya+m4>QpnM%2TQJMgMhN9b<53zM)XMb%6bY z(OU9RQk}(mm_Uv~UWZ*2q3i4}n0>bMoa$m*={B|Jv_lsvbzMwDuPC2eqk0gp&z?`d zx@()E_i29bwho8PxO9CzW{Pt(U#Wjj?I##aU>J}FBTP-1{|B~$mBuTRcEbNsw@+C! z2P!xBnH#`V!3vyo9G$tM%KN~Kmsk9ky|IFF?yFdWa+>jt*xp!`fbnN1j{nP~`xb(g z2*V?r=2+Di2QD~kUP4~-as!K5c8_Idmcki*C7jmdd#j0Ay+!@g7oNeyiV7e52AONt zOEb1*+y>bEQ%&$PbwSA)r|Ba|Jp&iJD-joUOO*|^G&sK{B}GZHX51_)s7RXGs(|r{ z4DeQPglDKaQtWpWQv{K~=3+dEd=Vcvpl1e7tUO3rZFP(NNs#zgi5&Z6ZSp^X+DCH3 zN8#ftdjG)XBHSct+Q23^qCG(v$Sr^?fh((YE|Td!g52}&367Iq{aoUiy!J! z1!-yS-Q|KN#64lI=8*s`R~6vyxbQY~81_6d2!HK9sQ_`yAm87-`Lv|kfpr$S;2W&H zUCf^%8)yE|?6xJvb*VH5YM*}?GxPp!w;BoCGyUt_^=mvI*nsB$mznSbfMZr_shMky z6yk!&0@kqE_|Q`&!*LKtoPOdKFTs1eeK4#@XFkU%4pZ;0HVrB2!? zFen=Z`LCM-BY>UFjYn74QCBO+Z66HqUloC3rLL|ylqf;~oU^Hr;1vMF8Vc|Y`Rs9}0 zK)wiVflbu6#XTfnAS`)h+6Z%=M}2%%6%yln(tTC!t(#hVN`Fo4xuK4yrVo#EB}kQ3 z2HH6(UIcusgQq`~?NK`IFDp??;Vg7>1j!mP{GytlKn}%zpH7{Tk*| zpO#wgs~f~te4y>tiMZ|0Ft;%N5o`n?mH2ZT_{s|No5l`e*@Nsfl6urw6(b=UVXr3R z$FqgPy}b6}M?>Kv_15`v^|?=90REjj+_b)oHkOno)H|#nyMmXEErS= zBduS;c6_ic5?yg%4~+psHwz$xm>%CNCEY~cXrj6Ygcdiei6MS|BE0;4sX-C9I4gF4 zgGnl3!;8N4S}AQqtheWM#ECGT{Hs6dl8Y`wV#=<3C5e6c4CNAzJmS5JmmDg&Rg*h< zUwLYj>~x^ojj14HwO*T-XjSF2!Lt?5!=jJ5E?0k$)s42&z45i}j%CjCR!*)*pOhS^ z-VI^&0NjyWv~%U1&(-IR^v#wH%-29~T8-9|pa2#pyYD(#xvbPDlY?(=ou)JX7~tAa zQ^o1cMlP9Js%ZRUqnG6pr$o7`6i;t@-3N;-WfEJo;chsn#7|cts`U9!+YWd2a51eB17mgD5#nYu< zd+iLYKY2r)0sBy#FajpR@HEH?w5`1U<1_97I(3!o%9NFSw}e<&3Xy3i&^5>sAD)8v z$$J6SMn0EnG^new2)QhO)E?`}L0Z&f?==MGO!;`^T?KReRW#lv%U@X2)k!4%##CEP zJy|>Xb*jeRoAtIs*rzhbd^%;_A%|>$s~sZB;u9fP;53>3-7~DvifuD(!vOwno$iod z4@;o5yWRLg$)@2pi3%)F%87D@uRF3wq{FnSXRA?VY@Im#XuYsuAGz@5oFI~Olrk21 z3kt5yc{ARD4pDOGK!pzxp`(?B#lo}Jd2Z2d70x^KFFxIwe!AM27O9Y-5oOWsY2+m9 zuC=L|;p_zQc2hcu>P4P!ybE&Pc;BRS$5D1dtV z_zy)i1kYRw>@PwC8S^@7fR+*!Fb8V@#xO9m-v&UJ!GAwFe8g@Vx-kCNIxjOH4Hf|` zP|#xV!4`~2h@EZOklcabHe)Hl>(~V3FmSXG&|vYVuLK#cwfpRld0X;Hax-zk)PXTgW_~ z^Uzaa5Rf8Ms{bty^g-rg-~(w(EsJ;IfBVGHMZxEw9<3E|1Tl>Dng!7)#)%dUQku7( zoyH(sH>gOU)Q4xdaYTqSs=)Qf)QEW(p8cxnqVV@ZGg_7WVu)8}^d(jenJ-(c```Z3 z9V`G1iujrU&4qj7xG_|tKB?(am)ySA$+O`LYb4|Ews?l5j3Mhe1G8Nl)MbVZS7J6g9hUOjsg6xHW|MI|K}tB zmqY*OC;fkTtuAHYCDq0iWclG- zd7)MM#oE#M2pj7%qMqYm;fIu5O>rY|oaHQTm8*JN52veJBj@Q~S$XZP=gKe3J~@N( z^S=Np+w*Rs-A=kmCab`V9$Bt>F-VMouD?(#eP{(X>x5v(&YM?|iqvTPq^9OJU8+y* zYEKXo-}L`E>NmBfnx6MGWRU1iT}hh`0lGqk}}iELI1%7D*IaqEioA zaKEyCT0|B0|1kBP;cWJQ-&CtrMX6CUttx7^_Dnj>+C|M&RS88=1mSF}sNGW3uDzx9 zro^aPwf9Kuofr{BuIIe||NDNP=an}(9655Fzw`I~ex{RlP^`_t3&v-sJX}X_CwifB zDkPr*Ng1j4R>b>QrQ}Icm+fv2X(9xfzw$@g8IIiXG@5rTEG*ZuA!Eqf%jzxpnu2q zhxI0fkCZ({o5LQ5u1-MYt=EU+os)n{`zwl;m2P{A=g(2!xJ({yzhZfJBH}Wd&)0J3 zTh@F+LT-O|(;1ViaQ5ou#;Bok&664YE`%&YiUjbR21!gjNPGPX+nW%lcarC~^o+mT z!Bu@%$7!3#Mf=^!V@dIsGvkxrj2C;u-BwK7#xg{W!zqpZ>iy8TO&+3bqY(DYqBBi! zSmjRUgOz#5ntM}iO!uEJdiV-+3J|H1mbz^3*pU#pB4stYn!PjYp5CFjJ0?kaypsnZ z?EpGz%Cr@7HLoo?IPC)7tH=hM{JhvzvdT&}HwkyBzMJf>(e#@kB^6uOc_G02+uK=d zeoW)A6xxubaORt>=xx5_yuG~|qltFgTY>36vo2NfN{n0QNxGieAm_krTIXgqlzfa? ziaM7>=pC{41?p2u_IRGe`PPRLh8-rF0N@Yo35v=V&c-R6&EvHp)d5AhU5lV?&xC

G?^*ncjheg5yUj$S}kp5a(tz#Wp?%#yuyL)$zZFA17`^P4H z8mx!zY6`|z?+m1&7T4hcKJ2Rd#9Lo#x}3oENWW*`-=PKxzVrS&(`+o!_nZ}gLycBT z`atP_h8hSJxRin-B$RQHwd0=S7?Fz-`EpX!*+4at~V!|0F6ozbEn@0RYtP) zbEvdJS8sFoVCQ?8r|X_C4_)<>9x2UZ6PEf_ zYR>keNm<8P#Fdt~==G}!|2Wdz2d)fEW&?<>MNo{@3j>Nt%?GazuXfe+7#+<0x_s=y zakkNpv)(U~taCoHJmr%->uIxL|CU#*^SH{1Z|gUXVz!;@>dn5aQzV9EU}1c$q>jv=;UFx_Cbl>%`(&WUxgd|Z zA}zW6iv7Civ*AvojPC}$>j-U@Z+6SFRpb-@t@_%_-%~1>znP3xGuNs2Gx_;|w9GGq zA z0C;1)2IPYrF`QhJ;@%Ep)|=b1g#fIx+fy}AXXH&)q|&&_unW7N_Jkph&M2#MgY&un z1KQ^JxaTGx3v^~>-;opdj0<#wG!64hgsYRb#+E-)U%z!@EEuql2`Lx)GL6rSxANw?1KZYlVK!3`6v}nT&>IQn}e2Zg@a8eKhYP>o_!rXMaNF{kDpLq zuVA6Vy;szcCYFy?fiQyQ23ViUhVxhNGPe)Q#=D@g8aprPavYgER#j>^NPC6@e}vl> zue;u3l~@d;>Zv}fIj{hzkQHzO2d3oVct;u=Ku}Ex1hAaLhuYwfvMp{+u$B8cHJkB# z7T&k~{m%X9xZkZ*IrvyyONT30z{JUTP%}UBmL>kiFqGS3CY`CTq$?k>|8y!jsQE(% zzx-{w&t*^Cmdn$qelU6yI|+PQ*(gVR1cdEyH*}7V#FfTdU)wlBW^}c^Xm$E9DB1vs z?|8cIB2KZR33k_0)}<08nGuFDEf*;ar1Z`trcOU}R2q8|K_~6&0=LGYWYuQ6V}=1TijVx-Qql(Q#x1GEn)TDL)fVOtG1t9dZ4JR3vG;i`DAP&0(O4LJOGGp0#uPld@pcI!fG4(< zSRd51xWRGq{h}Uwf}@Ed|94mAi92wSr=_On&xItU7EsFqcVP=Z=9#Ko8BhWQpv#pG zWk)K(&iYO*KCb!LqfSWOPD^Cz$u~FGFc}lNl#}(f`&LnER}$5xgk+=HskDln?h;?<4TZl?CUK2 zFm|-F0gVF8hh05YAzGud4bFVRF@G}$coL*`j3kbxK8JNw$9P3K(4>$)oEl;lz~Jqc zOVHb7BUcub6#lR)NW~V~f^D)kjqKYloG(66G2N#MPG zo*$rIoxcvf?F2Ap;7eM9!J_NZ4Qo-tIR`oXyklS91A5t#zSg+q=Yk67CQ8q_KX<>^ z*Ct}x^3YDg;`s_n{!|lT(6i_V39Emdj#W-SXNkaD_t_T8l_1<*^(*OUvhV%bXSw_x zm3n;VeQS_W_QCSlQo(LOaL3X1J-oW^Uon8J&<=Et@178iij z(h-J4zUNgbF>jgTy;g6D##mgGQnWuSYNnEiwJ$}tpN|1=l>rB#aGl1lPxZgC%*aiI zYI{lvOIP!s|JrclgY_Tps-W##P5mF570GwtypkA?MQ#tY%DThNt508?YIm)a)YZ`~ z(IvsYKZg781m$ckyErkWT6X|^Wc$BB01vVgbD!{76p=lE-8OBPPioL=H|wyyT`2WE zjx+sjegA{slMy_4HAby9o^RHyMCmari!)|81Gh@*&+|Q~7SqK{Ieo;+T%~jWDmCExW=~ug=T^sFR z$pqShY;|;~AfC7gOC*-WkL@2%Of^UoQyZXf-t_zY3RRDvvr{vzML_@(h#S$vv175| zgtG-~wQN6n3+*OG!+Q2g1;Tf?EmwO6X0P`vb~>c^+jLw#X}*#2 z7N=c=8}z{B#3Ihs&@-^>dqX=*k{M}J-!$Jbuorh--h~S%9Sr7sdqdS zGJQs4?FQ&6!w?#kSFRSA^sCMiFNY5jJ-c`K=8!Mg?_H37TK+pM>egK5{bF;ew^%(^ zC8MY{RvhtFbxWmQoA&m({q|~KN|tPHswZ3O3i#BLC3tD-VW>?>mCX_-g|CoeRP7PoABJ zB&C<9ZoeRYt=e(W1EWIOY6zd8=K)@e>I&86oR1y6CGB5AcQT_ao*~AhFCTY{TQ3CDxZA;mcue2T*FCo@ zm&565xz*%O@eeO+Rukzob7StJU|Xu+ZM>a|5=AJsqvCeCqf=(G{gA5(d7hQ! z(uJ=f+qZjPDV&3vuB`XL>_n$p2e>WK#gvhB>PlWooiR_Q&sURmtXmJY)vL?u>m&h( zC__!Nm!XO5@+PJ;v~76-GoYh>{e>rgebP9DFM!3IR6g7)h5SQ1>Y6qr953pVUBhA} z?B_7ItdHS|DH7#Z4u1RR8BdsXze!Wb0QbalqvEL!(%Ya$DdcnIVD^-NQc7uj=cYl! zx#8ytI#+IdoQPL<2#midY=JPO_RT|Rmb8>eXyw-@Jpxkcg4o+2%5NSd_-#!ka(~YG_nZd|$C^0S$f?c{60Pb(zIO(WxR^I`Eiv| z@z;5Prj|u201gQ{sWLa}`&P#0X#F?pYh=m01S^WaUTKe8xkQURW%i3tBaarFSy?vzE@~Vp^DjqT1)BP&CY*= z=d8ZP^w})HF(#kFi4F#q$SoIg4tmQiC4{c*t0dML!FeDZdgR4bQj_zR?7`~q|1zRm zlII>?wE)A`^YD)-owMqo25Z~o-;!C01X?lbhd>9RhVRPwz}X{-|KVzm{>#qK8(8?2hE0J0_q5Oy5Wx@9t_e}tDmO9axSlz-?(f5c68 zF(uj67WR)UDuOyGa&^lLWySaVr8@nROFKhn+FW^EyIiOnLnv9B1=ol!pi_Sy5iU`b zyD|3AZg_R!Iz4Qc>a?fgZRfmO#2S8#Nu|gfIU`i&7MCTgHPdrkT)E#=)$-M_W3;VP zBvsd`#K^*jGxGE~;v}g8nMDjB*%#>cRQA^EN4Ch#sb637Gu2x7^X(nRcJqnXYRmFV zSS4$-k^yk5jAZq;-a32{(3?uoCIPguDnCoZ!Nvof8U(*~XXwi$EUSdq$70Pj52;nz zrB%A|qF`)c)yxp;&|c^@F4<9hJ|LY$FONAbOOv`+qmeUTL}?uOeptlBJo(fW^(fC# zZg2K?zg=U{AlM(@B?h&PjXgvU{yfZoX8BDZ$?BT9L^9uFtjsPT;02-b)VqLyaWu*o zqaKV5bv+p_)mf@g(curZRdaZ7nXW78TG`QJ(q3&+npCr~c@(Y6s6cVt&|rfqxzzO| z(GAUMJSe#id9VSarm&*C2#|27z&ZeVRU2d@EY!kMQoKGg-FLyfjPv zEW0@G)!*NfCMIkCOc}VAOr$8MI&-W+o~~xCsO+dTzVvR}XhyPefx@KbEHwHta@*f# z4||_i55siXX-=B36Y>M(x03eu{MNLx9X0f?v%gk7_nuAwFWNnD@7r55fAhGdm}RGx z(P*RFtZic*i|Y>~Tt;G#g*=F1E#H~ezWfjH(lS{U9Op*hXf-6rMdCK=;`9t$@A4UR zW$aBXrBO!r8t*-Gh|X^g59mLpu8B8j@ALDsyO=(p6Dxj8n(P%LCTDQq)Q{$*mjg|}#3 z>W!sGO8w|~lRX_;Hmjv@d<9kN_aXjCl6;6);>6}9vX_d~_6$sylR#?4%G|YJ*GWb4Qk~}447)jjE0^OsJ<`5+`JS=~j7#}K zuHc53os7C*c9G*0dIG={HmM0`A>|p2V84d+5-LQZ^n_N_H|J&d$U>*R;+A@3a)F|x z4#!I3;S9nN`9}eMe5l17P~Xy>HTiG;J@hRp4_`zK4J3ZACLQ8+B}3$^SAP{@>5E{j z8>VBWkkbb{5d}mip9j|`8h>ugyrrY+AER&vjB@a);6t_n8ro!hC!EhgDIgEqWO_gF zd9#kR{@{)^XQo5x+(b89n+~IfgBho;)TJewPXySO9l^Dok4pz=HzG-Qr16hYZq6V# zJJPPUG}qVQi8 zRRnYggSH7C9?~r2M3KhtMjrT!YMnk>W0yA0e#DFyw7dlpUpH_MWSl+(paMsIu( zjp(Wi)wIAPmJ=-bhLJE}*fr-dRNCIV0U^VoLvAEC zW@z{5afbqPQIUv7;qNAoI4W(u2{i8f9QCNDv-D$<@dAX$f`oZQhz^!8y_rD^gJ=^qDcl%hIbT`9J8w>4O~utwd9fR}rB&I62OM*PLLNRQU^OU(l7 z0Ss0~x=nv&iOB6;+xP`dr~Ii||F1a}b;bw-998Z(HuT(k0sJoc+McuDW0D;fh;;oy zihh8vj>Vl?J<>h3>-#mFJ(}}=5|gJX#qp{tO5QkL&D7-5AL+;+5Dm%zXlKY<& zN4Ehc8PX&^Y92?xtECqrrsT4+9QcHz%}0wpHOLdCCGmfDc*8l>OkJgLf|wUJn*H}_ zu~6+;;r-`(Oq`V8=9!LsH$nTgEn{dV$)Eh-2eL@*Z}FjWlZa&X)FTy+(B@GZv*WVv zYhpiRyxZvvMmD*oJ$$m{^~xTCRv{}A7@f!}Z>|=Nod&L2&pRIN&9pB1yK|q3;C7T9 zGU7Zn&kxOg;4Kf$j1h?Zty3r$GceV8vc1-FgZO4meG3dpvbj0hNbT*1Ez!g9i4qt? za8^#1F&hrD*Hf}Lw`cH<%i5Fb@tTe8_rGX zbmjlSS_m=gW(J=P8U@B{-W{vBs{Z+c)4B9ONF z6A30_3V@jjdY=P?8Yukx{#~c|{fygc`%8djpx?bUfVLPGZ?Qe z?;X@QooHtTw ziP9+mO!>4uwKsRtUN3Q7iMjNBQh@2|@xvJ@t-d;qySX#5onMlI<_&N2Io%4x!?&h! zXd0B(u%t|%`_O)?uVgq{#pqRiRF;QlAwb(SxLQ@Oj<^~kxbxtyt)NEyL)`_MXN3jo z{bOWJeZ(>6HA?S1s~Gtr)`|whCx9m=79;)ID58UXLzN}h>qmB+SAvG`p8dV~cFR1g3=;XLQsvuS1 zm90q`V=M-tYm-r}*4~K)@ct>0r2_6kn3R zCIxr0W*)DcZap7!?6%>XmM6adc^^Xd69VOYG`#P(R=yPq)p(K^9%O~roqZ2bC>aap zHu&rLoD2i7&RTsd1nV59H=#KluLY~vnSZ_T_7Tsq@dP=PH9$^6s%qQjm?BW`E}L_f z28ALW+xCDE@U-WoRe9z(nQK`M_8|#vrz3JjF`sE^^DHYGY&m6)|kvj$#Ec zZxJLAmk-&k0*9H@HA=T#g=TB~VCg2dWRx?YK?=bSbS9Ywdd^>+T9bB+_GXWLb;Kf+ z$SRU}`L2}5ION^9pL<^(v_6xo-Uhx-V=uO3>f>!w<)36Ktqek4ACSYcZZ zj#D&BGOZjolPaGaDAQAjc`$*$K@H9Aw4PA4%eHo-drxEt|D;ZZ6^SkdYsPgM2O;yOC{+P6%q zf6CULxV@#=m~uRPb`RD8WFOnDI;RipKi$Q>DUYJR#-nzPB%7e3f^H0ee{?vQIvPm+ zPfI5g^;s5F0Hve%8O{W=pAYn%MTay#S=`Tu!$~o?Kgt7JRw5nDxiP{5$^vQX@e0Gp zSCD(RGN`U1Z8gubq4dZdzi4ihRyr=EZT+40^y%y){hwv!qd=u{gC~nfehqU;uaWRu z+P*)(UcHCDpmk`eQE%<@#tx9hMOEp$?qtr+POK;K_h2VY7Te{IGhf|Z)1(blicJ#INIj+H-<#IT*qpw@(L(H7# z4b2NxRXfX`H)Q-tziHFA4G54OI6Gxvxrnxy`UpfetlD&wT8(YKU+ZN^2}TeIqM zSjlxU8l{x?)X}F}0aFxiJR}e*;sDw^KoJ_}tWz`N(C!-n0yGgj)--tF{BA0t`XkQk zN;0NVxjdSS-QcDr?!8hZoY)NJG>Zd22CLv#kn|o#uDc$JrtLp26p3^y| zHf$xWinqt)g_}csMvOn`c|L-h`xy(hfV(hF7mE7S2z3QK0bH9WA#LfrU@e>5 z+L56ZLKbIVn>|_GiaRD-Qpy=D)bRi)NCI#9`DaBBcuHpGI z$&lUG;L;LA0O&FGJ|id{tUu?=L9t0MAz0o_aF-DrY7{MoPZ1aYp4 z#`$NNAl29nU-GT9liwW;Ym6CQ^?(_iWi~@dog3PascQ?oaVr%xKBh`(-*)rhuGUOR zDeN|D{ZuN>Y*iB1yI#D^`)Zq%duR}s2Z%^t+o6<1g56DnnmhK_V|X@H0ja4qtB8ui#Q#t zsq45Gdmn$^(HJ%MDtekiom#iFG~V~t+A28j`=T3CV{gci_;$3ZS=_?aLa4n{F8xvSG9AP? z-F`$$lj!Ig(!^Ito}x}Ek}2B;j2Z=r&w^o}*&!=hQl zoNy2(MADBC!FzrP9<1~@cWZ}}Zj(6-6KfsS>5+3eoyX`<&6F%M9}GXxZM+8>mcjuF zf7J<5loFu53#_P*Isp8qUrd0u5)$b7e7uQ)kc2@i6HfU5egtBiK@Q(h3yxmcy+rcz zss}Nhpm+XK_EwR%QzP^%Br}A6d^H_^p^Xh(ny9^LBHb@7Z*4|YZ|{`Ek-@W*gl}dqrN0PQ{KS)+a9bwU zHF0NqspZ)_7zag?1YqWwc8bXt(CbOTEd}2XQu&5;>ch;~eFC1?Jri;njA6X@C9BEX zUa{{A^s+%~pH_Cs_58}&xm4uZvA`Jjp1tz+f)r9CWf@OAB5lWm786 zHL4&yzHhDJi9^CHDb4Z7WS$lZv+VYF<4p((5O_E)X!eSZp}OEwPrOhNBb~qz;-Ve; zLcgM*`^;=Uj%{}k&~ZJYh#x~9XGq-m{qjd$5jM5B$|a?`#xH=*Cl1_OwRb-y9buTR zZj6nmF^`}g#{cYc0wXntt@E!5{fUxVcu_-jAT*a1x?xuv(OjZYtCbU%UcX#FxjQrb z39(XZ*&lgFG>F23Ge|y^vEw-6XAhT@Swcl;6al^e|X;pYQ;V( z%|4Y}cx-k+HZfu`BpAh+!#uA7n?7>J{prfn@ z8;jkR^?$9Sdm=rg@#e3h8u7)zvA^gtGRWVRvbXyzJ}YY(mzPXiw&-!Q0gHSE833u z9jY3)oBvQ5ySIoHY~1~|sQOuTvqHG{R^kFS)S?O2| zs5#qo!1Ef`gC5PMc8M?0Ylp)&s|h*G&oj3D_F6a#LcltIlGBQjvR z9n{;ukIKs1fO01hmF31{lGhelijBJW1O@?>d%r*YDd_p=dye{p5LpI{HyL1_-&7j- zAj=dSaCwL`v@gH4rhzopesPoL+Xw4>~_cH3C{lb2Yrz~u?I+MOkubMv4P zy{op7j=&AJY*KiTdB0A&LOmI12gP<({NyrlvwDvz6O#x}&N^nQi5^l~Z49NCsZNu{ zYN?8_^OM84t&{oAprK}ZWH`oG4@~z8l279r5(GmHubDSnX&PdUP9K^{yhbJ&y=^cN zjFEkOKJMioDh}|2=O~hzI1rKmQ1+wEbfkSFb&>~Sq0bg$%}u~LOjXAMc5b)SrS+V> zc}(SoMeQBFhP}F{?_T+Dhjj&d>xe+pHYbH=JBnzeL0}`f+kCB?a7^VEo8^ufs7FP! zK7M>YD=~=N;BJBa+rJ{S8~s1Me@neSUzOK3zzJ5jB64dmpDL0&(ts-jx!6T<_MttS zEAlla*Fv8(Xgr{6^3J#Y^|J29tM`l-4hEuHDj}WdVCx%%yjIcq;525EeZG~`k!z8Y zoP3Q^TIN`}1?ED%xu)R6-#)W zhx1pFfsY6>N>xEsNz)sjxt#oR(u}`Zc6xV4TrG-^h<{@8f*Ou%`86=b-ElG>N)|^W zX*%Y_20dYy5lMQ1nZsl+8D|>x)Hml%oeQQhZ)0W@YHyP?f%wPNR5UWwRSW2kFit9x zs&ffhZlrSB2CsFBRsX!i6&bGcWA5cB`*vIZ<3WY0*KmObs?FLvzo~eTqp zZ_`>r&GNZNjJQ>D9ONT}sNe`YiP}i`!GBKTU(1RLfNPeUwVWVdCv0!W^;RFTQ?8HN z1v{iymX%Vw+uCgq)dE4qV~h`GVX0+twY83*QhC6RBCC$;b0ZyWZqA=04R^&S`7e){ zr*=A*yji*19shBbyYza*?}5KpS;s*q?|;=HY|x8qDD9Q;L#u(PvV*Rz3bZ2yLb^v7 zFvss?sHPRzCn5gTzdnz^+JE>>V8$qW99@gP%HW zk)|9a2^;UyuLj%$4?| zQo1#rw$hbrs;|)LQhlbAv&&%kZSLj0#+uhN%$EBTo+Xa9(v5l4pF3VxT41C_?oAHp{Uy%2{Hbn!~vH{U;oqmnTQ`JvgK65Z!B&%SxVKR#*}L)KYf z$8zukM6W?f0Bb=qc?>Sv3GMe504FQO%UqfYGePfv@yPeQXB9Lt)d4LEuE92Geg2Lm zXDP54(M+rDBd3xbrM=D#q-)VQpr?~mG_Ezvz+eWlV0 zH~gf&TvGka))Ub>*!4G_ufEal3_ki!34eX#*o9xASh+G2T#tp8i1vM$M|d=*FJK-F zII;g!T;)eg@03C6Sa@xA!}9Y&z389%{9i%}ya5LF&D;|n#iXL9=bXfqz zDCo0ZMI4uXKXoKK>KW3CoOnJyi^8c+13Xt;j7E4^6t;BSaroz`cdBWEm$m(Md)aNmdJ2_l z&7)X+5fcO;G!1f=6u{XYA4^sqmW5xnvUww;Ej(qB9Ll|O?ni2vuHvn>bi zweMHEsghTbD?MM@i=a@5((N%-oTsFq~BdXO5a%X#TzFUp=Vy23M4!cC~iK?o9i!T7b*4l_6LL&9NJnC9HSi# zJv+W1=o1R{y@VW*-4NtXC)@>~@6b>n+=Z7Wc?4QVsM7tB8>H&9j z2~?O69jXp&Ln`gtEG%4O9QenC;-4%Xzn-UJp5QH(ri`Mh5xe%^g}dGr!V)A8kJ4?v z`K73S*Rqh;k+gOtiSDSTffgkE|H`JBq)3y5T?}wOlubHmH^tYe1Sbooa5t z*(ncc-|_Sq*TraBhcqc24*j8PmOn%E zqq_8;Xy>y$V=NLWx@$V6W-4?sD)YT`HYp)v8ubipAwH{`%`NiFlxxVH(PFjOxY{Kj z=f?At+by2uuXKUIUVo6bP4#=v5h^fb{Fli&O%^_5P@=09{p%m9Qyx#IGj&h~eCZEjuUcb;iU|K$1r@N_t7ETd| zP45a3gxVrs>Yx9DVVbV|xq2EF*2~KG>v5K?m3w7DSfL(YxvB$%MjG<`}7o|@uhZ0 zlQ;dfn6UXRx0=sDb?*k3UdI5RqnvqXY{9_7w&KAy%%~cW>)S1TFew6s+FgpF`7n5P zOsHK{3PM#d=(>F~HoK!Gsp;PJZd~1~WX<_Krgk(|&$TZ_s1qGQxsHbgubibfU(yvA zZlA_!XZ?FmYy^U6lndT2 zvFMucKVQA|yA3P4G^3}!S!L`q7E$Hc&%K3ym0B`f60=#k%mtk=bC-CQK8JEzjW$1f zyjS-KOrg1W=fHc5PGWb8tJfYRG+Iumz{{J7@;K&S09Cp{+yo8Y-5mkv{V)CZ?go<< zSr46WX^mH|A4Iz9R=1SbS;bn+s0l=N)u%C)*qKMFJmwWCGYp?qH7L1!_;!@dT*@tG zGViqy&wNG6c(99IGfAExWR6F~^@ixZv7TTjhCS*UiSM$3%S&*@v+Md;`53lLlCq9q zB%v|&u*Hb5V7K5%Cx1}C?0Y5#2oJ%WEL2)AhEaGoE-_u3own?_c~Xq1G}uDMaEW}& zSs95XvjuIF4D*^qjI)+f%ulClyGYKb50QCyNti{Q+(;1r-4rJ)+LTSJKJ1^if;OJr zQK?YsSP$zER9B9UUq$Xl8;FYb&%!Lv@HIL0xaA9?o$ZiO$OW6D%nA~I$WYf zId#-`HR0X@jaY)-59;uQU9t0au?ySDK>u0Jwo9eIBnHAJbLy1@&}`nb)-8m-uJ~5? zG;8Cr53ze2F4=K4j_aMrblcs^%WbNmaydq8X+Ta=k zE`Efc^2Y*JlUz(rx1Vh39mt(?T1(@P$vO;?bRTbtLj$$Pod9au<7ffb$nko=gmmOKu;+C6n&}se6}f5NKq&pN*uo-M z=ZS54wN9`qNimuJCJ7UBq^09r6-uk>#!NI?`MeZf6!EC{SZ<0TGtah@R2ccLJCe8p z`eki;M`v09_Q~Xh$&2lG2`6u;Yrk#S|CuK39>86&2w>pf&7pl@c}=?>oSakguHVomUWh!J-kqpG=#UP;Me-uEjwa! z*_pZGi2J@=qYW7KoqiE)yHOo%dEKNL6dj3Tz)hmy7vhV=x46<08Dm$>9pxWn18;Ai z8blhtp#|igbaVcqP1x#QWZ-Utvrw+ogf;3Rves+h6?~un%AVn-TLsE2)%La}9wt2h zZ95~Oci(}guleE{z|?#bnLyMbeZXDkZ}vBBhck0c5jQrTNQrSA)yRoJUr3cJ+VSc= z`f~3+2b5`gEuFQB=_5E$HOn~~0y9XOXVEym0=`r~e*u&FxiVKA=VBP$XS;n=vUkjr zh#1xUbN`36_l|0+UHb+@QA9wR6r}{EDZO_R1pyHOr57P0MLGgf4QvDf=~5#dMMS!E zsZpwQ=~6?2i1dUSAmsV>bKY~#H}5+$YtEXPKk^4_Cu{F)cJBMSu3wYfv{17y`U70p zN3#WnBci6@b!ZZQ9yD*-JXe7H-G+B%Ta2&SeDCUvlWZ5KvZ~$-v;H7G4f z64d4K;gO0e0byI7WZrq;DP5*hw0?wfv&DfB5Px^j)Zy++yBuWKJ|~111zIae0Nlvu z^>tJop{k!pNvx~$Nag8P-2m*&lLfzMUzFtw+9#5d?^cZ>iMr(#6<`ynukJ@ zSpxjYJs?8CM?K>m=$iJs3iX+tLIP#XNhygMN}^x@6nlYVaNNiQ%I-`i(ff&3kOjOX zDTbF9j9D}-CdXJo=KoPJej}{)zC>yR>#QJ`|Gic7E@*$>jKC%ReqDl>B?u26Rjn7U z92=Iy?G3qHF^ugjByU3dtA-t*(9-$nK~V=s%xgDvdKlju?c3jI@-9>j6Q|D^N)G}) zBtf^`p$UVzvxFqgbHev>b;T#qMuwdFr}BY$>Y^6JA$)WXKB!X;SBSb;@DR_S^3>i(W;^TMa`^tsw&mqUJbnpp;Y9- z;OU)K*;AXf!#`ajTBXh`9Xu39;uj`j8lPBbLoc;^>*D%WUna zr6K-~JYM{!icNd!hqZyOn}R(yxp>7U5gCmtgUmh{ca7m@BH!2kB1_W}Ma7$X6@^6^ z4SJK=r(SN+S@-&^z7vy%Wsa&Q4Mo#HIF%~xM2}2+EVMH#@psvvhylxn=zo{G6t*?cTm_f$RpG6NPqJ*urGR9nM^*ukefDpw%~{V3^4ih%zKD69on zX4+M(p@Ly0?kM(vA8l=25->}CKau{>f~?_+8|K9#rfj&$tX-!%H$z4v`4ZK8;&P|* z)w{z@ABa^Qv*J6O169N*N6l)Wa0>M45u$w)rVggj6~E zP^T_4t~~}0Vq6!8d#D>4W1ipX@_rv_3YC0(?!{%F!NW+UvErEUc+-?$){@Cs^Pw*R ztY)UASf>@CIpmJn(Ru*l0*tGwlkoxr3y$b|HFsgK@g(}5lG*HE^}J!-LFKl!19pk-rtI=h3>F6a=qmq2S4OMXoT8bqs;TLCE5vUCLU~#ftWiXw)~3NK~qcf zl8Ah{Y|rtAst^gEP#~Us8NBCoTGJJ3`UF|rxxXN@->M@9lJvD7sSmcLhIea2IO%EB07DTD9Zr=iLTUq^8X!2UGG$dFOp2FUoi$Y|#`aHJ`Ve|Q=EOHGoKO06X@HSB4`*#%06q%63nr3puLo-QuZlhhH@*iqmOSsj9b-rk2}nXxVN;^iGH2O1@iw7 zcIYichLY8u8t3g-1kaqXTeu> zdH8ltNn67zO8!(UXPG{z5-HCf3{|(Y8GIT0>*#9%9Sf2RjO=Ow)1*`9t-3!1LT_c? z#rH(Jxn7Ivz%4~KH60sZh)1!U{I?3OXEyIp+)}@e3gMy9XOaZ^42dAxWuN!={!k%N3Ei4piIG7iG{6%fzH+jxrSR8|x{Bz~aqm%-qr~?p{H5mb`5o{`UWy5a zDK$ELZ$~IMI*ZCfa4PlvoaG=s!%P|#!xo!yM^LxNgtsG1rN0qeF_|d-4Zk9Qh-CzI z;8Ps?OL$bP`A5N(G>jBA7Uw;7$FBc#->={|KD2DTiC^vZu1Z7+DaMi1rkw&DXA*@) zDyx+&efmO%{M|~`K4SDim6D@Y-s>2!Z~wfcd9JI2$81V^l!{k?D*jp5GUP^8Zi3h{ zh>!zy53Pl^L1y>jBbgIUuz$WJCkrA@=KNMJYKz6y>q2%s&gCeW*89} zuOjrWY$J79^n8BNeFvc6jqrGu&oX`GVbM+f!+4v&AY81MXoj|eM-$|MMWY1t`To?v z&gIo^?w>^dZ^d6#?&phT^|ZiHs-SFsDiL&wSr+)+49H8aS+#|imM^_VTKk=L-RtGOSPR^yKX2AV? zhetPcg|iNJTeG4}*|EYw28hdf;0)&L?&3_GU+qi#MEH?GtqRaE5HRGsKo|U!P2TH_KO;;L zNe5C`bfVFlY%XB^WfrFRk4^x@EMBVx(sdP+-*M!h2yRqKf?3H$VD_7HCP|DWwU=~M zc2a zW~HH}(@W9i;F#*4>8z;iE=nB%?*E`R1r749r9ShKKrMZu!laT9A4~r;#WlY4YZ>G= zC*RR!PcDY23m(hN)*Z=we?GmtMLKt*TgytM`<{X4Zh-w-3K&!&{*Qz(7SMC?*GCcq z=m-yz!a-hJh11^>X)zk%?x~}r(90+WUJ5RXDU`Pj^%cM?5saJEB!i{%&yq4=jk}Jb zWeIW2qNe^A=p-=!NdRa^Z)bs|HH@sKOYAE zj~D;{>&fds67@Q=)W5flsmXr$IS(<>_i*GkpjcNFiHMDT+p6WfJCW@+bfH21a)Wlg zZ#-Cy%AuG-DY#u>h9EI3*)5+-jS+u!11H(RXCN{~`-!RdEiUwDg(gBvFOrKXhQZC3A?Lnp?FRWBm1>G^js4`xVg*@%;;ude8iMs;Bvbf4)T3eDWA3 zK?VuHPE6_zrXh(lMw;d0BLl;E=!U^bQNX9ZL$vI@&*(jWkle*63H91NTtQEIjtte` zQb9M`QBZPFq$XK$D6W#PA%)I9vrT^sNzi{_~ zQ^HEAeburp8lvC*bx1`DYrT<;~qM^`@SP|+BtO| zo-&m2uqDUbWUYwH|Li_L{KdqV{JP>Tkac$fLlQ6!1hBy8I5JxY(%_nXyP$hIoqC4a zqLX)&B@AbmR-Pkm+n>8td3Y&kmTaza=6je&5RC3lknEgw%t#Hv4va9lz<9sJGDtqi z`aQE_7xSvt_2RiSiIn?=o4wEDBmB*uv5BOtpp9~MhU`@zoV{yTAclfOzH^%mycY

lY0Qhr2*ydOy+BDj=E4jn4*8ZSwl$sizAiYHJw2&%3>NX`1eP<<`S-OZkW0Y zuT%u~@`=|=iB=m4^ha>5^L#;L6-aS(xl;|xIYvmuFsGhbB0rUUtMqW^ z-bLRWT_D{B0Uxv@@H$Uqig!+zVEeL&SarWCAJEFMz$G3TxU~SVnYYKU>2C4(1l&{) z95NI!WwCZPmwo0{S$s_)8|0)!rIKz;k$H%xU9*+d%O65yb~Fi+uO;fOMT=FmhJH3v z{?<;BlyJT-zM>MIXijy#a{W=ip4XnR#Y}M%UFejp8bLd?XdQZqq>Np&bu*-!aiuE> z0!1Kx3s$F_JI>vlOrJ%RL+KosPgrLJ&UWBUG<9BH{P#`|qlmdh&PRE?lXD#kWa;tN zr+(N>!Mf1{b#2j~Pd>-u_1ucBWfyd>>Af{Y4bHyx7mufG%dtl5G3IVffJWPHUh%U@ z5e|f%$kuCE&iymiugzX@2if{}7HC(p0JnjGpo#*=$4t!5gXK$U{Pa+Nv$lBS+7Nfc zy7bD#qEJy}=7mS^7j-2TR1OXp0 zGuDEG9@L*U-@05Zk8D6WD)Xhxn&4)4+2*qh2bcW}nUHr-T`JH{Q4cT=K(2Zec6}p{ zH_RgNqrULNNipoG(b|(QZLt>j6R5|O#0Ketl$70nrzGSwjD|Wi?0Ct}!5w+5yHUJX z2tYK_*SkWej0kk2G79fc`AG_IaScff-+h-iyRf4u-Nb8BiqjZ;cB-o?YiW9Kt+FR= z$0>80Ex7=-6i*?e9O)K%v6`5EsvFp-L6D45b332?J`mc;;Sai@#Mg(gUs!EIPn!i< zsnWeQ)A6fsG#ci_2~u6A_>p>hjujPp5ln+)G@v#@kqxL>Z7*guZWiOSZO3-x)O{Zk zQJ>bD6IwG+83DlM)`xN&Qg@3SV(-p95c$)8FP$%(_I#MY=d+A^%c?2WgT$ZiwS+DC?A(2Tu26obC^IrwOkQQH#0@d@*$2JCwdTb(APU zz%?)%1__-&Q>Ymd;(Ezf!3NQq=czF9cfU9Ts5xG!h67n3%r2&oLg|XC%KEd-Ln1TPGRe5kuAE8$>EmyX{YLu`z z{-7~s3TvN^b4t__`KrRy|IBdk1M>uvOYzfZ!FgT;M#Hn*R#7a5Hc%Dk)Pl`tShJ#j zGlFfgF|@tv5>s(~_d(Z?h}=tlls)}t5hC!|*PPdXE`~8(;BBDsH)!onQmeFB==m?B zNS$}x*jhZ<@_DZJHdgvkNt3X3(M2{2dDv7 zzo}DcwBd)-3pQx(Ol*0IOUTWT9+u9}wE8xzU5K*INO)8&{#zuPUhh0j7LR44|HT}m z>dg1f=vta^q>J5B7a{KrS*~%4fAU?!B+^=Zu}bLOp<>-@biS(IHTH2C0sZugGp54~ z3hTPIv%$1CgguK3B|Cw0pe+f8S|Sce;OMGxHCoR=JEP97RW0gD44=)7ztF#Se0Q#8 zWfs!qtNIZkQlDtAL6pR!u(FQ=G1{L`d48vV)TdU-Lsu?hbc$yq&xQ8|$>l$dyU^0W z#$}!-lCz?IMMOHWSQ&4K74CeBHzlf|qe;eoWr3Y!*4%rm8clXC>=gpl2x}ndg@71~ z(oos;7Q2bE^f{rM!#-#au^U>}1QiSul9FhH)0;Wa+hQkU9yCn~O$oG(A7syoruxd) znT<1xu@si-o@>1^T5>^ys!?sCG(}5E$Q=lLf`vi4{B|0Tn=f{P!@x~RiLnoTqRxvLY$DdbT zC>v&l6z;$~4cqdyQ20uhx0J(eU2SlbgWtJ+L1UL(~gLhzVPJaR(ZL0iaHNL0Ro@ z*4Pc-X$-BA)YmFSJI0Y;n*DA9u@aa_%o5Aeprgg!x5oG1Wb<~XOzEjWUxB;1!MsUqBiv@(gAMsq)<7eIML4gTyc_`i`|Duk4ggHp?tra zpUypCp=qkpqe`={61`{ar|uV%!}*V*N%A|J7|?6q9E3&D98i*H(t)uGXd>hi@{L^M2;bLYhPM7u zV*_(%xQH=j2{hl@;p6qmp5hHQm-UCS%$#fVheBdw&<8~D1Nnn zx`{uq7oL&*;nt_lnkv1hCkLt}lGK8*`BCjxz}y4L=9UQkaNO9BY<(E+BuKdFOXBR} zjjgQ|-J*aj=Qkq!++PYVi55R(qonvdB~`>q#%op+Tht}#<&6bmjk-nBo&&GiZ#p{i zQ!_o^WYTek9P=M)fc!JxLtElEf-ue`Sf?xdr{BiMdi)1z(hY*I?#DHQ4om*D_S}eT zAu-AMDcq|^@ud_8(;2TR=KjudHB2pXwqf3_iihtSwVwxm?t%06Ps8q)0hTM0Qt1-4HZPSx^_f2k*LLLcFqfv?WLgFu@0O zas6ynU8W_${je5la5=L^Uf~cJ*Q*R~!LBzh$#*&*YF=8&vRtr_d9Zv=R~0ahmh-d5 zoS-#t_K#@3B}{jzo9!avti-3gtTC*WW!MpJ?Q7Kp`jl=*YN^2|QKvdt(udF+Hxfl( zTf5H~1q6jnYQyOoz1DGFAusZELx*_z3APo!%QnKe#A2{PqCLzm)t1*6_srbiP~}MS zi*s4~%A{sF_$nw#M6ZS>jocp<$Dwy~#o9M7#4yD5`aoQGAJ72`yHwt0b9&;p6+;e` zFjuUI1xQAz4Fs#2OQqH_@Y0u9YZ7NP<(PTCkUGm(^p4W*n~M45X375b^Y~tY36GKY zGe1$GHYYrbXk7FU(u)!ze-E6s)es9%SQCd2hc^&ORvC*HwsW1cnwE3=!MYACYSS_~ zpZ5fkX#8>Xf`H%i%B_={?AqF3;Hy#y>tWOs5{~!kHgA_C)|LaVZt#nX%bgsGC?74~ zpih;B#O?=A@$#yMhLz0U?);x?#eEOUWORR)obW0!Q}- zH%w+63AJBLsq)8ZIi*i-`&TCbJfzQ8`lM2jr3;eBX$zIxAWj(7 ziV%+VYGo@4EX1kpfk?h6fwk+ZLAIY4E<5|nE15rD>hVt754llKbZfbd?lxs|zTYT$~QkEhMJmO4NAH9hMOy5MxMR`z6fzM@aY&?4;F&gTC$Ziou#bj~qlD z%EL;|=saVM{6ggEgiqO4(L!hDT*&k4wMF{;?n9U?zBow&CO~XB0~^^m-kbP}p45O+ zwJg`Q>fA@2EOYs3oQwTJI;US*ntrmGD_adK{6T9>Zs(rlTbL_}(RNL}x9t2`yQgZQupU`GTMSJ8}k;=HqT3KI$GZyZ;U)TU~o0i5h)!KWU_q9ai_j25i z*EM8ywF%}nETh);MN{q4BzL?3Q(!bWKBT2$yE@?m1`Rj^;qj2;9!6@BY@$$06ta<%Dxn8|(tkTjrgJa)%kF|OZnv-A)_-CRvwN2>YCh(mR`^WqXOrrC_wY*wGRk~q zivAC?%fH=AaNaeG?uJeV7@qvWqHsHrd8L;G@zpRz?;#3xd%wTpPN2vQX(O{;WG{O9g{-lw7zK<+t0l;ge5PQF&#EXAH_&^qTZ#QCJb{#|H z5+;s5CJ%lCM&1HTqNIrbe!7VNmuLGwpA7iFI$L5Kb|g+sAKoQN_f7Hjfii!pWsQz@ z%bO@;70L_3$(lOpc`HAa94#`n(~M1456ySc{QDWT@Z^p#_;t9ejrOV0Spm}LXxhkN z>ZJ8>l;es2orG5p|A04>oV2w5tT@H)n60w%%rlg(+pu0x4Of%emZdlLeYg3R0fwE4hvdQt7=h#%&sP|zSPreMv zCTZ>>*$Q5mh#IY+v_}ceQV65Iex_hTNm+trK*#*^!m zzbXE+!Pws(Fg>xt9wRK$v&MqARSh5RFFk;K?nqH9VG-?1*t|^nb1fBf8FB)7n#!2H zdaV09at7_Z&^QiDgIut)wDOCgPNb}j$cKBz)yux*0*=Bm*Emy@U zV{u1&!gkEA>0YyfOyvjO_+n~Gsax_7Q8%)hfDT*cA8L%)Hrb9)u9{PY><`uz^`)|P ziKQwjn|iXWDHbWZ0(o`WJ^_zY?tPzruFG7Q%$EF;zFY>R@qduoUG7um5C{+4R(U`v zC)FAw+m#>?^t1c6Xx!7BC%;ML*b8Sn8ZRve?!s`IsWv1xK!U~HsAi(J>pGa(vX{E z%GtxslhAzo4?pMDvK&8vp_0LM>*!9ro;&vQjtF_NsD^ zDS~!hcBeu#Ic}Q$Dc)r%d;G?^q(L6hsmx{FkIx*08*4Cp8 zAub!wS!>Z#jk&y8yL3P5m;E__Ob#PfiESGzeOW15qBT8jT;Kl%yv1)9=F1h?^{mHj zfY$bb>%)+#jh&e)6Fc&-?e&R~Xg_FFsBl{Di?1jhz3c!>kDybv!pVD-g)=5cl?GS4 zCJtG@g657|yJ-1C#6t0V)i1TQh8s+Sp9hXCA_rTG4p9{cp&ofHhvBeUJp=#?RS*-=^}d0(Y?4P8V^ zs>UK6q#qp%Nt4$bJI`kJ-KM=1y=1547`zM#647=z9uY5v5u=90_45Zh4?1Ddc{=t8 zo#-lhQ_ks+XEOrRgU8H|%&vaC)QUeaEn*)((2jI}41z2-{+-Kt8)a?}j*%%pmX)qG zvuFC1Zz%q-w6N$n;!tG$au=8IU)w3G_U}69i7u0bs}FtBa#O4uPN1wChM%)=- zO6%*cBezg+qI2vwhll|dhnz#q@e#|=@K6P!Hk6$RlBEVUU$xYwz4UfeP);F>+5v~3C96E#U(tqEHL%d!T z9wpI!d<1!{(pvDS|H8G4JeEA(x|1dTybNvQN@R3TEac}iHdviTNg>nw%$G}*@QPIguiY(Z68Ko6NvOXdP~ayjNs5{a;3v|7G4z48y>Nn!+pKnek&8mgy1qw= zEAxy{v6#b!&8`}zvbLX|>KZisS(w+-Z_9aE&r(>@eoc6BHYpXimN(YS4fn!yB0F)_ zh%MvTc;oaB6G2(CiZ5(-R(y-js^vNz2_wesd(SK>i~7`w9qFM`s@bVEF?6 z&~WYI4vSkOe`aDHh)A{f~lCblN&_NfW)_H50(JN9eh;nTav zdU@G~<{_|^1~N)LCoXQ|>D#K!`C;ipKjXyMS<)QFan*he8cmiNZ|5T_>Jp+dbW%&K z%j3g-b@Sowjx>L9B#Ca<0GsZz++xxuhbgD|rn|w=T&K*C`&CzrY0U#$f-94L*Q;FJ zQeu<_iHDM%Xf{xs)}YgPpCF$VHGbgy(%j5AUH9T{Uw(IR)iWgh4&S@%&de9}=AD8k zM+lVWni3EreMQhfXXhcRo2D__CV%PhsXCq!x7}2_&ViXYjZ{h8qg@F{zm z&-GWYq{-t_Tu?y zzIEp#x!UihC1%Lv#N?3Qhqk$6)28;UU$YXDI(HHpv)Cd#6eD56bBs*RK9bDhzvuho z=2nec)>bGUHHBC>&eVPSMqXuU!p>!v+Lj@jcrw#({>~J0n?RsD&`)8c#!<9RF`0S* z%0@#HLC1mFM}2_T+qwh?(7WjE%iT!#AlAmy4-o6m3AausgBnQ!zfEfIuF;;R!w;u- zcV?_h^7KGuprxt%d#j1)otvcg8&$S(Ene@o`NCx`-TqsM6;AG%Hm+_O_j{#$qD@=^u9=GGNM@i`DFfJv#FrA>j$2!0NYQyhBJWG`da8!n?R>+vt*Juh=6{3 z`zu|Cg+AX)k=r}$91=rophqF5mXBY(5dy-yB?m~i3$6W1Z$)2NXaK>De_rr}DDPgo zb=hxf%3O28y7T?SddHakV~Mmnp=G;W($^wg{W%{hs`y=%A2dBM^|MwOpEFRy*a;-^ zGUCk=%E0`n9@Zf?@@xE{_>w-1tHdve1ul2@yaGg@CZF|V(`8TE0)`zr@Q%mb(tUgA1R4JpX<8fp8=ny6uz(FQa)M`Zwv$A&Oq0V;6z@- zE1EQq$urm^@aK|Z9jD}+CCz$%lO1&F75d~XEv|CN3Z%epUE^B)UO-D$C68sHI(KI2 zury!u=`kQhj#sHIBgA#tc-(XQzL|8Z8@B>k*VMAuKbbnw^7>5EG_?a&6*%Q~W1Lw9 zEQw)gdbyF96C{~0<#_+QJ(EF0?czsGYb}3!_ObgH__|U!MmUEcuFlXpoo`;3Njfb= zEiC3WI|o_%bIP`8+x5$7L!MKE>|U}fvtjHIc0TFhv7btiz5E0;SEz_J=>@)b6N?W9 z*>+avtILSN0PCV#s8|%}%fY^MD+6eFrX}EH9X^*6PQ35|7p52rB{>jQBh{eGA;yPQ z<8@FPa5_R0dM&jpL)-behi&(=cI^86eap!>c-p9^?{7${?&BL&8N}3650XM7(H!0d zyt0-5A#XZL(l1z3*odk1~}}4V?nmJ_heqtM|DGV@hyjMiEM#nK zh*^7Gi=ZvG+3Rj)yW$WaWoz;GDEUqrZ3WIyo<2FD%a1RN@o^myi!VL>mc`g5YREaK zi^Z_6KXX3OC&t>K%u^q6Ilq6{_)J{tW(LH;$9o78bQA#+BH)ZaiBEBe_;$ICW)9G7 z_#9GWf&_K+DHAz+e}mfg?hVUD>!>}bDCaN93sTNhr3*~8kL9Iu7-%Isf=iN6Dc;-b}xD_aC-psC(Em+k-9 zuSRx^q)3hH&mHF}f`np80!X5WJ&FM%VAVI)fl2OE^2Ig1a(j2W#8!RRtc*vwU&N7|Kaogq;j`H*6z-);7mmhr-wbp2|;^3`a-$QYeh*D z{Up$c>mPhFS+$2}Z6MQPiU*uHT&PMtt3TvQ+Qh(l5woogyN#>FfUS{whzshW9L1f+ zJ1UgnRDjjQfsL_stA~!|L}k_X0*gZ4x>JV6r>qAUDQC)V)(S7mAf zXJbz`Hs6De#;a#81o(15WVx4`FFqUFp6mDGy|ej9sit6is&YMreJuO^bITEV1!utT z1G&9<0pYlBj~~z9Z1;0+aZiuz{4z8C_4k#8JH=+Op+&ds0D&WNopvut1|5$6<3kuB zUjl1nW*hi=9xFL?0;COsR4{H}!V?+Q`zmnf>ul9G535zMY3xvCJ)}gAun>LV z8H>N?ea1TqLbt|Kdm#=l6Z$dN-aZy_@Z)`&Q58V8)hx7`c0yN{ob^}&nMSy)5Bq&x zUSM{&i%tH5WEhsd0CcPD@u0mNz)1}5>^eC&GAky3;|{)E>RE8OsuW+iDw9?SjFnA~ zOG7DEf{@Y-)8UG!22^hG{jxQUKJ-wcD$pFOvQTXV?1fmw9XTxla3=K>Ps`sESnAp$N zF=!gE+M)`Xdhz1zn11m`{KeJyRd9W52J@q2yB3AzXmS5Cwkqm4jac zmzlIL(Wy3GROZftjG37|=)X}jXg@zeQY<7ETb#13#?{|0bRIp+vxH3sJ_))L zr1dyoz%xMEboojjBuofR;QtoNK}d>3QdDkeOxqI57JJ`Wh2)u^+FRXToo84tVs*dO zI6ROSAslN3S#ze!BfebTp(RP+JBqevS+QNy8W=wDb@WLgW9_3yesFTob)Na)$?67=xi`SQIc_qQV_*! zL3M*s^7tkcEZjGd-~us$c!c^PPa%#{l%Wu5EYP4_rvOHT7!ww0ejxPTmogIuSquC3VO8CPW<)^%PtBZfm zx%jisdZe*Bif>)Jd)-KPHujx~!)_o|1;hemhyJtt9hnJ}a=+M2K^1akRP)!M~pJ$~-#2Pfm>&o=3C(-1hX?+$w5m$-EX$?IM^w%*L#( z$NCc@i!i*Y&Fqs}qw`&Zr9|*TS>t;(*`@7Bn(hk29ykZd^vTm|f>O4|7+>Zy4&D2O zkNp<`9r%`L@bvdzU;J`RsxonG%SQNE;CmejkQ-?jA;^4R!`9@8Rfw47%z^=;Nv227 zcipPLT02`E9EGXrGKz2$VcV9(>=wC^^CB-I%ES;7TsOFmD=OVHY+C*V2T>= zun(<{BC|P;K=U?!ojlj)EkfKv_JK=I?Uhrf%;h#eVA3^DequG+YhIf4){!Sl7aCTT zf%OW_6Kd2o%|^UDRI@*Aerv^Jr%ij*-jX+pFhV{93Byo&Bps1v*#hkbf1P)gF!D+g zh0PClAe`HIwe2Y`kT^yl-3O-j9%)g>Q{KClAo0ZGRl2N)5G@9h2?SOwXL zTTuDH7xxIE^`{CXDc~&0olH+0&?UaaR$X?1&K}kNQDj~Ve&lb3n)Q@7nXuGqjT7Nd z`?f4)1dJmiwU)6Q);3C9~MDGsIn1Y=w2x7IGWPpW4af=#PN!BYU%S~ zn?J>w>djf{dwgCIfy3|zXs6*dGe|teYW2ig>rd}{U9xcng7cEfjw@C>r5o(!q;#2H zmGwkOe2`Z2fZ#>pkr+1USD4^bE8|jr%+=@)=tYt~9u?avz62)ZX!$BM`hEDhQt!OA zRcSG7=W_Mtwa8=O=4+!tO-QhLPm?6t*+bN73MqLiPmCS8pWW4dq0Yn0_(yTW#KEbB zpF_%uZ66L`6)&oDNEHjCzr3r z-8NdhnEHIhV+VCIp1yE&ykOuh^2>1h|jDVhN6mYW5kF2APgWgR?>S8ddj%(=n- zEb#qraK}9|JDlax{4nJCqoT9G@=40=mLZM4SFc+{RVXP2h)#wvX6SRFm&ijKcuAZR zf$=3w%!PxOzdf}RMRmV%d}-PisPFbmNYx$?@JRb^$|7rLA`L;jhF>I$S-AQlwc<1g zWc%DgqTBLnO#R#x{_r&_xYS}{MvI}L%*D-@KE{{jE~lr({FcANO27>oG`Yu?a{`XN z0p1>iZFmf;<}%lo1g9T@duDcRI=B&3i_7|CA=w*#FGyeS;k{+`kR?ZA@fPeIN{X7w z^k0}52S5VOiog*dns`d?oJO2nsalg@21jN>TqJlIoDx@c%x!*f#*A1p>^Mclg{pw! zkcCCI(tE@PW=mL=)On1-XatqHl`;w+CVXPa>dY0OX0V@d$O}X_oyA!|r zWPbksQe`K!uRmk8J>VE3LE48BROS!%;bA$GR zSEidZBa-!^_gtQG>by>|y7#adEmlqz3~?tN@CIe*h;)lEds!4ebt~KVAKMtvjKhzR z;)Bi40I)VMWN+E~zYFUAhfKDEu+kQZc}YU4%2HfMUAa%C)>wF-(&ZxH13JsntU)-Z^nEh_e3iT2h;4S=G-0upxPdv>h(A?bl*W3+b0guCMKl3D?j4U}C-*W|w2E<6* z5KV7>Eg}}_hP9q*9j(VuW7btd25t)oy>M~2Sx$9`t$dgMk}Hjy*+8{$4qJ81^Y_Sk z3w|cfn|+r+`L`w*pvosko`L<-1;!WvE3${K2C8>}y4LvPe8*kH&|DI3riFK(;>SM} z7*0f#Wn5<-_astNCMrpzQh)CiaFSav#aX(+py9VTs(;qMs?B(b5EvU6xT7q1 z)WR!z%I)l{gWoeE^b}^wL)q4A9}PU4SrGHCzMYQ%7K2vdTju?E)AOH=yGL+kLUY?0 z`MD!C%bWLtUP+nkN8b2=aylS2g)U=6Hw_~%LUvgRI@p~aM%KXEr8}cTP2x@siquu8 zX9K%^E55m-)zrb8!Ua`XZ2u~y#DEu zH1`($z2vi*EEGGo^ueKJJ9PQOBxyV(G#Ch0fL-i1?mBf(Wy6~pEE?$*=hh#@s-ru| z{Nri}?1F07iCnv5MzK3eDsM?@c>K@TU(7$lMK`40W(D>$bs2?jrjHT==LPSdzaHq? zKNjagLhJmPsEl|EVkj9_Q>dUqSE(2e$*o@vd9oz$=5#w^xSPtvJKLNReH&f5GfUp6 zza_F}@+B?z#O2rPbs|E0vm&>mUgFSECu#9Vyew0{qEnAd2W~!4Wt6#MIGKP1&Q0X1 z$bXhyOq)I3Ir$he!aEjeMMU=GO%~)Yc#RhA!Xm2bVAP(p_zQ?1lx>gL*~)~j;r-I4evf-NdvkX5$fdSEoo-*GQB5w#WU zGAL4VOoO5==f(iI%NZteFwl^}AlE9hK`?aZQd64)neAv}!@itg+gLziAsT~|F_8>g z-tn>7e%3I@H<09Oy+(uP!t+`C-hO|6S^8$#ZYj-3>^QW00j;}u3s;g>mWWU2fvy6^ zu>}xp?7ITg9=+UYd2lv@V`RR6GF&B^v?#ZS1io1>n1t!*&s9$~5d$dH*ct9M z5=3Awutwk)nk!fBVWy^3@0SN;Hu=7DW20b)r%1H;9eNq)t{~Rp5D~`Y9o@-VoFqd$ zsf;T}A1t(+Z-3oSNFtb@e+Uk4mD3+pkXVk^PSmFd_Pt8ho&xy#^oPUz<=E5ayfQ2b z3A&g#U5$`EyWVEp*Q%BiWIbq1N^ilr7mzXYpWZ=d)#R?5gu$?!w1B0AQ&x&)- zj6~;+JdU;00S1n2Es&E1yMV&3cAU|5Dtbq@#ZsyN*_IHr=MR6eCEf^&6?Kn;?9R96 z@gmqN&upu<=YH9B1;q~es(g8i6rTwe=fJp;hS%GCQSag?ZvHnS#(%o$w_t_ehq!ay zzoMzOVYOlzOApKb2fyQSxXI1Ut=g%P+h}&OAfQE%v>K}{f37oDw?KEdaa8U!$a83- zW07U$el45PwqBzWgr4GSaq3EvXpzz|ax5q3*H}$UL;v=G0dB~b?d`;?LSBmwk(;*L z{ikgb2g*tGe?h+Y76H@y&=YC5G8u6AUjb&r-R~6t3xX{CTjqDxRJCG%a_1kz8!6Ps zNeX*_Mw%dslZ=90Cdxh}jDYDd32=iX|H*GDL5#GQ?TNXG5NOO->ZsJz8g=esk*-yX zFONT$?xN;VW+c=>$sT-j1Han!7lgN+ukX~9nn{@h zx-WNAKVU>wbpGTRXJNm1QGR)P1Dt*i%m5X0FRIL^YwBL)Bc^m#Zr|dSCSVRNKpWW( z_*RhVJ|o*)*i_a*IE9qsX}BYGQ0WEEK6)vI&#Q`8zU13<=GZ4TUJ_mk4gPOtz*ppH0YF0I1gy3^Y49IgCIsBj;Wx=@dPxZ|W!|i_BXXkaqAGNn`z9z|R1-dX5lv1>GQc-f zhSHLU_({bwPgF6D#L)vZQ}nTK$|aIjJ?e^df3U6)K+ej$BL7ISl20 z1Y;L5P#07dm^}TEzcg)z+!Guh;@1<4-~LrA%bg)yBHUC4zpJZd>8?0b;8Yw*f|m#O zc3A` zI19H(cb-}NTlliS&Lv8k1A^l2@ zCuHA<(Lmc*1F*^bNEQ9;?f2$Z)vSrb0GL|AOd_dxiJguHa=(mN;^4#TjU%#td5oU=h`DllG+VkAVhhPs~PuMwojRIkU;6E$AyMG}oaB4!w zaBewLxz55>eWHl3lBW@{=yfnTt9^GLqHsQ#^W}@)f_>aS7)M;+b&R{8xKFmwlPs;O z@b5%Po&1)qnZFPzm?!4(S3>?WI1ThK;C~^H7)y488UgSnXBu4u=8%#`ib$X{M29|! zoZySSl7ef&WakzWnhekrnDGPej0#=pC5A2c`V78wr!6vihR-T*ddp)KRov()@mmM* zEXVk|KN#dTW3g-5M?#}w4uuj4)CC7d_y1p|vJ){~ZG_2otmI#D6 zP*f0NuatoyEh0U)Sj~W`#UX=SXD73OdIy<@hqr z)vdm@py2cWxV^h?K)KB(_ry74nv@ss0*D$9|!wCHmMr{*{?;u6V^=j~{y zB3sCU^S&Ih@o)y#Am9OSNtyR<#B>c){s8mH-`V$I6xm*LF1}9`@o~3f%iMWBQ0Ll2u}PGWjFPg^laKM`CG;ki31#p8a@sUoSkdJW# zud5wTbONoVeHmvLOw7f#Uw#QsGR{~qws8ZqJF;=8-7=i;zv!gvyF=iLwrEp=Na1*^ zj&D%O!ooINVf_bbQI4jhf$_cHGa=O5EWDKrUE|#XRcNxLi>Fw+bv>FJ zmM7xN^J#*P59Ib#{5UB8NK%5|&8Mq}3BNXueSc!`G7XV$D`8b^4t@9Wj3X6GBNr9U zZJEm7lk6miX=LXjR6HWM0Gsk)cib$&;>&{ zYUj=Ss*qywlJ6N4r@=4J7Ev z6u6&KW|Dp&st>wc-(^$bE`$EzVq&eX7$@W6k901_J3391vMEXXi(}dNB37twILDh?;b{Ti$*8DKtN9PN^$Kk2mzmfTBEzP8)WPDhLhPF;7?BsNg9 z!)1Mu6J&wCK+Os0RZiL6JpCSKvFu+}guiVf6C=JCvsr(BntG2-PbSk-ghBEXyN%!% zCc{gT5fFAZ-`yFUukLiKGS&|%<1JQ#NnmISf3q*~rBTC$B2O2WItLZ{AUL z?TLN}3sb!XrTYu9frlWdh-OYJu2=jN*E#hbM>quu?4|dt?Xo?7Za<_WZ=bT#NJ~XI zsNW$ZL3NXCw;naxLB@e1p~<5GF6%O+Jt4#ST##r)NN~m8ZwFI9qgxG%wtNArCjLcs zU6(*72O?VBIwkzNEVMIdiCQIoE?lw*ErsrO$P5fihgP=cCfm z%XsC&;m^~|!b&hy0ALk-HnwQc_)iay6!XR6TEY+W5P?0G6?6>O4Z{0U?*lG^8D?Jb zrrc8}5*@CgvDJF=!MOic$$gyplhnbhO}4-D%-0_eT!la!2+xl{;(gksv&1_Vsp|Dw2Np zAK>=$%LxO+gFTKxWJ!qK=%ZUdoUJ*WYGvwbQkZqu(mwH_Ht4@GA;D(AWm;U-dr=qB zH?4eyU=nG2&G-RQIp4ujqGr;zHE?iKB(I^xuicD2JawDmj_lD3`9D5+J38$dJFSoN1+^lTg`w3qD&1IKLcZal zd&SW3qbvBSI%kkb9!L2!f|JXs#V^&uP@EC4Q5Rm+D!m;CO1JeXmh>A?ekLu16M6g@@ZxxktPMMON@<2; z=z(4fmJc2gu5uy?Ou+=O7J6p*%T05lqUBsCkK?B;T0@21lGOR?q}@~#rKFu(F4r0# zd}6qZL-c^I+##y_RVoq)S!i4X$6HSfP&HZx1b7iD;V?xQcpng!kCb8<>{7R`$5{=T|s zfikp-YE}Cw?7|-Mi3W7HLceyZu*O!!t6Ue2n||+IMdPH{Ln`E~EpW}>bi2gdUvr10 zV|=^$aNynz z=EzniVEgTGS%*@u)4kO8^+mtwrMuP4--b2zYMfVpP=)FfjR}}fIbkZ2qtak~gJ66E zVp;F3^&3Lu!o)C5Z&kkEseJP7^ZR?dkmF*`(H$J|BGolKLwB}-GpS`v$957pi{0Y5#eLNW(pCkt2)I_ zK>hb8+9gKPPn>w9jNw>2E%C{O>%-Xf+2NHA-#pSmsmczA;4y!QH2Qy@2K>K2 zj{e3<)t+dj1Nv&KK@|~>i%jt14AdN7o!E12NVv4Xeu)* z1SYpUO}xpqatvl;uedU7FHs#;uvm^CYCqgBdF9I28_``FO1qKa&+hpqdeBDjbOd;W z{}S;;!N3QdLQ>>90h#ggT>`{G+{D?+v1?=e^fK_yDn`lldx64{zeU1!Zyn~J&$iNx zd#8;<@{KP3Q@7FUP4h|0LT%$4dN9HtJf{jPQhHK`P)zlC7;QDK>FLRR+Zbr3fD_4E zExCV88H8lnTx=8sAx>R?E0b;w0G6I<_rol)Cxs*?Qh<2cAG;DT*K*zvD}f^^86=;1cBSUquJwt7y*sDHTcW5z`|XiYhmCr_NQoYs z2It*E7S@11pSEDWwVOT&W|=@ZCE?`TQvFFd>-XH94_)@<{!U4FMYkUSCH~YbLP?rh zak?a9DxT(6ZWB~y z`j6iF$d?rnWmso8?Ky4RY_y=iwlnax=S*y4ROYyE1EUHR@?+?oTB2`PMj9+(maaQp zLm+rq>n()LPCpE`?At^!mi(!n*L-%xe*^0Q^;GIpd$aH_v6djP$)SBd3?1B79&^GW zRuc^W?=!oq5f1Y%JQK1CbFPBz#_NSt9X={9hmRzrDC<*nIn(;RX|;UM)Zb zno+c{HBEFS{iM?<(X4m4LtTeQj7Rl}NdkmC@F`^oQj#!;uO07E;Tj9(CyaLo7{wns zxH5y+GD~6H4`xN?d|%FjF)70w8A(9Q5Z zn-Pb#1W&$t{FBccJw8*C7Mk1&7K)=k2fbB#80)$3aC_^?j69`J##Ze;#5?J=;Gg7x zHc)(-Q`y(>eUU^y?~3ho>_fWw>1k632dlSMzL)wwUvgw8R~}f_Qd1@S36Lhy*O1Q3 zL+Tix1Pp@7uS_+phwDyD^`s51GSazh+e3{j#or_P(v7)chJ%-}HP=_UJrgAzD8Mxy z*-T)m{x_LGV@=H+lxH7qxQ7YEzh7V-2&lP&i1bBU9bTw6Q5NbN%BWI zi4<~u=q7Yz`z^odR+$w5pxI1+NQwl@9gQWnCXuX zk1W0`>D+O7s>+Z6ulJ23vIKf#5+b=6s@oPTMrb>qgO5|O`PLK|xWh*&W!i-2 zi4eP=9M@Nx=(Hw5@V5u#7vDT|)bCOA_)_+!x$pb_-cmzCT{qVH!%GFXdL9I&{vSc5 zHiUNNj&1P5zHisDd+S8i;TF;s@6^M^H(r1Kt13Pt81pG@Md4|eK>U-SXr^7DOtMTK zX2|c&!<-}1=*-1(Hb?zpz1r`QS26F(5a(D>f+H9BLW|VJ}$^S%X*L;+c>&8 zqKGg478BKaFPNlsk%xRH&6nW4+@A87Pwf&p)#@rOeZexlQ+J=+xv@XYPBD0-VlndP z8d2w#)yrxHP$X_0r0^VlwAAk!QXizb;``nUV1M0cB-;+bg_Z}i)itvwKl`G3)ACV! zm;+CvDh1(p?U~ew`%S#}4dVbrm!7{&vD5_|)0c)7#O*T|QGZPCF(mPrsVV!jR3QjX zK~x9_~{nhG>*bV517zN}tU!|5SSOPo}4o-yLpU>bH{%w^u=k4YfYhXP)!5Weyf{Il74$8= zPXha{AxQw3w04}zKmx#z$Pw#E^s$Q+2|4%|LI$itadCiO0xA4L2mF^8l%&GztARgf zAnOD4XCn>u0w4izS)3R@x4h9Y0tL;XNgwhy7dhLWHt7$!yGzr4FzYU-QL5o!n8?2D zcX}KY(y=TwU~t1(I?1tdwn=^JyX0dZ0Zv^ZXWnt=q9pC5l5NPw1`@27d zG~P=vsV+kx5bADXl4pvx!q7sS8Q-VG|7g|Tg#h4RmB$y2z-kc^Y`E0afmT`~Wu}2r zhhPy5Hm@zx!L)&@i^kleESun$|E2UHJ5|8{Hb*++e~su}LjarWl~EN8JMlLGT;w}Y z58?*uUG%qn*u0j&)5&JgLaM7KCd(HSU*_qq{1G|GpXFhyk@CU=EL5%n8MDQXuaQ0G z_xt6W4czG`YTVvG+bpKx%{WAyN~-w&3EJKbj&u7Hv{`L_vh=IJ9C{t5gT4BRamj(} z+C+meS>KJ4XtIrj79M(_OIFWWwgZfI;yz!FGM=0f36;nh$3;3zAFMT zFtR{*$1*})w~+)P#-s^wJ^Xx}2Kz!>YRw2Sq`k^r_cD~2+FCx$_K%3HYtNrfyX()2 zH8L&a;o*zZJ~ML$u6)06;@AC7dM(8xhL?IQg(khD4sqBC#6l}c%R|mtO#9X>I`Ob{ zt>%oLLA1m6_s8mw`IN$l6uv;`K%N=WB|UyHT4Tj1!kdc1aVh}tE~=nLYwdr?3|06h zy4tIJ9N#8nQ$C6}f0oJ~%Q^PPpLFZp%r}9@zoCru zqA@XITqu9rU3VG&jVYS)B5eCBS7ra8QWe-ZiK_PZVOccx4}28o%x*exvEN|nRM(V1 z3$YuLFfMLbqXc7_gvBS$IlvVw(6>BAtO2K8 zlf}|zpl*;pzAwTQN-$fh=bfJU15?0tm@bO#s8k5h1AE!?fznPhWvjWMSV3VthuGZZ z(7Jwdj+R*uY5?>=)BT&WW$!0XpPK>8MyEyRH28!IIWSL_hJhN{F4)+dy1j0DUb7|N z-HCYmO^YSFCGv9kt!Z?|OH2ew$hm2j({-n!J*X!!_<+CfMGtVfAnD-w?%cQr$s(-l zF9h$1cHq?P+<#!nMTld6o$e3gHT?+e)-&&KnvHbJcN=Bchrd*DU? zJp}G+Vi#jWS;UF$LI+WNt!c5F(7%K$Q{nUTTuEtG+)7?i#ovviv6>mS$_&4yWbC<9 zbJM#Jd`F#6R8n1`Z_iVdxklE`cwuh=XWPkIZKTu5$G?#7ri39-_n5Y<7adGRNVE0) zOyB{`*izau^KQ-U{_fQ8?`Qq+B3tS0`;*_qajJB+5h@j%tn9d8N2pwyE$e)?dvRQ( za8mQ6vWjlDKURZ_`=S(fyV#6wrL&oCztBVTp&VH*bGqBFgJ2tPvp>RjweGVnu z!T-qYq&mZ5!gUuZ>U_r>J!9RZ8_}tA?+|o*m1Z@ zKw+!s9henI-WN4azZxrlGc?eYd{gpKylpB%%;>>vS)HSpR0!Ex9ck(~eFR9TuZnMH z&awQ8vw>5<^suK$8Rc=a$hPW_M^HM+?V zT{P?0)hm1s+wt(D#GJ(JxAI}(jIv*ypdWu)#u$EWY~=az<>3N50Kd!P4{7Y>rkDaH z166c{18_apoy3W%u=*t%|0J5(7R#38TR0lIy1GppmJ1kD+`eUkth*t2s+Aaem!dbA zAL%&}EaT^T?ESNy(=Y!iZii^l)`Yif@hw`^b`?zxD>jR%(72M$Psg>zrUYuGvj#_` z$(k_mxkwVDa1KC2$k!(*lx;z2iJq0}pZo%Ql3!1aNdFn(dFU*C7C<^)q_VLn-R+JW zFnMuJ>$1|)+~B#mE+H3tp|c#{Mj1+C+EGl1l!0E8EqTM!I{9*fr!I8yY6h*Lx}?&; zoOKij+#Zx>dq~P*sCH2yS_zJG(e;3bTOP#y^Ygb6?0pS3B{Jtw6C&l z`D|ZC*GQ2!UJPZurQ+s7?T#Oef#m#tBRI3i2p_lEVH_mhA9L@TRpf4th&`y60MenTlI8Djzq-YyJg(ZyN^K_G zqIv9KU}=|_^0cZI0=WVP6RCWF&Gr_S5)t@4QcZ=qgVEUbv#fi(O|4^MkB^_LF`ATL z4*j&0swB-*gUIZyzM&W!dZ@pS*Vx~S6zgp|8 z`;k!ud@S%ByrW^B^7MWRq;5qo1Qd&W@cX@il><$M2wmUOd&`Q=woC^d_mBJ{G=i|UtG=7ReJ_&z5a#b8gmuY=(s&tR5|ccmPAPhEld zOzABqYgxeVT3e&hphRLZyBh>T&I(uo;ZvlWwYXZpj9yx=7D3T+83A;7m2+#E2I|f<&&8a#q-z(R| zHlx^2lD|;8xLwg!;H8M3K-(rz>pH#k5@#b6-vgJTigP+uqurWIgOxvhywtMH~%dN#iLrTU95pfRw&ZWj4o zPjT{CvGsTOmjs)0^RU~uO;nXQm5!Zo%yHN~sXj%q+zMkSMN9N&rxH(Eyi1zvE3L{m zwm36> zB5o3IfA(YO3(%ja7k#18)Cf4FURNxL;nr}NF>|yruXO7Y@}Rmz0qub^)YHi=@3-O3 zzmYhrN9*zV7IVgqXSStg9XD8il@>UV$JT66WTr0*tT(kSlrqi>w4B+lKyrviX|M?cHE^ zjzB$|kM9XEd!qP8P3OU*h}zvH*Y?5Hkx?8RALq7S58Xa)QJ z`5<1}vy{eINoNQ!LP-0%1-b!)H@E7@x3;)0KShT3IoF)M%BrsREC;FG&d>$#e#ZxH zQ;D#mAFaWU=9}&AWLuT`FFMW}Q?yM0b_aB?EgA57;dWrwXwpVcHV-h~YT{kidmOsI zWKe6yaL2)@|F`N=k=a+xxQ}&O5;w!2(j|Zx+?5Iyp&^X3Dq*LGi+QpY`gyQqD?K~( z*a~BtI_6|TK~v_RZ|hLW$NG!@i5lxC`sdxy+MI}BZbxPnPC^eDxsASl7{eYj2ohr= zbd{agQv+S=%Cs7N5&;*9vPXEX65euRZSPa^>*sg3+_v4enGvk*JZ3ayqOyJ6I!qUN z-?8_sTDViUy&D<(1XXMLj}NFm+`O`p4l2oJq&isOxDSq|k+DdGHwJM{?2A$7kpHCs zmmIOm+L%-B-x!l6T78SZd?OFJfuda|R|V*ySffq`Dxz)#)zO3OE~A>$a+yGu z-<5m+cu1_2h7FJJYcR;HuBXYeOKVTG+H)4vC!LZa8$R0qD$n{dR$nrReb*3k>ig$> zKb~KBWEO#`K9^ZcE<);zS$)&_hu=)};ZyfNWyo^BUCwxamF!0#uO+bdeYcvoUMfN* z0S^S**Kgt0T@UVibPgRlrL+9rZuhZ#uyP!aZXj2-Ue@a_bDUdy7^C?|Kc|}Z(MFSM za0;*^jVUzed_l|y`qbe^Pco14<3CMj?h#C4lUJa`LQ(eqTRtlZW$5PYF{!( zNLZPUVd<7MLuWaEnh}iAX$+pRr2PVlQD}cty?Q7T+OC*amHFa0byCgoG6{fZ^_WFV zoCXO*_Ll@oA9{1u0mOvCHM>A*i7r=av$^gM4;kM*ct~CP=_P~`LN?o8bWi`$Jz7I8 z!m0MqSM7zG!|wly$9)4Ox0N`RI(`D4Bp)tv(YuN{XU~02M=+XOM5n(HUea?C!$l+I zl=p|zkpJE~P*?=HETDcIf`W1&1C(LvAmJ=Oz1g}JUhyxntCQ;nsjUm-#c9C1s-oLP z2}W2ynA!5e<5|m2S;guFrtSx!qgv?hND7`R2pAnMn>?D8)~+8mB2h&ZmmrN;%<(#t z0nxq90AfzP+&A4|`$?GXxwxapb{%s1hGTPU>d&Fu-vRsULLq4?yLz7b{Zl^(1Y9NX z?tNj{5bvkPXQ9bDO;Q0Qu6IUIcC_8Y1X*-#y@yJGHrC{)_4dub5FrKI`7dqpIdH9h@9K;L3riD~N(x7WtAJis{42vZ{t+_5q3&Nk4NK|v zZuiyX*Z?w8nIG0Bg**-P!^Z7^G6MBc$W zgS@21k96JcA9>3?!!M7&f-t(V_GVNMHoPzm-1jq~_ea^ur@P997XG9Egds=cpe63m zcX6()#bKXl)=7fkm*Tu1jDohuqDIeJeCBFt0K?k~y%tfkVG}iwfJxa8phM0Q8S0sL zjX^%^X#+uYDNm*DXD}NwUM!R0JpU#j!S(C%qsm_ z4lSKoJV>8~CR^2&+rcFZd!R-it6#9k0_j6CLa96Uzek3SgBhe(G*DBn4^hhJC z2^p4&RyoG4(OC;f-i*E~yFu{tFPHh%l87AHbPh?I!C8qI$x?aW=mD#*LhW|bW+$!| zGKS}hWp}DN6+H2X4r@{orxM6nw9eDfaLxhQ!o^H~A(k6p5NzG?m9GgaI8-%}LLwRM zE(bWR*ms=<^AeRNkZ>!KWikESf~5I!;k>2?*kPyNf36&sY^f25f{k2Olpsvi{Dm;i z9n7rkfT8ORYKfO7VE$rr0OQv8ixf|*oybT7IQIZo zOy>85{ZPd)%LiYQ;J<$NDhu^3IMpA|0Zj;9hSxAbf$T2whJ0~c_I57t?nudf;C+jH0ArtFS*C&M1hV>fC`iah7oKIp^qwhrQ{UobDU=z4D-05o z$G^|zwt-orpgB~JC3%op*IaV93QLwu1^joH7MnsHPTgv8q`ciE&teHu+-1h9uRENmMPsl+4Y1{ zz+5q+(nxbWW+GRQp{GiD+gvEc@LkDVgBGP|BKZn$qHq{@AEqQI@YWD;-X6F#SiCgK zl>oXGH!sZ)xQzBJq$@V2NLeOhq>%vrvNF2B6}jj=Q@ZE9Cp0qU;!{hyYB63!Bg*+p zGD}?_g$$3=if)^FXN~P$##QYUCMdmkzxg>(B!~8ZntJFYgOKz6VtDgd@yv7eKBzg0 z_Y3c4R2|;ry&s-$RW1qKOVs2(|4G?5JKcevlX^;kmj@38O!jrTvTrKJ6%Hp*#v^q~ zmvl;LZutrgc{;pu7hQD#+w;+(sSm6IeqQN~IaeWiU*xn}`&I>yJR_+07;(-%|_T`S%5W1X5GzIWl806j{u7F5CI5{wF*(taT zor%Mudfd_4;n-5cP*fv}N%|!Ng?KoK0$vsKWxNlet%3yiLvPk!uwxLRMAy#*{X@No z*`=6EO>BVo-uEvP(piSRzgdlWzA=e0>vVF;-K5S4yQkzphLk&(4IXVy2qUq9cHWVn z37jfWq|}txkXpln1#}bpl z%tReRc%@%cH(PK%MrVh8iGgrKx6b#Ev@_e;ao}7(FYQ}{ALDmQo`DCMwLo47gEB$; zoZ4^aTY6s@O!PBRfxyBQw>)Geyw&RInirFBZAaEkdtL2@8fX@>=+j9hz*xgNpCczt z2JANLfes`pTm%KYdPe|%14exBFNB4#VUE{Gm!Uy4Z6%vnZo?gzeQR{6uv$6~`Foth zf5-m11;NJvzXGulWf0ab8#cX-wr%c9R9Q&S6bEx_J>l=0p51zps z?mT!e34zM-Hb8va?+_@@wfw9QF{DcXLCtsBzx6gg;YHgNyWy1U{-+6;k|*Gjh`8Al z&!Ut2?$7OuRX^*9l_auF#AVPOJ@6oG=|)vU3!TO=K&!sYKueI)QRm56m_^JQ6r}iE zB1<*KsV8Z@dE3Fw4k9iao#<I00t1y52!E@{lIPh{P>z< z@Eu^!nMPoB!~N>l{pTJx<~t5XI#+8+y~Dz`$zx2)n*eH|_@X&DCa3Q_kf&??@F)xJ z0WvFs+mn<7SD@XjDy&4M$yUSR(Cxde8JAp;gT>f;@ypPE686iC#x_je4^%(zJ~9aSOT_2~tfqO@2rZ1DOHpxoI68wB` z{$!OKZ)A zu?;o=VNy7nu4m9jLejQU#Y0q%(4_I+yi#l#(63pbKJ`Vu5~MIVtWKy1#VPpnUC8}b z7NhMN|E6D+jWq)IK`3Y^?#)EvS@fb~(8WzfzryTskS8iWql>^6G zYp3d3L5i9JC4vGK$S3yTB3D>|bvQn$Y7w^tYrRvcL)v1JqzbewRmZeGXw$!PDKpsN zj2-b)+Lg(nDHuAvYuz?pweq|8?f2Q8>CfC@iC4M7TFqz{D{)xkRj5(hYTK+rLG!*M zw1VxBZ&1Fa2-#PPl*ie3A!c&`%zE;27W=v4)b^NJwDtLeI+tYU&J9Va>z(mreM~qR zj~`-2-SVb>TEB}j0-CDY7U~Yu6&j`FFtN7gZ%zx+bsp5WA|51IUF4GPJjAVFd@4To zxUX%SzO&aG50Q6ORMo(fu=~if&qr0o*vH>((~#>qZQ?#ERaM_#BNM32*7Rh5Y>mIh zCA5h@9ADpUw+5F)6+{PolHrFu%Z|0zazfK5+X2yQ9kW4`Or6*IvC zAZP~$XMj#Bhu1-==O!5g%UWd~`gZ9ZkQ0N(oTVNwAO^(2D=^m$1;7XZHZlYTB8bLw3Pwp7^T@()R1p`Qc@{)JGI z<|uX)`-$KFD<%403Df_V1~=9DecgokY&cgo5Be`;bkE}640LK<7(~baTll2%XFCB2 zJA?e^&u%-E=t#)I)oMe(SxTbElAbqf-7Ih1Us`mqp}Xbk@wLQV&$DmH4-gq1Gd>gY z*(n7GjCD;Z!%Jotb!dk+k9pHUJhhH|2W@bVt*gswe2!w#!8Z;rEQ(j}1|8;v9#iqr5U$--bA^6Q&@4W?pcS`I*RXLP|*3VzdsV4x=4l#eM2d@sX9x^7XT^@r7ol};b0@-5aCkk69|%g10>coki70Mvf3 zD`kyU9QdOk-+^Re>7kq(7C##sdQDWoFLjJWZ5<0M(>xlN=}$m;=WR3JQ}?9}JGiT% z-%9U~0J{*BpUlWnq{+-Yw)A^b8F+F851@}SKl?dvl zYcC9(#*d9{_H*kp4#dT`d{q)|B;Vvg>^hg;yjKLM0N&;MZ8us|7?FkOs03a)0&XJpRr86GL4Q zArk;7`T>WWcW03a_3V0#1hrWPW`dPoqhnI9PiwB0^);2RPY1K*Xx&)TPb+x`1pXO~ zLxL#z4iqjv>3^1E(wVjft8A_Q)#hL@rjxe?jGhMY^f!!S4`%1On(2z(Ryw3Vsl$Zb zo|X>M8oVW^U|=%&!1b&AtNMh*I>BM5^%?+bvO;}xMZ-OIYbjf@dD>}qcxSM1H^`2C zJCiP|OcD2SYqE)V#eRQ#-mJN9?!{5<>pq+Is}~I~E+b;vuR~}dqSnj6GE+d#Q!Gdh zx+igt0t;&xu*XFQ{Gob&01)dJ2nsl(-gZv>R1IDUI}+u!=h^N1v#_Fm%3ts%;72xh zMZU9!x|F;UvT(6t;V-87zRPOc`j24AWKykD-p;7MDD(8byFF1otY_JA?W=*p^L*wx zJ)bl0l&OmClP2pq;q8%F`JC7sx;>JxAH`E#%=epe&z!S+v8q}T7~RT^>&wRvmWVBC4Dz&_-qT0kBWjU%%{V4o)`$c zEAg2Nm@`@}H9Z9P?G! zpny@o_kUuq$*CtbRMyd(a;_olyKP~8&V98K-Ct{z*416O9Y z(e=|k#w0$cRLMEQjn1Txqt15bLA5W{<~7y-2-O{*>MNYaX;3rC7vD2J8~7EVH?LPl zY5@4=TRRKI7zP$C?i=0cp0qqi*!rB;BNQBS4=;jFPd_JjT)&%AO(7(=o-Ina7gR+c z{lUsbv`hF+cLb#7=l|M7jXET5-B9zqoH^~O`lIPsPNb6UY?(3kQ|MO?yAYu!CkZkU zGBZj!yf6m5*}|`Y+lNui;mB+q)~hL|setl-Q(e3n0zmFpori{_;gAxSby&K`)P zpDV_`d&s)0*|5s{+z9#1q^kVW*ALd?NRgXTy{sYh@lPa!bRmvFld}Yw5SjetDk>yv z`%f5Pk7w^jv%;KKbakGL9@OiwHhl4ldf6EI+ey3F){KVT&R*2oh0NzA&}UD}-C$%9tNGNKs#M@{T@{j9L-BsgrSB+0oL!wl zcd|;DZ+pp^u?64_fGyEW>$pbl*?WSO8&O5Xg#3d*v=x=AZqF z4(&SlJV-Ay-qq4S66U|aaCY)+&UES+IX^$Ulz{?JI*8{#yxUmxamU3f38g(8zJ}+% z<(|CXBc2Wztne9MQ@g~dP&$A1MwUNYEU7|!QT%(AXu2+P^qgVY^^_3_)))T~3EJ@E z1fEa_Kbc)xL9Ksx0bL)Iv}KH4TJ;LL_`^6nw#)c-AHBIg?IFF~v>$etm-!pFjlSJu z34pcveMX$1%(HE-os)E**NTBt0ZNuXYMb|?&Pp8w%YQW%i&o7g^_$<$s%X0W=bit& z^}Omrtj&sa>>_h>!OjsNm$@zuB08$*hRp zFlcP>e(zs?fK9vOBX3|;em@mPd$gxp6kl#=5dUrHdG}~0@6jWD$NHv*sm7gOiv@t& zWr)YwQV%>rg>9m6v{e%i?L0Hgy)rIZxL@OXX>|YT;8)Hr+LB8)9#^GzXSQF}J|4NH zZP@jy^a+DKt9G%ow4<44VT42F9qr{5-uDmb<7267KP0Mgs@+VG)OT}{G;|0ezmvW_ zyO5_76P4#UU!XQG=re0ZlU+>RM^P;RG+fqTOm`7F2GNssDZL4=3(MZ67h|p!2X87@ zOg?>SO0fZB1#Bc0lUT3!b7MnGM@u3WID4}}s&Mel~(V=p;^2|bKtIC3! zEhiSqm-kw*z)9uq*c@vs)KtoC#CZ;o*bKb?^*+0 zElBX#e2C}+2uvVtlD&8jVO>SK6E8=4>lidb>>_c_EhEl^NJ^lq;b(xbz%_ZyKzJ{| zbOu~5Jc0jVAo#kz1vbkF2Vg~>3yQkE0*!vbv3FO1O=c8WLH&SZ7TUr4cwQA4*G(M> zij_zA5Flh3l@IRKpG*

Xra&4<9hQ`#m}yyaNb9NphMEL1F*PApqbQ{{I?+lx7#S znxzX(?^pK#Z$nbX`xJ|X1N6C<+vaiqENW$0s9V88K-VnLRD#oRGWK3J-wGZuHvPx= zXEBrfmqswSq6v`N7V0O?|5x!&0oo=+qo7)Q`^b2UZ)qB4G4Wza$!@-}w!*#Qtp;e>qv=2nhK?Ce zyzjRaWu#^iu=T=gtxkFO6zpjj3X|Hvo0_)$?t({7Kbb1Zll{^Qlqe0wb%3(;Cz&dT zUP3z^Qi+iCfFyt;>LEj}9?|$nEbuD^r$PeXDve`#Xq5IBu@1#gvX4Nr}_5%s~vYhP}!j7$4D zvyZQRV7=aVuI%_1Vzu6YPKfs*?Y;oOR*0&}v99{Xk{CE2+vLPMswU=ezPo>LOPz|S zc;X%D?K0e&yCM9}IdLf)(?Y3C#a-t0Y8dm|e}v&lgXsiXQiOq_PZkVCIE|^FA6b-s7LK4!fn8c($%1W175~)7lC)v?*5u zvq2F{z+`4Xd_sxo((EHCj^lBW8uaMLM5mn+!(vMzuY!V{7yD=mtdo9OWJa;6n4|?; z-Fe#UdP!=?AHArYdU)qSwT;d(F>3o+~ZqH&7E+vSeYBRBh!KCKb= ze#CdvjGIf7SpQNUe&@GodjP9Q-}~1>UkUzpi#kC;TBNx0oD_qH{y&ny)1nel13hpLgfB=m2K_wIj0S@Zty1T3S}WG>xCalgAHh~l;& zr~1!I>5#y%0%95t&8oPfnqcm)UqRbgo^vx^+1G4WCvFG|8AQj1eQm^LNzN?llw45u z+%gonVYcIC?Uk2`@)*-AYBD`ETI&BbjbT}oo zeXoaqKX}q#_>|RUc6H;ujuXc}CW^*FeqxUIfxM4JW8vOIEI--@Vw)UWlq`(@fDk$t z->BH9jIKmu*OshG5cYDoGUY7Nk=G_XH8Q=fG+IvPd^}cVup$IBX5J9Ob zy(?Aeh;#v^_ed`h0!j%G*a*_Av?vHjlir(j=>j5EkkCVsP67e}LcV8ze{`+0PP8W1a5iBjc$jwcME$rz2kP3# zzu%`_8lN#3n19wS;SzEQkG-4Q zRD2|~^3@a9(Q|@rDKO96yMQO5+n&YTdOL_0bdjWog=ODtR!1 zXwjAxVHlgg%#yd4E~&z^h_GTzT34y!?SbwYQ$^9H&Z)(11#ygY*mw7MV&U*d`++Mn zw6BBh3Ys1vTE*h26-B0dp2UQkm5M%m@g{a-!xYNUPzcms{&G(zeq2dp*;(QU78qTD z-`UC#zG3Z>hY=dN;zRb3I2spU?x_38!CS$l<%PzTR~}SUNy}U@`2vllv!w}4m#e_m z&;kB77HQO~e{+jSk@a-i;8`m_plUQt4A7R&wa8ygt|9Y>v>F2z_U7C7bws}|7MDDwAH*aI^(nrN# z`L(Azl4HHfXe6`GD|{ZoS&>@!p5=caOo)j70mEPWM$EJ6fLSmYP~; zgvbl=xIZqhsJ%tF$M@Hp74<*Z^8fUTNx%Ofeem@KYa<{7!5y^?mI9Fd}eqsYy- zL7L344xpJjKX0~BM4Xq!2I=cN)SHX)G%4Y58|5gtaK+bLy|Q}JrmfsA9lD4<+ZBu3 znn5OD+F*&{d%)&q8#)FxyY6{R(ggFVTz6Zd#==?ak)bRa`P| zas6DUW7fd`)OS4TNoRVwsjsS1I_uhVvPmaD+eMQy(=K~g^=uBt%{PyP9zSj|Xj?M& zt#UW=&p7?aTSgag9NC8Riy-J=Xuv3NWvu*r-IDF(dEoml@vgw#REb+hw?039(ft_Z z!Wz%AOZc8EtjDf#ExPckmQkOzx}bn1ozJjipp*#5zI;>rd#Qw)RWYVZ)Uz?7AU=ek zJ|o(u=2+$Oe*34$-_!`9_hiBlu=G zhy99QB+I(O(wV6*AEebPdZ;I>WI8mP7>AbKej;wgI>H(-93L@_R44J z|Klc-_@D2hFNs|7;^#&Wk}C9K3cyGe<}24MW+q;ENZEc#Su(CHn(+B}_7#;gPsRu2 zE}=W`0Gm&+P~xo_D50XlA8dU~{43LO+wQ*ZJi9am=@?+EFg+uf94B=Ui293GDCCIoZ`Yo!#NL--StKKXshVaq$M1*e<_@J6{mm-YtzSk-95HEe!CiF zsW14nPHX+ih*@AjdiaiIgX3gnZ)^VGBn)(pKL!#O9Zcpex~>Dt608_t8>l`#dF z_g_^alhS`M{Q*5+hksl?hWJEhrDBzfc8h*n;iTjpajh%PJ%6TFaj4jezy0IHzLRn8 zn=)x9oie*U>#E7br=Ll(O)2l(dGW5WmNmmN$&3(ye^HFDoa~Zb6oBsqPBMD-@#gq2 z279Mt*6+GzKV;Cey+=s5<&~}A_^Ab0JzfoDl!T(4m;IvPOaq#@HHkr(C0lO@G&= z+q#GJ#~55F)c5;Gp|7OBx3AtD+E3Xw6`#WH>7V?b04x33;GUDH#BV9O#ZX`&O93f$ zncw)dS^a&Xp;BtFaZ`j&$Rd=V`R%88V~Xw9R-@>PPXlG!0H-2@D~DLlHxyA!ZMx!9 zk1+l0ye1eI;>5K0THQ{D;(?hm>xk{Ro|;q1VA<~uM3MdbF)%gaUB*FJJtw_%k!WTK zK2s0uDmEhpm0t`}l*!v3#Yk)Z;#{YZJGS~|F z`Z6s6v<=l`LMHsFRz<%b~w5&bPicFcSVRjO^V zNpHV+-2IG>5gwl!uu0S-7(CwjSF_>Z;%_Ix*i=3+d-QxO2^cd9KK(argSzBG9qe%3 zpXodRk(@Xq;LX5Spz0DfBmY;={;xk7_&@T} z;5|hJIKdOqO1(>0x6g~ZM?BC&12&WU9!`jvS6)2b5;bQnkZS}1?H7%IGC$#hHKK-V z7)vmA>$Lp3JVA4@TCal3O$7);|8}iR9s(!jzv?TT*@%*BCmMd@J_FiVmSWDC z!Y@<^Qd2%68sEm>E10?->1e6NM&)laQt9KWY~-&0XEU3QeC!g2E?S<)b1i57hojox z>&EY~3iBCfZl9RdKb>KvinPw;h$Te6-xI6oW82*$HhCd2Dfl>0)qI~5{{pnjq4vQq z1ueMk(z4$i^Wo3HaO)lfPJ15M^wVIVl7sC z(ad45kg6*pc8fdClyCf#4SP3_Y^%ACvrTji9(jU zOXdq<_H(>IhVBr zHNo%QJXbX3L+Nf;`ZW`BKWIzz{Ytf-Ozo6@W$>swge&=~6aVCs1bg>Qa~1Sr{@9p7 zYJQ-?pzN3yo9_v>?*#EO{{EjeV-o+JzAKN1^gp(%54*x;KG*=a7Py^wJt20HjRBaq z{RR0jg{&bCChRl{tVL9@byn%)FAK*q1NMR&x1%v- zNJ9_(hPl?Jk&OC;7o^2@E6Psa_}D%@muoe%AeXX_f399r{Ng*rQ*KOp&d*NrgU`5G4ZFXf3#EK3b_*+*C^|7`q@LbOy=`Bx%i{JSidv| z-V@O|BbpxpDcdfrDe1dGq$xGO1W{tvV7mRrFsThdqRhdyx-{Fb6x+!F{%{^4B_QsXN!%dSb!s4D%@p_ttH)Uv@eP7#9;<<3A zU4a$Zr{6L_*o5X-ebtR^naTNQ(|>fq@t+9FCZX4k4ZnRg$KQQw9u~7mT@k%}n$NAK zw7Uo3JWf{<8n7z+ZOrZ~W$sGQrg5n!NBV#d&u-;O$LspL7Vj9U(^maiRO2AxR44BZ z{_K2ytvz2jihr?mM;^L5w-m)8554lceX)IoT~CNc>3MUD+1&}7tqqW$xVpe4_08c- znJrX(>_;=G>i@wQYm(ru+KAXJIc@PlRxn({F=T zHjz4@*io&u{It5h&eh2*52v%$h<5K=xzBYG<_-(_2Iuv^rm_U}r~;IF-Y7Yo5G%c> z0kF<~MHIH)P6n+zP=wRY003Y=#SSMjZf|3oKGa1~q_B!)YS$H0$vxySV|^dh$7lR{ zuA*Y{h_ep4B>tyoX?7Sx8Cf4_tz`8T`#Vz)|E2`NvS-@ezY36-s#_di?S3&fewjt2L2BR~F;rz)LG=}U%GUQ46h&_8 z%vAZvNw=B%HTh4idXl|R${Cx1fY;t*d!HsW8D&$5DfcqWGbQdyCOI+eRq?DxZ zA{S2YeW8wUu(#LZk{X$Ou=n$QlHU&gbzK|*@N}u|Z%lcM-R>fT+&I&S`<+vgB}lEp z!u^z2O)cSdHtIa}O)vYL(j-2Ny?Z4Ap`9G1h*jv`Y$|e-4X!CdZ=-cD+xOyk1>V4X z+y#djak3Fw2J}-&LqjUm*T`?E@mAz>tfkMG%rtKKIeRf;wPm>9KXS0$zPcO(3DcdH z%yQ&Q(flPMmOQ;-`529MOj46~Z`Axy0?Ls$hWyE2PP$ffpNblw;JGA*DW3Ffj<<;q zaZa(zO4oKcq;cGnY6Y9p5^gdRs&MZ8q^IF)wrnr!Y>hra0-jTjL)`{BLHO+AWfG zC^P8e`GTYYjV9=Sz=Q3J3a9_4(}0l?lynNWO}^ z5ZbJCY)sY(;So$K>lI)5@K)Fv&P};nFlS@RT>rGCbB}AqQt8GbUP%?ec7i0pD$AV- z{OjrNE)nCZ{gv8;!{y#1eU_^$2}ZON;D&bM`L2K_!IZNFX2uAh5gsOTPCa>b`*5nZ&YLTQ(eDQ8iD?dI&9qqh1e~idl*}E zBD{}ND*;~31*L1K32)aP{MIfOR+fioVG^`#rkTYX_8stbz3&Ro(bhg#rB#a6#gJTU%0G(?Pl0OnfF>&IgjMA+)iL@;4q?ck1qkirW z|ML5$x~c%%4GNX-_dn-pr>v;)ftdJ$(9G zmbjJ{+@42`seZdIxx04wEPIMX4COpY8i#V4B;~ex(@7u9NgU(6g`}zky&Z*--I)tF zdricD+w_6;;f*;q`_~M#Glmf++vTMi_d`SwGTln`8X-uYd<0i_qtbQZzd;oJG_w5= zAFlN-26APs^#sVEd`hxEi1I~wO_GNG`!ZtU^a$qxPUdn&F;K9e>EQm2SJ-PD7kPF#0lt?PM5K5m6`~mL+W5)V) z$OKV~k4c?ULg;S+hSO5+a>^5Mp9PyOk#g@HSC9Q7pq24qiSwIX#+!IU?fvhpjYT~~ zl1_ZeGW52~5(g4=RJdOsSS7c}Dm=-{=*srz?NDzUH_-tEf`5)_kfN)85{7?H_W9_f z>%3R!{PL!M>qxQiRy{t9`K^8w-GF|y*dshk4!xO76i*j0J#in`5fG)!W5M|qe+8r! zLR&{*BD*5)ojREu0{E@U0bGyv)Lq=vIdrzoG1sLfA}a`kdKVNC7-;J`m|t#VI%_ui z)n=3*{viAr&F4bh*Y1<&Gk;lg+MDBDaq5$1wfIahfZKVGtV*gdtLVfxK5qe0sjGLf zE98G;>KGWU)!rT3eK&mKgTDH7QRC-lMICc(r5W2d9+R>#cCgg#?U0=$kYP_(QLzn) zk*I9RiE`T+5u_H+!jD?%@KP)2Y$4HnQNG8i*MnE)eIuizb)5rBGQIKg`KhnI>V$VD zz=1%W=;Pn7hzMEzI_|u&HM+`4<7$dBrLeqc02+bp9WoCa$ zN)P4LM~&-XjmkFetBJGb8erMa#2)Fr?GS=lP{w*g$q;(R%xfZO9EF5CDw5qBj$q@!ktf=TYOqC^<}UdiTOy^DJSek7|z)m zH{Dr@y!p$cYUafh&FG0!+f2r*(kLZheKx$;HPh?u?f3^2yP57?)@3lfo3<6&=A6-I z#JO)#82buZPmxwMOq+Y5y6DD9M~&QAq^fVMtA5?w{f;==<_TzV=7P@QJ}VQ(tYalM??=<3Z^;?Qo&b<(BgNbI9O@#iSo=PF#y;` zd!d}0OVn)wVzCW7)=#$4a>MKjB7ZLcMNDAq@Yf2F)p&6krVPYP6>^M{6I3KFh{aVP ze-&iuUpPWIv7MdUG5=w$!a*zVbCUnf2LS)|OZ=Cv0Q~>`JH+9MH|C$5oN|&81#}oo zUyKtZ@0e?Lk>7cs!d^_amID&bdEZ`$A))vboS@P>C<8oqrqW`jJE?DFkVRKQ+m~UG zv2d*`siXJ%=pfUZi+*Fs`5z)PDjH3?Co+no+mp*j=qpbV3^7XYIvIr}vsiw*KOyeJ zX>CRJFi#FOm)bT<%k`^2(>bAD=#dFAbbvzEpdOz6ABcSgm`9s$(TB<%1wF8IwPb<- z+SGm_^5o4>+@0K`-=#yvG;@?} zUlr&}uDJX?*tT2Y^Y>vkzx+sW$5cf@Z)IHQCyaKZI}(d+5L(%YA4xkIh3z?LhkI;k zE=AR*+=GmI1axj{|@s)7Ca{)^Pl!oa*mv{idaqgL+=swTR7@jQrkLtMh8oRH|1?XriV6DZu#hpp;l{w*##m?UmCHJEsgM2w@k0E*U3wb%;**XufQ% ze>eEXKgd<8K%egeS%g{P-F?AKPwlnqZlxRU9mNWn>$TP+mv2@mwc5zo;;r`r5u z$KU62C!W}YXfWAZs7sLnmyn|Pv3xV&nT6v*{iK?JM%cqp4nA-GE>%%l-`X{Sp*X-Bh>Qf z!*@BGur0-VV^ZtRW5**(FO2WlyS`3=D4e8N8+l23v5aS+^7RiN!&YD`S!G=VzogDG z$ZpmA1m!OgRLCyrF$&H}iUh7hY$JH>_86F6lJKZ&Qr-y4QJzg}fwueclw2+M7orynuu$@05)pVLOIIHn-Ej1OOamhidz z+J%IHAG5IDU(y^aJsy`bl@kD7RAMNq#-713ihJ)4w>O~}a+vpBk=9~e%XZ*NKu#6k z(PzrBl$8}%6w7^5f7(EzHt_A>g6){@QvEmp^&*8#9~`KOQxu!(;6(D_auWW!hV!AWZ4x$Sl*b+5k)ilZ*pyzXasB6{+*Jx>T>c=Dlb+OqFd zW{C!A@J9E-5c>EX^}}rFD%Px8ctObWxv^N#_0^LO zYzho*Jaa-@!W?>lWCgoCJ60Lo6hF{b!VJm)j#Ahh>g~X9=YR;^r~Xk?<9pN|h88;G zh{vvC7E^F6N5CQ39PT{UzIb)8EMnX zrrmkNq4XtqyhC=XL%nN-D78u?MoSqp>ah1x-&I&|wDT zhlhy`v1P+FborN{29FnZ-SDR;4HJJ4$nmWcJf|D~)0oMY;*3X_ZEi zB^{sR-#6kFU;U{VXxctf&pQU&r-T}1q@g_H^?i3O_0iU^e1ZadTB_bP0Kg5HNeAyQ z?{+b5Y+q!S_jypN?;g+8;;YgSA*=f-&3?>kPx&MRg$nlBE~x>KLDHxcPLRT07~y1k ztZ$m(6s8oJB6~d-yS_-GeM0BUVwIXW#UJ1N;r;T3isSXZMv@HAy!i@|D}p6L`C~*y zL?XsCG?;xSTLJH1ig21o08sTQpV&ETXRWVo7cZMahTivM=X=)v*oXe$z8F$w)!Y1% zFTf*OI8tAkpcSkHQ=;`QgC)jEC+|>sxjL3CY!mNbkI=3e3WbyYUR3E{+uj!d53O?e z-jG95$;g-L41%}h37zysRlyUo+!m2Bf9Ytz^JZH!b}b;jWSXVV)M3KZu%Ql?-jFt- zFD=m!e3Zv^!`P~ZG`ZmKE4I^`W-M$~It3S~#mu7CHX~+l@8qMPjlL_nw0F(cG-Ki# zRr-63Fj>g;+?R#15v0zPG(&OCi5kWHsE1-?_fd2bq;i0QNr-&PfD((8430a8rh{(t zJtCz~uxRg1{fC-~eoqTO%5+LR2^%hXN#Lw4*#E^0f~}TgqRRBdcKhNixP{X+9ec?W zX`TRAa@5GX0DH{W_x_x_A&5m@Sf?Y)*80m_#mC!=+|#c_ZKx_l(bI=CvS4rm4V8~d6sSHa!_au2tTgUpt1}X{3+?G zF4E+P7zWJ6rVeX91dHeEnZ-ezBFF=~-2awFTW)dzO00&N8kR#zPMt zlT!gT+ZsAY(6q940r+(8e9Z1yQDSo8kQs9abpT>PpmHl$rGO{kpB-mCp zV(8KN!>Y(M49JRYI}`S$=&-gid-crUk#l>eCFo9-HD8KKAYaO|*A*1{JIOP!?O~dt z{F56c>_yg(dU--(+vKrNdO|`k3YKxt@nBtTpOE_xq6>?Z{jOuEBz- zr;($(mjD=fE=tHFJCo=g@0d`5?AeEfjYytZk7cyftYpl5Q;L>RWKTW-`8^^?Yo&Jt zuCh%cOWovOv1%QKzLED~qs_kAFSz?ily9gv`#$dr+GdNc!yT?%+N2E^g)R-f?>?)vPNlI}t0f3?~T6W%(Jk!YN}%T-|dIkq=iiCFL~pLKLqV zxpF6Kh)X>5zn(xU7`Gpj_IQuz*v(d?R2e+VA%~ME>+0abSGblUcsXwjV5l70#~pI4 zLU@*2ECHh_j#Gr;)j`etEUtEUt=6Pf%kV5pR}q_>p!{W&tTITwXXkheh_=Q zTZaM28X}_ckL>8R%h{23OdQVqU*Ox(9!1UgFmn-#nDnhq__c3>}x_ztv%O?@r zC9XU^+dR4zhT`A}%^qMZ3Kf+nS;>j>MLT7cBjhx(*-gTWY%({kXykv`XONu~-Z(G^ z7ObzPtZ#nH>bh_YY?#KJ=uJOsUxohOUg{0zZN+6}H?0Kj>Ft>Qf_IE!;MaL8URFaz z8Vv_sQK#cl)eavXeD^c4VtG;FXxrlF$A#nE4aZz-dsvPa`qs%-1b`8HDOq6f3LaD3o#0$U9NJd3_5p_N{O zh+mXi$2f6j?X1)J;wc^T$GX=CbDeU2)T?*cVf@4CS{Yi_k_hL%(cgy$nDFDNfqwLT zwKpplG)w|gU_gxej!vNUpONbKmGtiK`cNZhcfLOps#LMIwM6B9DtW`)&^Zg(Tj699 ztB|hch*gys%R+E`R<{~EI@_RSusvjH@9Om$#p)r#<*J$|@AOQA<)fl?pJ0jlAla9> zRyn26PEwH$O8p?)754xoyoMAN6$x)#Iw?P9YWbb2)b2C3-J5ypHsVEpo?IOMp#E7@ zzw|nv);iI+eE2`4*?&_DQ)F-6{!bb!CGs_Hy1l@13rdHi1ObGr*WxQag@9pkSs)b2 z`nA`0bTjzjAmaA=kS9Tj73vSVz>g>1coy*SK@Ah>RJZVOkz5;N32Hwqe_7{&QXOaJ z(ppQFQjdNl3uO--wd%?qgZSaijYS(xr9l737Zc6fS_+i5Z*3w>(_ai#1KZ(ZCg)AR z>VEu`a69|SgAsDCwZ7lNbLoZvx0Ox&$y3NP=RH2>89xMB8+<{33xUU8S4WJ6P?Mk(?M))bt3 zBM7xNVY_OQwwZ?g3!utCu5y;5%Li$a=bGrikP)crhh@*RX>0zKy#t}X${l0kv$MFe zhrMf$3ml_6TlOAxjA|6w?5tVh@2#HVW3dPN({L4ClS$v4(x>y{)_vi~gt@Os-=&zD z`#)?PL*vZ}umu!8{U1m$*liZ&8k#ozpckh|d7yS0I|n9~`n>~4AE>KTy~HMx;!Lua z>N*qe;=?`*^)2f&EQmBkdE;kJErNPJ@yxp^Zl|19D>TpN2Foz(Ut0DHyR$zyW-sD+ z4|nAm_q%8*3tGHhiYcf1W;%0I%jQ$dpvdx)>DFAUE zHIA*2+#N8y0XBqEdg*yk3s$7hKwmgFaPRGEC$rkJ{&hDFq0A@KK53JyM~Xf9vkkxI z=ik1I^Vvz(f>0+bGm;pt=qKL87h-$5m2QgE&rD))PLcK%h56sPVLr`odFaz26hfVM zzWtPaPRYJ17TX~YM9nN+@1P|pP9SwETr8|Bt17Kw0lOK-@u_}df^*7Mw;Fnrfltx@ub_% z7};p*{uv7amE{JpZwi5GzX+;d24+Sao1xb;n zuWxB_Ux(!SUzi{TEJ42T?B=N~P+2VRg)wgvp^w#UP0&?56f*tl*czkxE=ihP)xNZG z_UVVi@dagBeG<_7riPP&wqOU|wz~?YB%#Q|qiW$QaBEbCL67gU(OZ+s|bScd)ENTlj)fkl zQL#~$^r1temPJu33u1lDOE+F7445^y)Hl3v^q#qI3B{kAyT5aCk^ht^ITXV+-#WDB zK*n-$lGGY|Vlf61-7Y1*VdocMXx~_Wc&<+A2YYiV)>wprbjyv0*CW;#x*0T~EfU|Z z#51q%H|HX{54r$O4tc5~1nrh2>k`APn{V)4mM2hrh(e8smKafxO)%I5M9!W}b9CN}onl#9FXfa|3>&q1S8}=swi6ioe_^qX-e- zRdQ2$%aRz`ki)353PLo;EJonLlSK=2qr~(+ck1U_48yWNhq`&A*|d$`iG(TN@?Jux ztT?r=R#D(tdrUJT%EaRD{o+{MW|v;KuS@UCH-Fh|8YjuH|N6QnBxHFdR5k$#V(djQHCgylRu->_8V(JYvsC!4sFp;h=Rf(i?n~l&f>&Dna7H zj5xaZ07sJ;?oAgZAYE2$rQ>|7t;KQeifFzTdqttEt#L%qd(9o{a0f?&p@KKc40!#M z(#sqI+&QQi3`o^YO}Frk=Zh+8ZEpNpAF@@mtONM4)%8aUd({bkf8LbdE3{`H14$4= zcb)(#-Ax^mnHWZ23zJMgcSqlubD#AB?C|TtiXO`mh96ANuf?CZBasDT6h&PJ35z%I zGd`FL_%*cKSmIR}ndaBPJz30@-ZT24+Pi6t%V+Q0l^&q4kyukf0u=s3TD*ztA!=5k zer%DjqvAyWTLBFP43jl6@r7x>=t-QM9zkdevZpe&tmFCk9FUjJjzJgth-_<1!}LHP zg5}n1%+R=&YNb`~Sz&PG5aru5YU#bsJ6H8%YG?~}%7r;%;Vv_#>zDArpmpt}RQKwyFm_WK)W+>~SEIx_zvPaJp^H;#rInQ6Tfitl4=kg# zL8a12d>8G1E)1UA3-)CrLm1cX5~Li-H{#u9jA+Z?LE1^w5n(X@eOnB&)vE3h(c z(NV%oibcL%OG3(9O9513HHiux<_{r_^X)&7lSgh!=Ly@W&-Y0M8=jG+Ur!;Pyaa9# zCjw4NC_&pA)yf&N-Ex2>PL_@CABa3AiFi!R^embko$i1~<8E<;wOM^;V&T|g*)5lppRQQ0r=>8+ z+x~->CHwlj2Y*qmw;YfsGR&r&KvXtwU0cB%FymNz__#9~pd^Ul46J!qdzmvT%FhKp zpmmED{AoK}l+Wb!2}jVG>922|WzuCn(ymMPZ2x54)Bq+9j1Nid>S>p1(B@6;`SAD@ z?#=u-Qc%){hbS@0E74R%9lL9EElS4MH$x5DWH=qCjH9FWzmfSqNi0hN#_YcpX>90ppO>n@aM7Z4I=*qMPK zj)iO}wH1|u-J^jKuP;C5%s36=2H4-G@D=)aG4h0#X*?=u__8PVNI{7AGVa`1x4unUt`K#1W~$q(>gGLD`W{98+v-ofp{y4_}r4ZUkWA$2vS_nhsitzClpHF+5E zJMs!yy~8nYhdw`a_G1>Z2FYME!J)OqbGo+~^u=Fn51CJwso21+Hj|{cO<`_e&S4mtE{ELz7bPW*Qep>mI0rZ;1YV!2b4T!7o#?r*X?obLbBu#fYCigW|}$8$^QpTrhU z+y0ybGw;Te2Tfm!>Sm^LIFK{t_$QobdkkM}QS+yw$Tl8FnaID<;=avQaE4LwD zQ&&fjJV+KyoVy?2MT=Iq(rREQYBl#ImBnAd4oHk^X08*EI^}#?`^Qaak$W5OT z55ug^4ar+$i=Gp|;fxy(7DpXhhl7?Dx!JYOi!S1vz~S*-jpPpbX4 zEl+KLA}*ll6u#keyv3ol%2_APG>73f*&#+`%l-n$$uD>96tAAU@6FE)dv?6<<0mMh zeF;j-;AhS|65>q) zyx`$Q_RB(dxKtB9+!?29GHQ}y)?yEHLt32ZJF^8l^)(@4!JX(%f!&)7$aX`N&m0us z*a1(e`kPg7LJlKRyAh{WKDY49=GrIeB7er$WJwGg(=e4$LL=FyLy}QM>oYun;S9~; z>`)>oa*7qg(3n|v?%*|M{G94Z!l`1cV(9p#)(X4`| zWy)2Gw5lx*>UQj(5J?dh&Z{6|koKs@cad{qZL&89E$eMEeW>x!RqMyukiXXL)liZX zpv(G=#n;O;c`*!Ul+5U=&g$~!z7JKF>|+~U*DH;F{dMIAaz8@o+n?7d0gryVkgYcR zJnoyo#_q(Z+woru3cqbuti~OSf3yQR~(Lfy42Zx+(f;$7Z6ouD5URnX8YP z-uN&@Q$!J>2p+pbLw>up6oo3@a?N0BITtPT!vWo%=G(4twmra6ZoYQlQH-*|lQ>oP zmqvSC%VQMKfzHTIR>I8-F@nn!(&yr-Tjvcf_?4wh9cS!9MEdk=Zb!6Z^7yR}d*m-a zk&Hkb5la~7@HTVsK3xt@S)sf0d%J?J2hOxD<7$hn6GKU5B{jW2dC2y`39Wys$4@o{ zf*&7X!EPlG4k*>?_aD&A?})AD+`{KPojA_2*fDL+(Al9{P~2_LENj;1A@>q?VZO?B zdo5X61WArP{dT~Jl)$^=nlL%N)_%J9aZt@LEMG|2 zLaq&NggmIpI5qwA?6oiN@1EGWx8cHqQ4 zQ@)<2{!bEZ&H!)~DX>ZhA}5pE^ml{SELFDJ%_RyNu1bEAUf`^a=jQq6tp2GZIT!ca02!B9swa4EO8tf z@l!_$l!SnQF;bx0>*A>|2(ttgqXDje9|{50v$>O)JuGt%keJYU5dpq!UBKn%5=omF zNRIvoa(D$ab$ld_Uv`iq3~@McQ9JAaAM>o!m1Bvt?H(R~u1_?5PQjcgZ;iv~lOdwz zb+9?z0ir+xTKD&q6u%;mK1?L7DS7eLmiSh4^m~~HUPEb=4G^T-g%AM%!IsyOq({0h{}5@hBL=Nnn{?>q;ZqgykIkZ{aeQHqpQl6Zycy^P;<^!4JbaqhcCQef&T`m-#l1cw1TZr+vyUY5r!>e4Cre;+5xsL%1F*HLWIO@D~7= zOAKd`j{Zz=0KG{t%pPmEg-!cvEG*}yg{$9#;n#5%G_pgl+K-3I-MPBsa={$p)(4Cv z`MAK76#QVNYsP*eHu771zNPEZ4M~8ElX0}%QaA-P47eq;YVtIGRJU(_dgQ2XSEe)y zy)X5AKQv;MWiva`!_X3xY{}{69XB;%Iv9ny}|aisZ`>FE%!8@PPehIz_!A>HBRQToR&0wTd&uI$8ovM zK71Rt&UR~FI>Zy)>njPcJai5N!D5WN$x;d*GFZ52`e0rySTf$@XZJMMhH~;X!f+U= z`1dFhbP;05?5r;)r;p+Jq5HFb6frMj zc;dlu6K#RBY9qT4MI*e%#WSQz3-oPELM{$9(6XPu+F7d!O8z76c{ z!c+7cj;uKz*wK$XdLQ^eZj%fE{e_{?bNHf_KXFp}5$Bu{V1E{aRj&dUdKz+^^_P5M z&spJDtA-6B%r#%>hj=Z0g*k_lCAeSFLKIYqEGhlIZxH7qhRZ)Z(S*lMlXky+q;b0C z((q#I+22-COj;8UYMwS#I%b`O1Da`R);V?%TNj{FAt!G4uY#XWL$n$y+NYh)sA=rD z%w6YMXP_9JZmB>5wyVX=9GO>jJNjK1O|z;HglAyS6*Ho$HJJj%yO$t%gIV1c4lQ@?k9KC6cA|qbzSjQ6k9rA?c{&=!h(=7hF;_W645rfO4_&pZo!rFUNxMo@^q{M1wb;7Sif2Pd|w(K|{9?jV%m ze-r7?Saea~a=GP;4T@O^wcJKcY5*K3PX>OdsU&OZUBAWW%uP0hbY9iJ>8eyO*278W zdX7rVLqw|KL>3ESYyFGJ#vJh7I=ipyR4&!)c2`sS{h+0Q@Byql0ZMzf;Mk?-EwFgb zK8~ATXC(VvrdV~#sETb{l)pKqKkaCI$tw3uKP(ag39wGd)#F3a+LL`pjX9H(r&Ky> zp*+?}82|~)ZQ|r*K`^uHo477p*HUP>!kr)7bl>wb&Rz|lHiLOIXu;QKb3~Gp$EN!T zKXwzj!O6rSuw*vhyLHSrz~O~E8bD=JV9Ec1{I<=pnc>yt(oB#z%KX^zd`Na(T@!Ss zxE$DU-q^VL?PX34k$Xw_-_$NshkZ7}nH=6vkq-XZ`Y;WsL?_Yh0+q4B= zG&q0<&KWITYv6ak*_%z@LH^l4WPbDA<-9gqz)GQF+F`2rc2c2brptliqt15$PbE1Oe#@H9&}WecwL&ckVf7-+S(F?6dbC_a6sixL~fi-uGQ= zmS;W>ulHI{kh0?ZY|ZTX0NeI$w)-R}q9!Lv_^MxpwWZ!C*QYX@;!lCXazkzWtFxY~ zwf<&fOZW2~{ag;#P2V~+9sn!5YKqs}2BLNy_kAB{yoIHV1x@??>^Ob9pP->zK2ByJ zCI%h1yl`*sKOgT``{iYNsCw2hAsk|l{r?0e$^LIfba%#-?E-m1dqqTMx_2JXvsj({W!w+4re zne$hgQCNB2CI{feK-b0MB7<^SG{-wsPswi{MtbybrR0Bz5s6$I*Z8Vs)}|#~C$?Tou)dmwue`y_?3FUz1_y z{b)Zuln=#>swsoB{1Yd#C|47(n}+ge=K*?P%qYip9${tn*^V{JZ^zi~0(cSeerI}F zU5sJHMcMR6(@!Zz11(OANS^qWMd}*7G4|jczN2G^sL-PyB&|m<$jwnmk&eZsGj#J` z`ITA~=RhU+)ygj<^fR>+w5fb-tC8-+x#NT7RIl?Vk`-QWUGaHNTIjmJ)t7erBVFr$ zXSh)v)yl@;ezz0!cgBA!``GT7sGSAhN7MOW-ADB^x^N3jkJ|4YNbck-H!8Rz6)@?z<4}d`DE1;z+vk`g+axGW>4)v36Th#PfO|5}`{V z{MyecX8uB>s9`mRat=bNq6ReeS2|m|w zZK?r3!4vqYQU(%0Po{Wj`c~c) z_@*qHaFcZV^c^6N;3ihI&GC)+*ix-M>nVgkvp&*l$9~nl`Tn}f>r(ZQ588G%!IE}F zms}yXh|sii@{-md&C=>Jm`Vv`om-kQ(_@I}8cn^%a)r+=}YGyd>}`_V`=vPouU*djo(qNf8?m9Mv`4ieyQ=&tp*| zWpW4(gB5g=zbi8=C*hNjqdxLohtckr$4ivD^*2)y@vht?*Iuj@Jb$U|9Wpli8KBxi zCosW4@ynWx!x6djw!c$UyWjXcbLOkK z|LwQY9KiX&;?iS>yZ1uxy+p}T+yE)}f}8n}G)$C4loN|}P@UX|T=fI_x)bdwIF@lX z6bXOh3-$*G-pvf2sllTo9usZ_E2eZP8>13=g>riet5O~ICF!;maDjfI7ym#MzHiR{ zkiEgTRftzy=x$gwDF6P$vqN*aThsuz8b08GX z>F+b*hh<;|h_@eF2Kn}n;5;_(rwg&>jGG;<35h_0TE(VEVj;_5MyMJ{P4IF`Mqw6u zLVa{diXQw~FVPn4l`#)J1QgV1Q`>LVa>WN`gHw?$iAvanhmrC+LpML?w;C!#RA>Rn z?&}tZmdPz9E-(jgi&{Xx0P9#zC-=uVZ&q_sE?(aa+xVIS-#(dKk)s%$AwpYgny=XM z+n^M0VLDwOO+6U<^(CfMt-D9kV}*Tw>6Nb7TQAgdh|&Pj0Zfu@VEEE_Ib%kyOk&f_P1c zXD|nm4X@Wa+~c`05JsjQ%ni4a@LHW>C~kQF_RMSXi9$(lK)7Rt3+KiwrVOY3X8;+0)e=nhqw9A^DG}Wt^bO9Seu;ZqnT~2v+a; zNHz8DSXl-P$i(4LD|+f0qysGh37XH;DDk^rbkfzcmK0wq6(Kw6<2uRQOlWKUyD zkN81|&H5(dRFc0-s8fk40icm9O%ym^flsf%B?R28DIY7AkI2+f$oZ8q8Q1wrWR$H- z_>n*!@hB-p>wOmKM0 zN%a8g4z}~2;kB}}fxeIZpIzgq<|xqEJ{ja$&)oV5nb1c)Q&*LqJOPP0LzEG8Bq^{Q z72as~88vUvI4aycwx=p|BkTGv4c6(^e(ynbY!zcRdP#i5 z^23|e==*g;a8-CU=FyOpywY;wnx%uK=d}w`?i}*zx*YOvTCG?@=#LO~NH9lnwlr`i zF!h-F>w_c1V_R}MQX5_GmOo^R6g@}FY*ou_U7NK06rGqq?R&z$t{*({Z&qJJ4fp_geSA+hy6Qk&|_S!Sq)=INVCaaU|9 zu@FZS2RFm96*}`2W&Fq;H=5C@cg#Fcxm$fe=)PbK-?(vOW4M8HC4_OY17Pr$rkh5U zOduOtlHm^vtz9|0ota-R*TyQ%K5CB_ygxxv!_{g<|`&bGqDpLrI7~t>kK)8 zGMTqCb<;jYsjEqlIu~arkt{2b$$oC;c`7dCx*OerI=&IWjE z;nD#Mo3fgXqmtwETEkn0LOo1rlybutuYB^FdGn6tW>}v)WCRr0ou_pL3nhq1A0#&_ zYYo`#vZzidopWWaO0F}|)2}IuoWcrm;AVcUe$gQ`8t}^M?nGR;2UXNRec34Fns4J$ z-_nWXbw&FgH_bIXlP4Wq_|#lKgwd@{IW)~bFJB?Xl7BXd)y>4}17vEV#X*_^6-8#j z1{AJ?{q?isY1ykBStgxAfy(YV&iOZn9RQ(p=b}euLXd1F3SIj5EG{h-G$Gr6P0gAw zkCtvWs^p{MOe?FKYSr0iraF1T!D3Kq{Iw|ef~TAg6THR}KUG4M<|jKAk~+7~25L7D z4mf>hmR!K2hvvElM0a#?> z>MFEG!XkaPxljO3GS2)*4BVpsMKx>d+V769&+;sEbdEBAyi?Z~XJ%Z-<+X3};+kJL zX0Q1-7iZ6|TBJ3X<+x1g_wp}eg3hU2ivF6!%U|@{^p>~%^D}pxpaUg;Adk11T!Ohq z_nPc*zKNx9%_3BsK0$LOjwgVn0La`_%~r@{+i0;*6eZ8z*3?8^r@}! zAY3!(L?_TJ2SN3ej&Y;+x2!h2v~S*^R5eXe*Ahh^ zs6izz$-<3JoBmp+V(_2Rl+&sM-lUCrgn;c-WA#qW3ek6SpbFxQHo8sakB-xa+5l+6 zn~4q#C;2=Ir!VqlRH{<%4HJYnKU6d2_qmrlp4~yNOV4WdpA4Ld3wWl4IPo(N&ijc! zKAtm9YLh&&19?Z>RayoQGLEwslNg3ta9hEHidh#t=0QsQdijWSzeG1vk{=$vJHRQM+5`)DujcemPfR;chPMie(F6NVw(*xw>=vUs z7??UPJ4P2tIgFN+m?eAUJ~+qT<0)unG+>p4%T_);a0|4N1brAM*XMwRa2eP^ zAe!HjjU*;=zF{Dzr6U`_L-l&vJ*IfJwUnYd=2;gNtFu39e7=S7&#txtSdj(Z{Q2N- zo;{i=a6__u-pH*v0VB518j{pwmf|KW@ui?y#fl@h->Wc}xXIr=Ia=3ARHZtj+XSPbca$3?h$jCiLepjW|@vqCLE88P2yOIqY?HAxC6J!T9WDX%At|`|*i<^n_KLWy=igGpE}qbL-2F*|u+X zk4g;UdmVXDUnhI5T`<3%T9CpzfhuIg-nwxkO1tAQBcYEmdV)Lr!eNI{5KrS1jpG3p zqi|qpROIp>h=;RBZ`Lwv?;%@-2?nfl_C>_`Ca}*dXB%P z#?>$(?ag4O3*sndq1I(4uJ45IRX-ZE>Mq`?mRb9Tdi0<=R- zX-G{z5anh>*?HP!-738k=+^J2xN(Sy0_sRLv<2%3qDt9w1uT(WQu_0YaLMXT{1>wQ~`BjUUAkeH!t!H}C^s35S2 z*TAAKP=)zry6lF4W(wXtdzt!DhFe84xO9c*jXFM(V-ve;W(JunSL{7{0y*vV+LOikqC4G ziS2{}z2iC!oiN;T#6TJXYdf1yHVWNd{27_ORiiWIcI{V|th3O2hN_oGUauCY;Se{J zEcSGEU=txw<(MeM@HZ$$91YxuCZX@NLC*u0i#6f<>#hkx zi?A1N@#G8m7Y-R?{`0Gw7zMUOT}T=9G`HK*EpQwNE9N%-j(6D?b8pE|H9HDC{(1a^ zr^oQEi#^whi%B)mr}m1Ja%8LbmMOZbG#X^0@WmoxTNoQ@IAa?nKUh?zo9?(A9OU@W zaJ~FiGM%=la)YbqTM=-My77rYpu0zW_Ui?w6AMENDW_7uZSUEw%GKe+3e}lQFp5_X zJh^Qi+D@A0Z(i;d1BrR$ibku}$8t*&03MB#n}JrtAMlJd_YlbZV&;Zf{dxR01r}NyQkdr8b6s#M)!tFcTv8zR zq%{51{9B-I%V{YV{G#xTG|N>Pqx_y1KDknr+?KMKI`^=sE@LG>J6tzgub#KB~&>HXBs4 zGiK(hFy-TrYFtzj1Uw_S>h!J`A}@VB{VKdxRgJM%JG32p{lzd*Ln}AHI(R4hRW@gH zi(AP!;QLuEGW4w5hs3El;aJhsix=sy<59tTo=TAul|H7O$VmtN?< z?o{;V8%ka@_ZS5d9aK&!0x&`s@4U-jx8bN7k}HMT5N&XWh6y)UD>^*#U2sajQdh@Q zZvVcJVZ?p9ApBW;Z)p^ocnZd^xOtAi>*Je^&lHO4C!!k73xAK?cV!bkj?APrShlp7 znbAIJh~b#KZ`psdwXpR2FAhNgdqVX}mvYahUNOq1qXXcABb}Y`04QzIRQ3h0osfC6 zrmPWdIL1;U#a_CZ?S&IXd+OH4rFBz4I!9Z;ye4U$)@@UbcWsVaK!RWf);DQ>PesKs z)I?S#mF3W=t;YNxqCes6#o#UDtV=j3k; zv%D=M%9zZ&&1buQY#UX1;!{T+D+{30J9+Kc=tZ`QyRteQmB2!;b&GeN;RaWI(x&){ z;S}ojye;@Pw!?l>)W8-`bguXLo_NQxe^Td`U=I)T{sF|d_O%N!vbX+%PN-3lOo?w6 zVVtPUHrw;wr3_Q^s~MNVaC!=Zq(2ZzNgqVj3pcT}7{5oZm#IyrO8A&#;tgpcX4Zq{ z@pk;!8 zk4!;6j)W8KKyy6*3vhlXuae-9=YnBlNHt9XtnW`ZIFm1a`BD|>)Ho6Su~+s?+E5l3 z1oFz>8KO#Kd-@*X#6-P*XiK}_#M`Qzmw>1YyvASs!JhpWkJ+S8OsQaJS!L3dKDwXZ z<^k3ZciGP=LH1fA=9x*Zi_%eZa!5YSPt!znyK;5md+QmekN1n+1;jvkzI_XAyqm|}ilE=LFI;gWd3>5Hj7gY14!2_y5W z{h^pGerTr!uh-kh1xKRL_h5AX1e0i^NR-BHv;7X{k@M=Du+vJ7hvehvz*j<)K&wDzz==lS6Z{b|^I1t=N3C~Kl-KWiKHuG* z;8!j@Rr~30RIY{!i61#l3~xkU-T7`II^jyWMG=-0JlZTzpj_qjZ;mfpYZf2?(Q?-( zpX?j3@(#IS&3$z2Q|{Dy%24kL*|Jw<#5m}4xjfXr^>pEmM@+!$ObuQQpC zpcn(znn)Dytxl6P$8svt9BTcXsBA z(Dba8)c5#r7XmpF&O#1L(Vj|BAvA~~ltS;qs2+jbQ7Sdf2&giYF%Gd~(tHm1IZ^0wS-wHRVmkO}P#X91mfvQyfu#*p~7N%MLVB;RlH@qej?Uo z{JAFl)g7|g{O-=xei?}#DVwRc?`}&wcU2d!xV1?2XMOsd|AFP^=XeM;gnduU@>XD< zTSJ)>OGltOF#_L$Z7Q54*6-elT}o|y?#!y|`xJR71iRL&dWQy{Y@=oOdg8Hsn^_N2%%?KTq8$B_g&BFWR%58i@ z?xghNP1Pv^@dCe6_c>|YU@J~`t*q1T!^UsTIl1HBL-Y}VobO!bvf;~?kg`QMN5884 zycY|quVU6+0|p`V6d~dp$OsNl!A2|v`Ad}z`mI?4=rtpP*_xeJ9tfaixcv_XyoccM z;w?aQ?N}fNN2l?y)agJut?c^MIm=H&DdhD~@hhWZkA!+HuIN?hmUd1>UaIB@9uA+e z^_~L!l>$m9rx((GeKZf|TfN;@^GJ|Df65ZlQ*(Rga)+P(?Ce>x_>O9=r;U$+`6H3t z3er#=c00nBp;1g`h8WN~C7IUFSG4L7X|f^)*HM3XG-1Ol*7^`8PkRm7ltw=NAz|wK zdeKbkV&kU<>*=kZYlTDbFDVaIL3WS~$~)*U<&+oF%P6RT9dLt)G>i4)Pvw9J0p6Ce zQlEsIbPOhYn>VM15BhFD=w5bptbHZ78=D`)xL}ma3YoJRrdXc;vJxv7#I}$W*$i8= zfvGXqqb&)VB`-eBSgVWAOk293nWWQxHH3UQ$}*x?|Nc0>z*LZ7g5-lg!E$aoE;6x^ zJSV&{OwK^_1-wm?8`j#@@||Gi&gOEHazyIg-f(k0Jr5zRv(D!W+9q%Csiu+Z3SD^& zR@y9)=Rvx`DeaB~pj(X(Z&Fn1#+Vl@e^~+PNv(J`ch3F<(bxI42y!=-Vo89GxI&=p zYgHy!T<$s?mzyMA%jS1+lf|9s&U=>U%4v~Pqy_|aE&a$m&qFKcHB&?ov!fOCjz2$h zLyBnJ!zpleG?2{)#}qw3Ws46Fbk9T%*-UgF`bQL0*W-&S?srdxw{uGiWgL357+eX) z>q+QX^KC)E2ZTopq{H*&>hSKa}csy4X(F z2TBX8=)u;Eh8of#JFKk2Doiizt3@N zswaOC3G2Do>sG(Gx&8DSAa?Xg^Nc7o3H)lj!D1p1l%OGrIP}Md~gXMBco!c zIEJC;Qjz3FbH77IyaJkmOUJ@d&#mwv(wu)v%hU`qGfpxfV2p{*i}TTQ7m2;Nh9Cet z-;Sgs=61=&O~=uRHyMo>rt4+KIbV!cs#Y^SIt3iPW#6IBimQe6 z7Kw?_(Os`vz1ZXWPF9 z&@4zNRzZJ(fZfs;5qWAZ7#~x#EQU33LN-4Ysa8i*C}8 z&doX94!{qt2D0GUut_0Xkp;O<2)8j`zo@&TpA^kh{y-YJYfTRg8GIK|00p{l$~a<$ zG(1KI3q^i)No)@0Z-dfqzx_hKe?nWfHv(ijewe64r6jXRpf{u`Z!(*KlG+t`B)QuS z2?``AC;WpWEl~zF5nTkziXay;(eNO!B{zi1(<;6m( za=~$#*+S>)jGVlUwQjnvLbcH~aZlQQu5mqf+moYkBy(Ku@i~^f`T=mX{yp^Hyadc+|F_rtM_vN_vp4+zelr93O>`HMBk&p6>Pu?0#!pGp zIjh0&SxwpL$}^m$o}NK|pZd7yxOi`tV5D@n`fgAP^!=-(tSCUA&gE!W(fR7kgI1YP z3VtTl-xPZktkaeszs7lRna4Oo)T1sJsMWovIWkI2E_I)KhqwZ{70LB(PAQp3qwZV& znZ#=;>s=7F5U#<_@8%zGnm>Pa?{N|`2O_S?8}xgW)KC4uT zO^_>E$%2V;_e{8GvmxM3q$EdO{U0M_G#DZ4OQalR*Clpf4XO*DmS?&tc4yI#N@a*< z8S^&^h>X~0+{~wAPR=fCRiZV37@*5+q3h@b8|cN$R9DGk^A)GxQ#w2djNFf_UTP>` z!aVQ3ELy@I61Svc%}euECM(!(m4*Ijab}ug!*a$U9FtJ)wR7oM0-%F2<+ucY?>Xo>nOr=puT|13+mwGq$twV^S z8V?HHQQs0JA!l&eW~9^y1R;Jg&>Egw63*!V^unCu@m+oU5MgRaazDgp6ubvPG@b*L zHJc&P=LzGyvWK`*n3ZpMEf~Hz0pq`K+dOVN2r6{t~TjB?a2aWQyfnXYH8wI z;?j{kLh2)`$M9j)o`5E+`+WvovtDN0Wm)&VHOVPNv`7Cy3K5lQ^) zbJwSo^_%y4j~yUW@<>AH!7ZJ#t~3!{t%ryC(BEj3Bh=?_lQp18W=eD;0&0-5peewz zQyK>B7zL{UlQ$~qR*K2=K0E_&uTT>6T#&?rBK9tEHLTUyl-Z0Zh zozrrX<_YX_0@r<);7dp@*Eu=mC$>|*Hn7-l!&3^olzyT%Q{%(){+hK`D(=sZ@}c{* zV9ue*0t$o_fC3@D3pJ6|7#J(Q2^F$!K zxdFH7GLHmHBVWCCY7XtxcU1x)LH=d}im=GOf`BLbqh>3=(5AljNABWsMfnc}m!#YL zYRYZ$(3iWA3AW6CAb$94Y$}gU%H`u?FaqHUkM)|6qabC(l|dfq`Dr_A~C|?=iBu&t4Efx2Lqr$l3WY|J380yiL-s zMjkPJeIUT6Y|Uhk<~y3j4#YkFHyV;FHQ-6&qWD+zw@3}Q7xdg2Kr)!miKBk20+*J) zy5Hn7a-zDgDC)jhtkHY1jegqqlgm!UmB;x{_#Cf8sywkUghtgMc%zpIRDeTj@4?q? z&u7w-JB^va_DA)0M6=-#seuni$NSAqr)32_2dH@4M|$xVy)WlwdlD^|*D+Yz+mO)t z4uqq%Ie--$&mjZjv)|Ev3U6k~*-5jDzWl*!$A(mj0xY|GD%MP7R)AUiz*!&jB=L|_ zW@lc!w`SQkGZzsNBucq)48wp#>GRV(Zvn^Xt^?uPuA(;#yKvM}vwIzxeeeBQNs{b= z&lsA_%HU$V={D2ro8tU#eog6gPSJB>t276q@s~RwhuQy8fBso_fd9k({Il)=|C{ys zXZ`tS-2whL>+{e0^Ut~i{O4}B|HU2P-*nJH@U5wK0JT-=fOdeUb>2LpJvKK2@3lDkZi6MK~C>k%`H}OYh5=t zAKMEKwdJwD^dI#KU3ps6TYHt>1r0B_~ zrjgC%n0>!H*+1}_#c-3Z9~BS=hfyu9_Vg~ znPcOzK8$as>dSRjJYG)z(C5b)VN`l~GWcHM8Zi^dAs5S}N z(A=RFqCo%rZv26W6LkDHl&(wKBgaxYcH*XN%H8JS)K52OU~qNu_phZWBoy+!-f?xl zYZZh({|oj2wnM~#-UmdGAK>AvC0K2mNc~H|P%KR!nhiXhzojNl9um4;cX}8_=AQ2H z5!O!4y!X~YX0GA0kU8=6UQg&|XQO^f%sM3Bb`%VS+ zb8_L`5&*`o^sKixr)u)R5clnF_U%ORb9eG8Vq-l5MC|MqVDbB45&7mxhk!hw(%H<} zfYupjeZbWhdQ+!^7?v-D=rl_5U&U^?={xY;545RruHm*Vb=@%l12+85OfvVsPold0H=ZRu-(^y_dPC>u zE>&%$if&<2YMSUJE2^LpB&N@3z;ml!{VG^e+912)%QpA-qq?%d(a_eUMIZ{DluMmt zJs3T<7ZCkJBz770`ixKm#B4+a`4AioUD}3)rsR=y0)~Q&EmqfSW`7A z7*9ibxn{?MREU6HN$yJf08bZz4SNu^c=jM_R8sw&*~dGdK7CdVE3}(tpGaLyw-3#W zwRLt~s{~@Y-q{aa=^1NR{kXU1U)q*kSbA%x{r`q@fxM?~_~bOKv-UY#;`>v`z8qv4 zR0XOv#f_tuV~)%c!o-eQ1iB#bm(;qg@6Ui`BxdlZE{Nn`ghZ5&9l6(U(4l|arnCV3 z@**v9(&3Ww3_Q|Nz4aZgr zCgSa2Ay6(AO!B`iwGfT|{%-(wuk{aPP@AmNL1x87|AFj*G09Gy2l;$}vh4toX6!0r z55&B_x(cF9@^FY#))rC;xXmL3exP^B6#M#2r&PeNc*Or?TDu|#gp`E*znlKsGVot# z`ftm?e~H)s-D%)I`+@&=(|=nA{u9^we=_~IW#B(&oBdm+|F#VLAKKxPfwIH$32g1f zZl&fTIh#L<3tgls^+i}D$3J*Jjca_Wqid$LoslsdpNHCf-25v!%h@6=P+Ojp8I|9z zSXJ70wrh1Q@pCyGk0!vV9{&Y{_^Zf!NsPXQ-O5!Vi)&npbUjZKDAai9DH8E1@0{mT zAv->|uh`e3@I)q~8$7A#xnC3<=$FS77gG$k#L-tvD9@nDMHD<}(J2ZMbn6!iLByC7 zRJMy{eFWub7S~?UL0x&ENSE8EL@if;ttNHgWw7Ci?gHWGAoaVy*%#9SK^BQu9e6u( zhkpzCP04SxVGO7=^FJ2e?8)E1NpHdfa0OcK@=09tWvZRJHIFbKd>4fu#2&eG*bFPZ z)tK|KKNPpzb^J*PJ1}m4XgZk9)HJuf@;JZ%q~z-PVOt4{1^)tqt7oA!Vq!$YbD@u# zpin5x*jgDa%TU)dCvxYx7cXjYsWb`)w~=L!y|3RiIczg*x>XG%&5jh4k_KxnXoG~XyECD;SM!DeWTf!MM5{3nmvY>qI$Bsa9}W3*njT43u^-tvY`*+v znAOI2xco37g3leMn3n^;X^2TZ4p#QR)K`H1(F|pKWinpEUf1!1$-Bw8rBG)ba4NoX zq8_JC9K#anGtfY&R5;q`{qhPjH9SK``~CGjinkNlot2JJ_jWDl=05K4#toIO+dun; zy*=1jU#D>4ByBv)*aAd3TUI8o`)d_GELk=6A4fl}n4E9p?{AD7TJ)jM+qu}Bp5XLI zth-nd1=0|5@1*@kGML_TDZDM2+c+LkKlqgZO!@#7jVb_Gf~Gqi8w`9{aKDRdmG8y} z=*YImDo8ztY4~8vL`ri&=VI{4vPHxk4h$ELj(!dxCWq+`Z2Iwv?R?agcOOh2~&I|$QzI=|`_b*;9Yl&SIf0CKnvn=0%&1S&4Q zAheNGvP%jJQR4`DT?hDlMG*t;^6>Y#UHADVDe@h~gP!vg--rVTpH6c&Hk;*8eQh?B z#304+VA^%`bKl~E**VFrQV$O|hR(8fp>Kf3741vg*J{K$P0lK$3kuYo7_Yr_#gq+n zsk#qd4KpML6k2QX=IcrbKg6^S(scXh#lp4m0|zq14<}+I?J8Nw=&>*X#E?TkQ@VWX_AeXL>};KJi!wC0G-1(vdba6J!=AUHqZ$mrSFSo_gS9 z(#q90W%^uKZdSC`Y;r1t>=wl@6S_7zikwBJ=^hh2GJhX;h`owSL22r=t?mbK{ujPJ!j--NvP1!7hz2TrwsC7epalBazy|lcfhvvvq zqr#93m>i= zS%?p?9{M#LKjG?qp=R`fsbj*eB#Gx6gIl(5tU`5WD8_>ao5fDSqC7SWP`iQx7SSPa zF1a9M?07-GMiJ3;ZP6j~d4Bc=r_l`Sk1p%jer{U*$8O-j>uvoN$t9Uss!M&b+_MvqiBU^-)pW}0uDb!<_Nd#Yr~$rjl9`k$x71}})u95r=M90p z6Dtx0@j3yC&5LbxCeXCY*)5#iGjNX63Lm(BBTP4fu`wx)|_ z%Tct@kj~R|qA^sKJqJ^^VQ2bVI{lhK`1d=9#>@_dG_yk1)uciZ!|&6Z)etV#j>-D}ai zmFZ6wC=uwZhm?BgFL4wKU_*;0J%GyYAD>)NdJpdDe&2=D=fm^1JbV?-HAcDlwoYYs zY!sevzw%IRNmZnof)%h__?t<&c{#=jY1LS@u;`NBGWI>z;lc zsax%B<>tQFNa~a8u)o(-a^YB{A?Ou3d3DKLOZ1x|%RWtS7SN@OaUH@c0wosu`%ZHr$?CyrcAdD=akXPj*u3i96_ zF(WT;(%?gaN|x8a=^>t|)2H`u@l-Y1y=tY8@!WSnY$Uoo-Yv?UOF8ZmX-?0uEybNO zTr8(QnTb6vcC4<+ohMHdMV9@2L)0#!jByU3Sr_nGAEg8@KORz=R+Mji-J5ue%mIs5 zRR_UmcdlO)3tHMf^uckly7fwqcR-kTW$~{58s@+XKb{^4^2HEWx&=7u z)T8@>tlWO)gU9aoy%(RZ+Js?mpRZ~gFEBXLLpG4lEtSZ2jvsoCij7%&@CWbL%ox`a ze7m>7)m?=#7e6A*M_hz8jeGZbGMs*7!et1sr!wktZ|NNrEyeejBLaAus@NMp7K^M^ zldBwtT7NZaY%OiC?avLSGzy=HxnuS|`u|afM379tvImb-`kWBHJCA{k@vcjB8VFu% z-6>+CVm@1%+y3bHV!;)n-!sj9H9g+YQuKH_jE$4KBGWC;f03a}#y zTWZ9HJlOqS%>oI4UBLD?wFJdqkX8gh-!j=7nN$#{gO_NLs7J(Zxo2yPT3SIKA)@0k z-OXl^n6iSnwkE7mOKLSKsSMY(xw=r+=R6F44{EYomi>Wvl36Z@*Q?Kq^NH%pML!v- z{Xv)9{vL$pz5bl^oPCdL#u`p7fXZmlv?bH@{|+3t4J~-{=tFom!C;y+4}7$!2swRA z5-)I`V{0JH$C#-J61s-QJERd-h!CU(7642UdkK!B~1*Q6JJWYJ@Dq}a`{Qs zPE!&nK3=(o%YM-T(1!l9aSKE)@9kC%T*otoY=YBuuDJP7Z`QE3-F2xszBfI64981v zyR}K>j2EG>c4R>*lXTxA@Mf^*q39pTnZB8zobfrcn!e>AYR5NyVx3DwkojbrmTpQ4 z?+@;6|B0hxp;0m|u}V#pB%z07vpmX3_^9{6wYtKq+o`)}j>-2X*GU=Y^M@uii11KaZBY6!*l;ae^t&8?IaOm-1|U)$1PxCr89{|69gQ$ z%Ld*__Ia+uX)5cWgMaF0wfoZ@5;V0V6ZkDrcT=X(`zR>$&~lyg^GS=Pw`$|ASZSv~ zpJHFZZovJMRqwan0I(XIH%RB}a`5p;O33wjQa@|t8CpA)iV?0A`2!bE&lRM>Rv>S9 zY8}snt6!GtiMTL;&Y~J8nG({FY(%ut*hAq5uP4(Gd!qCErLVEVD`5pO;NeUY5m~KS zezV``G6wgNxB_-c?qNCC9sWQrC4ky2;5ZS0^;#t}mHj*C?&PsAPRxIYkT2O|94 zAoJujON+FX^nBxioiMbVu+pAHG$2D=F;qYex$CqFYfh3q%{#D_{kAKqF94)8x_A3N znUfvy^@r@jri>4y@!Ol+cw`5Lt`u%56;?vLJ4Y8J-udPJQTeS<_RwX8@Y@j|vjjD? zmnmL`r%#7c*mm2N=XEMI3!*SRwr2n}S!Xh%hrs0hX;dP=b_dH^j=SUo4#M1|tbW$5 zW4NWWpEle;{3cmG8tsa$8A9u&jw62*-3Bb7XpjTTQ!o37Y%VmBdF4 zG`1(a(Y*gdhbrX;KTQsw{B z7BiNdNSW&_Y%jlkGcv!U3GwSLJKE&ofjr zf1nwJU3R|j8mx4&MJ9it1U9GC{p_^(b; zd>PpcKT({Yv7keP!l*kvB189N&p<&M!yAO2jr}~-Za1jlOfvqzh+~HD}F>nRDLcRo2SRZr}U9zt`uwN}O9ulo@lK>$%6%U-ytlBTTD9<_13UUEnUJ zk)J-T#fikeXH+Fxo(hso@bn9zjgm!Va7brD3|izEw-(f^!0-ETX6{Q0TUt7wj(&*B ziPAZ5CUa%Wx*deY6c@gCC)nZ*GV$hmpdy7k`)`bU{6dE!YtlUDUiHOP5rR*R>0HKp z{&J_on{k@1Nd{^64!{>_<}q>A^iKaxJ;HYx&+vxz1E1m#R7yN>p8?w!0ZL?0vV;GJg{fn`!PmLw}if1RQr8 z2Y3g<@)jz2edebAx&g;mQDQ(uWit*1rH5Y*ZAD-4YJf4pnSmj6`RlTSYzDx=@gzj$ zgItBdIN)&a>SuLs4tsuIsU!{1Lz?J(s7>r%47$_NkAY2F+$(X;l62#uEgoP9b@TQ1 zcp-)=l_*WZ*!`$Zeqne;NGrJOw$+~c6+e+f-h}(fy5o(SIBuSmClS~OaILYMBg%3$ z0WWsHW{Z9DNxgsA?0NAj&AD(B>$K4%DGejg6_y$=+e|qHGS30f=;oQlzO9!o=IsHz zPmZ{a zHCR|ayE2w#jgJ9QznFu^#3h0OSU!96J(Y1$)%<0lnf;^5;A98j8S!xI*+td7=))Vy zFDAd;dGLXAdVL{%-OpQk^N8B`)>x6|j-wo!da&axQ#S6NcJ(-!j+o>V1Zz3cy2yPp z>$uB3sMGw{Xi`WdIlS25os+%y0$Ov*zmp_!GWBE=TYr_nod3yoVtb<)EnLcnE}a{& zM}1RO2Vbl)JoLaAf@|Xn$&+a7vNSt`*z1^nc#p{`HLb5NK17q_`Q(A?=kR0Yh4ZGO zMSJijCv+4~QiAz&?TqR2R(`X&LoEd&c~g)h3?tW1reGKf8`}!x)lE0WtgD z59Y~rBe4#Ng>!E%ZnpeknhX(HyZs~GuUE3e*f%z1;@}v_7L09s-11y7ysT8HL2aXQ z&Yv@7(DOgu5GkbRXE`JZe8khi#%s+S1c_K>JFI=GwMaXHt5Pp!>RAkreMh*qPEMFE zMPqzp7UbC?l|(4C>Qy*G(m4R^#sA<7qDUM#C~b+ z!K>W>H3x+EI!`57HG0CB{&pl7SeUr7GtT*_Vpy!qMT{@)x_@Nq^0JbH*68_o`*hjg zx22PVX+ATg1}lKQ`h&s0eX!}nF&DS(9c4SzJeBniO7D$sY5(z%B~h zMCutKfnFRH08Zjep7Q(0g|X__%+mhsRt-DMQb{|`mx^r2D{;I^pUisJCQOI~Q_ zVZ;BC_-4_=ot1A(_o^3pPNcZN>NZDr4$G!Fh6I-IAKdpB>yoRIEL4wdUPVM;t#A^Z zZBNoG;iWcGZjZ-gPICt~PjQwztbX(|=*w<~mx<4PhrIp1D0s-I33+bRsrPKRHi;N; zcN?;W9(tpr^mp}uG4Eqk*D~A`c@Glr?G6^vMRkkg<4D6~l4yV}MJ&^M=XurFcnJ=N zBelxfXmhMv=x1-dRfmgP@#qW^Uk_7zATeKuEIo;X)W5O4|YA&{a| znnE7_he85rt-2%8p&gA!PyMJRkDQxRXj*dd=i2zQfCBV`mdDqpPsX`s6mVJdSlLuz zyJx+t?YCOuKZh_lZ{Cm}js9dkG6toWfAHy(K*@NV&x`8&VSo1_K&2EL4mFLwk6%fU z1;Rnj*BrMH6|fVY6YmnFEMj{u^~5;w-WO?o;#yq&|x-0L>NdL z?L>N?24=*uLRr^t&vX{idNC?$h&Ar95^eg7@^4liJBr>w3Lmnv!v_ZD0ID{r!XNF67i_LT5=x-5 z!-wL`(gdb&W1x7R{ehnLQNvg|6vqRT6vyaWMab3Rh`R5GcF zc~^BtB;oK-U%3?EjPWd3ZUzl10n)4P7%?TG7yi7*6{v(3%&kt%@{+3d>$Ds5eH zRQ5TwqK4+zKY1^4eJ(@rb`1g@IH9HLmw5)H5tkXwisOI4c+DS{lu>Pq&dZ~Yb&qTL zWm4uAIwg?DdCu5@Za13lMaxny&=*IhAJ&oHuKi^#wLYk==ccaYRO9)zYR}LSpP~8s zJ#c73_uVy>DmUfnw4|}GZvbp*tihpJ2k?QRrWXZoG0!*Ctt&^MpdG1Ji7%I9^_1e-e~}%2+4wtyISL`q#*P z6$czw8^n^1dHcMbPt`)8Joe)UTwluZ$PT6F6Q;dB?aqa5G_&2rdxaZwUsO_?1FCgi zD6|*fagrTJ<);5bAxi|%ylbl_*-U?&;$-__bo+X~G+uj^r%8L#Xa^JzoQ+`Kb{14M zz~pE7XrjN$c&hQuJ+VQm7yQP}_{H)Tm44ffwD89iYh2(`^!FBJg<4c6Mce2*orz7X4B~#`FBhkq{gPBP&Jh$=-MZ7;WVyDx9#9+7 zC}OvmK(nTLWklCG=+>Cn*w5DYZ}${pZ(X*nZF93wn`sq-rVIH6Ypf zsB?7Q1#Qsxr%Dd8@eP+O$t?7jR?tV{)gT-omcWlBt5SXf334H!Uc%$9N=Ho4^Nsd; zFN^SE`ml~v)}*t(n73T)I9F}D8I3>g0nrp=|EW6Z+~OR;zp&7;LrLQCKpUhxDr=N< zPi-wy_iAe(c&lI;WP@sxcZ{r&M@kbaS@?Dk?@hxUfeATfgmanmaeb;5g^HYs>uT(cdc4?Ud z@PS1&^KzX#HrvUyIGf38ewx1Qr)T?}g|9BFuh<6`IJmoTk)9eO*ySuDLEBVMwRVC`*nGtBac#7Fv3J~eOQRhc}#+q_w4DkGrARH*&19vAl)7lt0zz3K<&UAe?w-V z1oL7P)essK1f5OZc7i*_k-7X%+tmQ>*LL0&VO>#JWA@W=lTX?rloNJjzGJ&;uUAn^ zAA1!VRav${V1{;zl)d@}zxTA6s##Yp>U19j6Ki3zCzUgKNeXsPpL=_`b38TBpw6Lg z3bJr4&cktf_&i%p;?=%+!R(w=;(Luv2S?eLG*#OPEW~rrRvU4`LTegFb03fcD=YYN zL7_9{=fd}Zcl>XF#x0mg`$P-YL&0*IFLswc+A{M;u&jXtql1h{_$&Jol2y|%K$695adl6)uew{d+O6p2 z!!}H(MU$uRZt<3QT#%Cc*)G>*MEP_5#2~bk{2K~|^J5K(+Wfy41dKYZS2PMnVsj^f=3q1=X6rsiYaV$_&aIjsHKJB)}) zurL^1sW)xE4dopqP_n_>7mkzu@`mm7{FivWo7wxbi>`klxo3sm`f9D{D#OhI0#^sp zpfgSa`=H)_^8v;=~Mf zM;hSavtB1klBb1TC=ReUaV`mNUAgIA;~8UQL*Zl6$ZEQk5;hK5P1mj_3*kGeR+mQ!-cS_t9{TACx!dwni3!8J8KJ+>AU^XpV ztFP*L{9z_mpZZaTb@`C$jddHo9|X;B)#l-3zb{ofxv2_mP=&cWG%w;jgR3Y{m{LiU zHvvd*qxL@(f`<4#GFV2(qW5u_UW%@XJSdZzV~|99E#%iQ0_upK-qS*CI5D&A)&>@R z010^SCg(o{+EYE%FdFz(-e2JJeR!IZew_U(9GJ3FFlFKvj6~i9%?*7|=SB{+Yt6@@ z?EX6)j1j{;~v zz+)TdPo3K5k)udUP54YL_V*nOO#%k!RAYj3zNF1_DgtH0$qhm`5d3d-fBPi14CqWH z%%da(Y94=l+)M6gIzc3OHK}QxWHkrES48tZ0ewFa+XY3_h7+g^J#g;M1TYdGWkaiW zSg+qEdSY)1q!ik{i!n7kxRTZGcMNbm8R-D4WK~bP2g50VkBupe7Azn#SeeOeQoi5VrtbiZnmk6V8lhIj4*CR1}?ckgsAQKWHPGNtC= zpx8@@YQBl5-cnO*J9Rs^dV&mBhGH4xIJ1zRMXN{9XR5;C^x~^l^WC!L<_-mtjRJRP zCVAi77J~dDUnf4THjGx|RF*Aq8?J`McRE9(O#uv@6-G_mF>^DPT zGL`VBl4JYQ*=P-j`*A z*?y7W4as#S={xKro)W9*776bE);80T-0)#>Un7-W2%ZKtJbdGgK`KWv-lsGN!Z&|J zFLw!XGHj_!j?FRw?7<4<2~@y{ZQ}@rdVlt|jSH1i_H4L`tYCPq8{jmQaFCM!RC8 z4NDfgY(e=CYq_T_lKDe%jbGuRyAjid)bU-xGOK^dYvp1rKuU#$!VX!-)%J%xk>6P< z#gV2|=iVb1qd}{~LAW8Nl&eee{82bu<7KS{B8Ms5VIXH!W3y#3MByC7idZ5h>cU~W zA=33=;Di+aiASo2s?kH<*HLp0u+E0@t~hLZ8HQSn+OQKB7E47sve@3#oJhoM?|&@I z50T7Fop|{8qsuknO=^m7$Ey6M`ALtTz^6YrnM|`-JxaF*c*VFz3NT+6KG#?O2#M+V zQ=R{yW-s?EHe*A`DYXgU_B{lr-&`1xpyZC`D|oke6_R54`VD#n+(On$116XUku zIh>bJ;rR?}U6LeD?N02T=$NN%xp$F|$A^64*Wb-eygkz11N21Sj>AGbtTn`7tTfN# z*1aI-YpW~z28_howD0v6bCdf zwuvna=&PyzJlV`m4N60~nX;H0dbj6+BBwOd6G7Eh?V!7Ukb!9dy$HcVVSvK3S8;>;F_{_S-G4 z+6F^0WV|}-fuU}BN)tbUwKLSMj`0##SVu!E`#&^(3V;NpX?`xsL*q1 z2)M=b$Msg=h4+9@qio2xRr4OyX|KezNsz^yN{KmPy_? zk&vDN3sDCcRFy$f;B(}&iA~ONGT(;Vn+sF78SerW^nKZyvIl+^Relj727v~02t(BD z0gM+*_~4B1avf7y>m>x?>QBcLmScgbzK)ZWi7=EZ4>8w}_^pxtjaNpeT{53Y_D$pU z{0oDDn*#yMn3Bi3n5B>o;7ZIABHRAN(lBwV^P{4Br}k}dx43@A#&Ao*a$~Q2!+SrX z7vPa}i-AqAuZdM&Z^A3~xU;?G5}TyRwqenBYdR)X1^woCQgPS-B@qD3|M1EY(Lfyb zvN;Q}LV~s&2Am}0{?*SzG6@6iC<>DP5P;z(#w<25cTC$5gsR3$Zq`S=;N~_{zx%zX zok7ciDm4wCfRTH<7?T~LXxI+B1WXdO)?ix}d={GZj(g#)o_t*J+TWD?Z+|O*(m8u@ zJdHthq8J4taOjIfpfg?Kqd@e6ab@)G`9uxM!Q0nwpNnhH(x)&PMbMDA#~K-(@dKvB zrx+y^%@Z(pF+x1SbzE+n=0@JTvX0l4@D|7()Kr~5bkVof*-~H6u$l610W?Ymh z)tXmfn0nq&8F8lH_>I}QE4%H><=eNxR>86(yKI~oNQCY6cOXHR?Kdc`p>hj)rMOWb1v*f1$xk{OD3;LpYrxx3z z9J46!ql&31`I?N3(>C|MsteSNd~jiP!1#(@r}KZd#q)vEyahN%W`v8Oo5xhS;9_`G z7)n;MW*et7e7!2k{5f64>RbHph3>|RM z>YsgAFCapPn=Sc{Dj#~ylZIe?X@?5`P}C(ajgHS&0&+wDNiw4*M0b*9NczLgG*9RH zSQGPFrUxPm-ko)c)XYm9H9wo&cL&FpN{XT2ycpm6eCwjPV>KwSMp?V;1T#AXP zm`)oVTi4$;HJq47q+2ez7Eny6@*y#u&AGjn-3wIJhPSY)UpMrrsk6RHUpd>}V1{B* z9K8)Kh5u0e(z;}E+)hd-F>ePJvD<&F2li+a;wC=)L(#9hHCNW%;CG}xtf~mVo+9ID z1{iKx@{M{$^-Mpnqiw!vh|gT$^`-cZLjAhEUr8!2GQ?U91!ZlFR32wDr9Lmh&%e&J z$KT;7BTSvF6&*8!O(iM&GMQ)6DgCYexTx+(wZkV{Tw~0o(}?HojPWz&)5SM_@z{sN z>5@E-UZ5LmPmhihJNzwrD`|!cND6_2K+rXt1)l|8H0lLDft_d=z+T&M5#7nvCw31DwC!9Z=57xh`6O>B>Nkxh7Aj1Z-b}8_^4VAY08lo=*%ooE zF_ieMSUdbol-HxvbCzD+OjR1C;R~~gTvGOuyXD+8S1ChrhHpU`Ml}-ouAs>X#307xg=_JgMh*F32Y=DqCSDzY(#c}n()f<$Y9A>d;)l!X^yri4( zfM+*+tSFQcXMeAN4d$&Ra^YVY5@yj?MM0N~2Me8h(z*h5tO>5QC7ZVgR?g2ghG@M0 z6B^3H$Ve=pA8FmCF#-Iw2Ab+@>VdDd{FWQ2>mG!#uOWdAQ<2` zh|b=!JMGzF2u6HqXRp7fey)R0jl|6t+$Dp#NmA6*gGLwbqbuDu50s~yIu;=q)L7pJIZEIGgOMJVKbKZE7)yw z*)tw{3gZeDy8g%L0to@dkN8C!vT5czM%c*3#hfKSQ9az*!#r9%GoYG2RfC{b04ydzTb0{I**6b=CzTh$oqoP4^#)Typ~GOm(qg!GbIR zX6rgw3zDN?oiNhP9pGOps2s2C6MldNz{h~G2v`S5)(?WqPRCBH8hu$6EMi`6q=cm4 zU1YO!&IFFGY?7DRNa%o=Qc#LCbCO z+eOW!GJJa+L-(dt#uk@oXw>V+yJ2kd=~ic3Zhc@ux!3yd*s%vLg<|PLc|YCIFH1!p zG$9`oO>x$~Ew!d{$2TfI4!qb}A5@|{#Q7*uh%6VmcPdJIORZPrF5KUNlFox$T?r)x zod?dXCjCNfjpk29^p^MlwBB(X598Ul&F*A3f^WO;IszuQqxB?H`zSchU1wj}LvP(m zgPu3)U6Y+#590<&+VrWdAe3JT)fo$fX0yYMPfz{0b;0Y|~VugiD7-gn7wh>(hJ& zf|NL1zpe;+#r>vPfk0G>$NMVaJZ9)A%*!J;Gcqu&{BT62`xWC^k#F?0hz{yL`cC2G z#5ezcoyD>Mh{68``2rn^4NbbKR<-g@Sy!#+6}E{L*FGSKFCp|Se-<2zic>#4ndrqq zNB2%x7TV1ipfcOdXH17r&L3=ZcG%(wCh3K{&M9ZpHG%v~b7k3%DrcK(^i5SRGQ)Lk zxk7!Cs=-vw>w@wlF`wp=#*brVW(S922DsRCxFNXjFeGe!*wrTzUaE;WCS9@BMmr@^KR$Xd$k1$sJzabHgB zU~~JhbbA2!i3+z8aTa}ux_Y7|chqE^wC@0e0G)-zl`^>e`(q2XIF{-yde7Z%FY1$r zyd&~r@x4(SS`hLT>41-HB*l4>a{eLmVCecUG{%iD|6{4s;Mpt*cL-ZspAzhd#7m>q_vV10HU6n0}zRjdRq%T zUG=G%=d4Yxl)tfJOx{dU@rDKUavdUOJKqOshCaU|ht%Ar>h-LS$&~ zJUpxiQ%nBKXPq%cRngCN1lPUgvWT8q=}z#z)4nCrYTm3kTIH3At(}lCIpMPObM?6` zrOkj7AEyZwBzw4^&l0{`rtv{}}DW5TDx5H}Gw%($^4%o2cK2a0j32p|&0-OmX-f8~#W2n5k zswS@w-GOgrI5canr)ovr(P1)Q7M(O=NPc{VLZ}i0?<3nlOc>5m1lRdaL=}+avPAS6 zzIkPQ+<8qu$`%R!F^`n_0t8j<8wxO)3vd{Sn`deyX5x>qhHo38--tV{SzI-QmysuO zMP6GXkH7y&dZGcO>#aav?%IdFi~X%(^k8xEGNV}b;m>Bmco-o81B+cE8WLE!1u=59 z*kf@xOX0lU*N5*>$#QA#OSMUDFV6m$2;b6eyKWSuS<=Pn3&)%moM=M5C^cZgzSstYj9

93sLR<0u_YTeS_^|8_dESBT&!f-l6ob|@CV%GkOnQmZdcXWhJ@`M@c}O+Nfw)-| zV$?=tXj{#fI?l^k-()Z=LG1f(8s)afGWt77WXXMC9j;;Vp{A6*C!kvkEf5GkT@aQ}(9Z`qREAs!R6rTE#J6cbi#;q`)QIX$LVwTL4tp#+ z=JhcXqvl0l${B>xC4!$bt-S409M~6EE`xn>fF&$50f>?Ouo4{)3n#Oj|Jk@OjVN8) zMVm5WQW~L52wCD0R^`hBp#I!tM%topy-p?q3v$|!f~^^vQPMa?w6W z(0Yy8tRZNtV%K5oH@|4HK%4zA(Bw9K+^RGsJ~4Ov^(m3o1H7z;*o3#7pMLk{{GQHw zH!Sxs{dIrl=cCDA(&Ulae8=|PR0xx$;5Se$&WKmr1!7*p^)D_1Pn%Co$E7#_*1*-XQ7U%W%8m_co^jI9_vpeW?pK8Flfb5JerHJ?`|pe8VZZV$XO; zt>eT%6!s`Tw9jV0aNfowwG=^JZ9WF&1XUu`!H5Oe?n_Xd#|muPApfDb_kiNhfm&R4 zReS+Kurr&cnU<*6B4z2v);29rEna|C*ltZM`{g4Y=(d@v2{6ca>8 zP#w=O4*b1p5l2}!&_q4Cw*(2Bn!zn)r5rzyPBZ+>RnazVxjL}@=lmgGQFOTU%}=%0 zDIEJwUEsF(qxOYJD5vt@Ax7(XGcEpi!#ARuMx3{BZqwL-*BI;Yydy_14QTq^|Cl-1^9A~q^%^fMmMqMvVQ~yvf);mBj01e!hz}7av3bz}AQ-6N4!6Ql7 z>9)g^UZTHi9{-yLrdtjA2`94y;h4`nvacc6=0saHg_*g+HIM6(q?CVQD1G-oGz(5e)NfgQ5mQu;;^Wx?nS{? zc~%&MWBp3!MqoA*F{TS4#U`lpea!W<-rD&QOH5*J2YPkm+QEP&+)aJ2# zL}xs3m^o23M>Wu4!$T;oi7K4+b2d*f89v~qHEuXP-){N)-A;(xzTOAzcE0yrTq*q& zK@p&C>0g}`h_NKIMKcRs5R4bUUWGT06fdh{9mhoIH6-avntL=GGc!}LE6l4}Jv-YD z50+a+wm3s>BR@Go5^kG8S^dnxOIfX!poS43h^KrBDge(~C@G=VM%!(9YZR{8;JSOq z?#&9fl!^lcjkH{V07&fxNb>*q!7K?0&gjfyA!u(IE~*i=;ClH^|4_Ung1L8Un|Bgt z{Vl7Z3|n*GGwY@&2dtnEC=QE|l4F#FW+$^xh~wru1AQ~RXa||nFV27Y!vvE~ z175N0S>>Cwk0gIv5YxOm6IVMDBzd}Xb1{LH}v@R+FL)WzOnO_I~T;YK5(H)enz8bqd>8;He zQdIXC6aza{q1XKnYWd#(TKjYi#vsqEoq8x3ntUQx45RU5HIojlJfn=7yGxG-3c;1XJ(k z`B^jlq;6*9fQEUB^B23gKSq%+c&jq!1xfCUk!3toFA&Pg@u7=%pUgR!AA*S;7fSsZ zBJyTus(elKL@+NTZ_}51Cg}jJO4@`=yWiQ@>*Nf3gk&w=B)zk!bZO$ws)3EKRHoUF zdTtVaOB;mAYyNhe7v?XjAKoD?+?#5qhbu$rNYwV2)2RC=A4(?b^IH_8t$X^)RS0dN zvqG{53IdjA$j~?rXTg$ee-IJq`vV6qzeLZluRXt6dj5;KLn(S-*%x5h;1L3air{ld zi9=sQP_Cn|DC=SpqR07!#jV{krNg&VelOcXsLZ7sARVE(dtJ$@3O08 z+3dwSzF;p^)-w>Rz;F<~@k%Xf`0Stp^3~s!tfhzO&C=}Y2C=fwyNCYWKL@63UFQU6 zyA*y>?4m)4$CD739Vt`l+tPfyIpGmur5nmfOpy@tG6mE~ylo4*>63c5c0^<}Fj~*& zEfNLV*VUDv%!tluP$IBHPtr=pu5|8?k?aT%!{PmuF`2XTF0W;Z#Z*_eZd-Q098pNU z^Re;)--jJK^=To9n#EZxLddV(W*lyaV~jtMsYTo*`j^7x8!JZ|ImKR!R-KN~UrkM< z&F$m!1imHyiYGrOh4zB3O#NgLFvz_X8aXd9qRfp$Me#5rJLWGB?QiLCs&W~krin$% z<2|L%U2b8iJ?sL?wQhQ6K8X8Ce3lbTT}l20t@LTapBfN#aj2{7arPxd7{-JsZ8VrZ zDXr{;MDy-wSpfG#f;m5IH^;`BcJ&P8;f=A1vO8Vc$P z#S2E=yHvw&rAU#S&-%T4|DmWS|7tee4`P%hwh~x@m}V}#5gz$YSsUeNakwSRcv<_) zQ(y?p(YaaQi2UTH#lg=RL*)C(}%ULOYu?&TZJiJQXvX4%Qr(>jF{7(D^n0MJZ^fLK6>dS4F zHtvib0O33rDv2J8`n;`&85|`pEk6>eul!z+L2dLU$L_O(xZ-Zfxi`PKl|>P~6a5E( z|L)Xpd*vB!Q#Q4#ee2Yopl*7?NPGQZS}D;I#~8Bj++=8u@1K}Ly)Mah6lfaxs$@;| zjXyd`hrMO~SAaQ?n2jkmDb|*5Ywh)#OMkF!99XL4b@2L-=*Duln)6so%1LIuxJ!RZ z%lVaW^~nkUXg9&UTi+9gZ!XWVgr6s-U7L;z>-^$+!nI|VBxhux(53$dyLU1ftMWjy zb-XcmqUEHR`=dB#PI$)%O4R#89v@kDXtpi8WB&Tz0uwO2Kw=pbU5V(n5gh46PVt!} ztsVZd?9Z}49m3F3zQ1^!fKRka>NEq1b*UoZ5>1`y8To>SAhT?gU5&i5s)$0zitMzX zlgV=Px{0Nh3J*GWJ#gYJ{Ki)zZJVO`JDJa_179sgl@1qwp4r@0c^V^mBzwqC$E}Q} zr;}C5Ed@5_Nb?2lFQ1&GWr=?kFWdFqw`S{caGkJi`&-IWg338Vk_0pqAxhLeYdVVo zYelWBap*ME{?u@4S7TwxP5(^M6;=us7HW$Lq?^E%#z4YfyWeV{%`Mj<;LaquvvL!d z8nWtM_Rq52Qy!>VaUGkfEPfTtz=g@)1Of~EnBYu~C4Rr0Yj~K!fM&Q-eT|}REqN$> z+mAmmuI;6y?K9ye($qpGj{Q1EHm#aHV>H0H1Lwk&njVI_XoIy;u=o{g!H54v!x{fe z!~FqixJZ`&&~S5->RfCK6%5EC(Eo$6Rpnkc+f5hqPByaGNHx&l%o1-ccjBVEvuMk# zR1zZiEP{{PXjZ_FB9Cpem0;d!bB%DAL3o90YA*K0f{m*UQL&AitvytKcz<%+?nU54 z6?1uA9baLO_obXfcdqM@A_8)AD4&Sk!?+!;L^k#-VdgW69L-aK=W2vIAxFAF3SETj zySK|=c`Mrl0JI5D#4~QD;pP|y+IcP#j=E=dm`KWa&9JWIFMa`iy5wI;C;eMl(?SPI z?=qW5Z-gCwdlaX4vyNkt;vT;8(1=)oCm*#obB?Jw$c=)+NGc5fuvec1qqc3~g65T! z>Kb&ju1-4pTcgKz8scMfBUdR`vw4~?Z7f{fR_4M2*%0EIGC%moDAtG9r)5be?&xYy zv8vQP4P`EzXu`F3_Et=zFN2lGB_KKTjFc z`U|F7BD?Ho{aso<8J$;SA$RhK5|N*YIAdZQp_GrX6E8UUXd~SqSZvEHQ0&&0xNS{U zjXy9~F--k3N!N1OUdg@T3FIqCFW944{4zRJ1qcz{sA!*NnQ__d3-f+gS{$b@4ws60 z&E)F;3YSnc<=cIq=F)pre2iU&@)t>&XiKOCbz+d_YYz2y;<;K8V#HT=YN4`Qf;Pi^ zqx;qYO19iTk}N$%ZJa7JuHk>jWmZaMH!8MJ*z2FluJc^PXSW+pEodbkd>tqALL(7k zV`5&F_W3iG7S^XFOu%73d66XL-hzRoNEq;ZNy|hMvy8$ zdNFp>1yz_+M)WWHYudw3?}@^z>rs`mQ^&_-OH@&>XQL9hq`iDON_41F9=y-zaf;)gTUGQb zu(0}oo!Ly8TH+Br1M*}b0rTv{$6u}dRyy9$m$w(tP2UZ0O9kq$djI6}=##KG{$+k; zXyv5Yn#}^`KoS|Zv^=R-Eue1m9#ySO*EPzm@53SH+oP1Z?0F1-q^-!LMkYNzlC%w1Kq)%yF)kuzuFiuOQ8?ME$fKMc$MouO>WYb z_zu+4j1#qTADhF{#@cLrzU7ifnvIA=hcd!+0{3;_WY0SL;19VQ^6A;$Lzy?5>*Sr* z$Ie2>s%2^u>63W3Ef)Mv$4DmBq6wgN9?V24+El6b^S$vk+6i-W)mUpOD`C6xtlV(w zF#`CVJ1;%7Ft+*+g&J@2ZOp@VYN(5r>Ui?B(NVHZ%;RJKtn6!v{hWPt(W|&yy>Bia zTxi-KO&QwLmv2z%O|PlVbCCObYBg4}zhS>2JGya&u``k-(`o-ZVWpIxWv&77vg-C& zxyhd#YQZUig++hXhEolFtaA@s7|*Mo(HQ`=9z%^^NUqg9CErObM{@_as6?p=AD3t| zDL=~M5{#(M-gCKlTsv_xwi)!2D=d|PmL==aBXGPUzK2Y*Rx>Dc^#T#)%LQ-k8wuJ` z)uvcRusiU{D}JB|NtkV8oQ-|PAo+=b;!^S4hZlVmOOYLVS&d|2L`(&_fq&(d{|4Ky z5AKr=fy&CN>=M7NXGqF(Gy4HYj9LG=N+%E&AxQV(iRMBuN!J-m|G-*FFGHs>{$y7I z?c!?d!B^b6WG*VNqR2_HfP^KSlSaaBLZXqYYLFWs-2g?pby7+HU$!>LQwK=q-}nb1 zAqD_@wVsX#^m3lRRhy2}Owev}dB9F{Mc3(u=dG_7kE2~7i31+$+(-{c5U~a06s}Sx zW7TMguew;(rkaHZMP)5rA9Q z$H5m;v`X@1&sGbLpLiE+f+^?$N?{FSUUQ}vDHpG=D}EY&al|KV^>@f-n@BNG_>3_2XMI0iSp8jL)bEY!<|RSO*coMt)C zx7#{A!))U5#@0#YdwwM^dAZdbqcT1<)JzpxQ{Vni|Mr(RJD>auOb;1&PsNO^U50P~ zjE_Nq0Vw!j^@Ft9DAuyhOW?b*i*C_>+iRmsOgQ9&N|Dc*vr3D%+2Q)4zUHr2AFE9K z+-X_<_^ZZh@AIf4F|jZ6z~H4&#fdk?A0ty^_AMtQ39 z#hH5x`?8GQ0P`-O6j`dYtFH6O?%muF@}>#N1^975FQPr{CBnYn&-}1zWD|>33VdQF zG_hJl!Ll=yXUKTo;KQTm_53g3{Q(Ca(%vGvy@tiUK%ltUSB?KZRzemfg9(lAZ&F>k z^}h~?A=UiBkPdKpx{VBqhS(v~sv#PG(?Ud$hLCf*NI7Y6@#1(0rig&zJ|Mr8Tf@uw za9C_Hi)i`4<$3m*Rnn#s%P~~=HRLBYSm1j)cDZXFyFFG~kjm$8`FGZdkIbZcG4(%gb%NTEA2GZFaMPrPwcCn;_?b>bFVKy(=PNNBG{qhj zV>8HlTH;dHo%$qYva%88Z|N9RM6|zRp8^Y`SVd|)Iw$k5+ma=t^eFss4)G4|LQ@dd zvha0jHI&|=+~Su@zE=vie)Lx~$JN+xsW-KaxPzQZy@F&#>EZ2yd2~|C9>tHVz0wV2 zULQCIDg{WJli3X0)lN8oT?5=d6p{J8zYS}dXwJ1$BELVRO-vRJ|twvSZ1>yrX%}Q&l&O@K(Cyxw<0FO^^ zdX*sW{~K@b0o7Eut_ufIP$`inQlnC(N=I5g3m_o9*Qkgz>CyuWML@dJqO_ zH|f21kSc)$1OkM3*LTi7`@iRWcl`VJ?)~pE91fAptToq~&wR^#-shD^Su&jcUa>`g z){yK!|3-oTHsW!;oa9=8HU09SS8SJf;gNVwI?3$Y`1t)k26=BLWtMi=PeJl0sD}(^ z7ymN$s--q`U9CWENpdhhv_R+%W&JKQmg3r=LXYz_sn6{XR@CBSa$%H?JlS~^eKGkX zlFk`yz8C_sEo%RpS~xE z(mQk3HvX#adz51Af6UbG9vO>aLLTJym8(Cm2f>ArGAGhRWb6SUWx?jK70%p=L#e%l zF}MV1xr->s#Y;?WXx?z&xm4{H)ZQabZ_0@L6a?2>kJr^z`O|L^Kv`TTe-a-Ay!*S~ zq9q0#u7u#Esrvg8kk{QR$G$@C$nZqCL3a&gCyJn#hFme$B5(( zEP^KHmG=YE)NXAoRc7PnZ|;FvN=FT;2dhv87e@se7tLuCl+vLN>Pp} z0voF@aIQ6M_Qi)Y%iyeQz5>HP^Dq>4`lj|mFzxbqt&mTpj|N&p+*l5E_|J?Juaj6w zbKr#XNOZFjCyrga@Y4ED*i>z;wQWlDNkB;iB+q35PCjMgz-ovImgIj{_Ehw%I@d7 zkzl#vgU`(LFfWQAs#tK^+b!Bdg|#dnHElA#1}IHw%3Y2a47*Fu)M`8X#|J6|E|TDQ z;!mQ-5LBP{V*!ldH`34vH{h^~k4|@%!5;MSpDAbvB0}vSeYQ8MrylrNJo>KwqZ4P^ z#Wt^w+{80Wcefh{$lSnL&raAM8;%D{-pDUag62%C<=h!GySe`OPG!`|XGStd5*OzLoP8)3P5fmXp9`l@@o`A0-*Z7G zV45@AfGQkXL;lCP4sh1qk8(DQt*8gGi-$~<&`!K*RLXWOAHy#$No;@6Ifo%A6Qxc; z?&1YwB3HHT!Fb|#@U%D%Ugqo5-Y>&;aAM0yX7pQd0{+E#`dv5y|6)A-E}VdWF`j-G zPQbqyPrnN%;9rcV--Q$KFUHgF!U-(<{+HtEci{y51N-#5c=}y90sp{0{Vtw<7f!%G zuus2>r{9GW@DJ?M@8apV;q(ve)9>Qxci{y51N-#5c=}y90smfi?=Nry{=M$rU*H7% zd)>Xi!s*}Z?)?Q$z`xhs`wN_af3Lgu7dQd`UU%;=<_Y-c!l%FB>387-{Bz;cU-0z1 zZ~}GX{+aOUuXy^+=n44e!l%FB>387-{Bz;cU-0z1a033h@aZpj`dv8vli|}}@btTI zg8%n)#BbvXMOIFJ2O#@K)F)&tiU!Y7)#3Z2Fsy8B54hdh$u&JBY@dkT;PNPkxM~(4=^8x@4jTJs|BHqKHTMINA=V_F)Q=C5Z zjG<(hFLx@S7;;-4-n?eONpVY2J1Y>=Or#0!xhd9eoq{~hOv{h$^@)utlBIs~_1%4p zx7U7+0*b`eeemWN4#C_2;Tl+*xN>qI z00suL!Tu=O^S>bB8Ki4-8dC@YGZDq#X7~Q$tgxqiTy0*Pg+; zf-^2CKNh?6X*MAl;a{|?N|X_efUm0#0l4ex_4T8r=A9+-|&zjT;@9m~r zMm9-ywMBlGj%6pQ2%-!M3%;DYDk-3-Yzjc)tpf;Q zzM}TP0s;pWJVo|4)PNKs@a;kgE}~N?*LX5+74spuv&)o(&S-uyNAeH6xR!Wo2y}h;r)5aIWwyuFQTL+W5 zVgQUQaO_}!&~Xq#;K5C|a`oV_D<`uv(_+mQovT+TNEr@J`>oMG%!Y@A(YdY<<|wcC zlw5?h>8dH^nr_yg5Z~@sUss7_w_`GZe4&7lf6b5WPH~z=E0JdFuMF(ZPYzp+mfc@A zucI(G$0~UQ4ffc^T>r3t6sKb0&CTg52&iifDXq1n+B}m6)Dz*MpAw$b6dHlG)P-gh zr`ZZzxQ>O27Q|(wY4wP8EnX|kkw1>gT3$n7x^+n2`pRrA$cFYqamCxSbiXPxt#^rP zUa@x{N=+&pv*_j7EsN47_`tV7`OVwH{$S;WHc9d{A*P=Y3Lew$f@G32w|@Z;DCH@r zI=Ie*0TyyWmSwEXRdDH8qd_VK9LEjA;eg40(o3^L(nU1U?m!RvzYqSuAA2X3zz^U0 zexDN1A6IQS&m$UzOxbr>H2uC12-IEYY{)cV3AHfM!f4jcMWf#_IxV3N6YYWb%_o`SA+tE z>CDYr-x`-whf-24`?I?$^O)OAMV=OYeR=RatxEp~%Z{&xv!PS$Ktv;vAN5Z03_qZv z+6)f`Db+-?IhL}9xXx=|t4D0pn-6^PHKRZv9wXT!nuDwHv|Y;Saf+@rgBHDSKIZ zHYjrT-Fm3u$1(KuN|`C@B+Uiaz557kwpp+H3&IFU2F{G{7M)&4eNaAY4LtE_Ty8c4 z>q1S|xm%&SD8=5)KRpT$GxGe=Znp6xZNZInmE9H7o zQKDEbHC;N&L9t`4AS)3OYzhzh==W-QenWG!NquanzlYP|0fr>V@tZepIi~83h#X z5t%@|Cp#|?g$M!bCi+{QY;{U1#~%(p?`77g9P`w?V`Ak}GIh69Ldbsaep?zuH>Pa; zX%*_giNEWnN5c1vE7^(PiF4tmirR4DZcx!fKwSh`6lIZ#_2>CsF-zG74U<4hkL3#) zn+6*Xm!%0zn+@$Og)A~ldjuEtxa@J+m@wUHVzdEF<9asKk+sFIQn^#KhGI5f~o?EZg78G@M$*#N$LnDaa7trLEih}zx@&Xub-iV4U>em zg^pLTr+3GHLGCz&RS{F#=XZ4;|AKTJa}DIegyOF)Y{oClvjK|@Wwwp8j!QcsKhwYh z{z%(p1jQVvDH~7E-1E>d`D#Z9PeycNELjF*fnK@U0%I9lve0)uBkr+-ZhPM14-wPC zIjK@s-Jg&U#ttZqs*Fvx7 z4%HVa5{*L>ivaiIb`2{g{%*%QF>lC$2LxWLima24_4ckMA)LHc{9jk$BNSL z7>FpdpikB znwM+ehSxq3hARZ&lxJ zINdzFV7V{W^>rWrrJ}4=Q+`+f5M8I6JrrmK>p@Kjds;Un&R;m>_&q z=ID2CXcx>Jx@7ScG2K07^uoFs1!e8<$aHfbFwnB`W?5 z&%Te(Xju&C-rPTCt#;bu+Nvbyh$v_^@iEBdMJq*@H-w2wj0BQ_y6{v453qhP>FqB_ zrkg?cl5H>QlI&S#vv{~o?9r{ADN0f^$R^8TX38fq2BO>HyIMBzMd0{>HmEJS_oqM^MvBWLDZMAgxrkIMq^!$6l^aS1-~yxWxEJb@g_k9nzcdHWR6a=Z$vz(3xj3 zvChhr9nNu3$bhYAr{5axEqmwukoOK{kJhL~>KF@!sj<*UJ*g^#oLgFh4MHwXOhed7 z2dA`E$vxJA=Y`AX`R5Rf1p!v4CtGfuEWaRcW7U5_FzfK2kH<6K165<7By|I?n@z(` z0?-EQg;huq^MU)RsHg|z2;fgf+m$&9SzKUsfaEmhlyQWsM(7tLAcG`JvLk-MB}Mu# zLmBOr7>C~p_hI~L5+11Bk&Iz>J7DQNV5%{KV@jx>iEr^(*dRjb+yNecWd9#y86)-L z$OpqDW~8DtZiCenr7JD&LMT+AOVjZD%&agCA-l^%-5#!%u} z7pRpZ7cE#*!^3nsF{>hDFZ{DcYWTo88T8?{*Z}sVF=&@Cx_PQgwZ?1sJxv7_dTBaB z#I0U7%Kr4R2{3zjFv3=bsVJTAI=o4O5v+bRBRIHlVc}apPihKSHCB>&FL*c)wK)pr zww%c)7f7XhqY7N5i{Knr4Rm|gBDQ!K-K?$~txTn8EvW9M?PL^P0Lv)K`$8J1_9_Bv zz_x4QKIyzw15a?%N6c!MWu^YYS7Y6_o>gyj}o$OCC!u@J*8CS_xT zSu2|Zi|H{XClB-4iKf$35gDWh&S9-Ev*DOZKW^Zs&o~W9jffz?4Fc1R_;!6zw_C`0 zQEv!j3h_{BJ^=owZvHNU7yvep|A9HB9{-6t{|oXho`wV)MZa(}CdLRJlV0cH;2nP2 zpqi&zp`*-kTa<+T;bk??tTyW1+uT9tykEM^&Zu_uGF&>Z^ugH3A2rhR^-Chp<&pT( z!g!5O37(N#V;v$IF*!l4)n)8)_Id5Sd579+A{*Nl!0Cs2A#bk8flyX5pzd|zWhGdF8;*(`(NM>M&SC;542u8Hrs%qlPcuG$UYRyPy(6MES`zjKagIutIQ?^g3&@s4GO zFZ=Y#u8A46HqR4~n1bX6tu+?>c=H?wZmuEYN8qIje@*7LWU2Bh$EVoJDc`Eu%l<*C zFbl7boZxQx)Th?#av_ZU=*vK^fIDi>9V-f!O^Jiehl7Djq#Ki#o19cbA8SI}4MPF1 zxJ}uJA{KI56`$-WRU_{R%Ohp*pv}(`m}1 zO%tC^DEy!<@?qJ#y)o)gp{b}Z3KIS&n)dn&C^Nz5`~f-yOn%=c+*l<4kt0faHJUO* z(p+{q;uiy!PwCbZVv|r}LS%g)0Z0Zof*l0mTa~C!ivurP{z$(er}TpJtq`hLYBFLx z{`tpD7OHW(&Vz!-7>dA*mRtTExFk77dymmoi*jSs#5BfZvarFAi zYZ&8KU|<(il0?YDH!Pr|kmvGzJd_D-3)TTXZ;!6;ncQD|YRZJ$`q&j*8WrMPMLA#~U@UyP;kk$3csp2-LTG3t@!^uPmbG75amE`C&&`cl z%C}*?FoJF;17ogO9r@_BDIhsg>o|=N$l3AGEy2xTeMx{{9jlHKNk`PC%_pU2gObnw zCpR4iXMQL%dpKUaPI2FhLg2zht#5|2#%?Col-C$_5>R)kY0X=G zfJZ8YtQi?vZfOZpgL89UwaJ>3w`4}&CCyzsO(EU}3O462k)+o>`hi?k+V|9Qs?jb& zA6PAJzPit2!t+}FL9EBMnJZ9*&)lBEJjHTrAxqSfH-;J?eot`WT~nb)?s_}jQZXvl zRqsX0h+W5HUKAkVzT{vhAD3K*w=gQ<6bqlAR+L7@=F??~Z`ZBay3cgPeO|cV-vpuG z+9{gYB|iJnu5r%nX5bufkmK*~fUMtKHP(&yFWa6jj2x{MJ<%ab)x<0ihL@OIoNkO@ zCfiXpo$Jl-VXll*he%2e=m&Yelu7$ch*GB3e5vXA!VZ>-0wZW3sPJ{k+PEQ-B% zEX)<;s~*_X0KLD=-IJyN`gV&icH=`M6P?n5&6QgAbgtRld`W@?n7vckN?iRl` zuUcKa`p)poh~zH_7^^P#6@XER4Qa;P8*QsCjwtEnP6z2%zU4FQP!Cl}KVYE9ugws%^A6Ko3Jeutu*gqd~EZz6vu+kXyRR2lyo$tKE4K{F4 zh+DfU8SK?C<%`ttT+}q8(d*Z&y)%9`69~U?zokb#?B)k<^2UDIj5O0pI^!suTelmd z`&Px|mm)WJbBE-bQqQv4GNqP$?XEjPaGnN7#6`zBR#4()V?SEj(WlH(#r#CgRIBsj z+v5C!fn(U+HsBu4NIZs%H$y8m-qpzQi|wMtqmaD2(s_Dr#qrevoUidy$1N+(LHFg* zp`484wYJN%(A&7xG^v&EhXaZCN8Wt6Db(Y44nnWCU>u)r&A(85TqfAb16G^nab|0T z6hw9mg1n z#rh2*m4QWnVS6NKa_I`nV#D@_9YoG0kX%JMp@OTJ&RTc5=ho);NvLHxh9uT2U)~&) z-tbV*lAnI>zD_*iEcf53mQ=98=Pb^lmZvfFKFo`erjFHtADPUg=X+8QGxv{zZDA#QpglG&iEMhf_zjU!H%Z7U^QFTYjJWu1>mR3!Hk9>O| zKOqek>6zD0v0fdJUH5TgXp_EF-@-xNs(E%vIOr|>U2po>#F}eY*=Q`==1&QBTA0A| zN9>wv>S=2Hd7nWM2X1U_A`k~QV|&|&WCE2R4-N6FNhmZ`x}~3^d8eq9_xLtTq3sEK zfn>ID&Fwc~3owurdZTp9PeCJbRZ{#o^<9mgmcrKqw?U6KWvj!(=ley>FGwQD8(+&u zvB+DWV4-HcBRY|mNQJ3E15*iZc@A&YIw1UZ-wgXjR-4uLmz~TEELXyF_p9w|h7au> z#^S2IyaG6<3sjIYc$u(T{S!;OskIOgRYmqPQ-ruFhD)*0VZuc|nmtrb>@*YDy9D(< z*UD5VrD4m`O0S;e*6hx>W1VFZ>W3pbb2OaTg?glPh7UzcdxjasX+q9};(T?M7xnVy zS$;ras6Ofl_kl+Azj;});{TO%0M*s!`N8%V;5u-2JTf7@RT4a{L)%8yg1Lx?lYe?y z(7D?G$II#t^fWy_`nb45w`p}{{5cp9t?!-3Jnw$DT`&_BwB8h|>ej(k`Kd!%=7I(+ zEd4T2UCWys7;SRUs|uRTVz9oS!wu=UY)RS9%8T*kM;R0iO)(dz>jnCf!{#6zN$tbq z?7L5UVPtoOmbOx$`_$*m3~wl#(WAwaEtya?*`2+((lQUIqIg*uE(8&-4&vEu0KJAdw+OsO^0%5 zOwKqo@CK0rw-a73Lij+?B+B^mzRmfeXO1vGS zbFV$9-245Ujo7VqZ{#JM+x{?^g}dX>xef5)D~JhK8t|moKmAm@QEwB@wu7u|IDl+A zvGCbD0JU&mm$E@)0NQ;xgF~FDnpBF(9huwCPue-6@J}uqH69g(Zlphd8Bfq2tmG43 zQn0yL(&N;iyY$*aba5G=dhm)5*`d~j5u@N^dAv4XYH&|$> zH2$^F#!4f&X?d*fWOwGk!Ejjs@U0a$j*cx$@Qpu_9>YS&g;3e_}Erq zh6S$zinN6kH;>QC&wEO`vN-UU%*%~rqfS??MwFfpRX#QJ=@w{T#4!~Wc@$zVmz!l3 zY~+l8dIV~jo`%)m0vmkBE{eXGD$ATwN*qzZx8-&xy8kFVo=YCS3t!||+v3BcJEQ?9*z!-%qHM$^BiUr7f#=#8+3s?6 zD|y?CZoBFB-RT0@G?rN<$#1TD9gp3L+@cdR@I-+6h;X*R5@DYhDvR3ToZbN*>9O~ooZw!e%jEpH~DiB_t^2H%sr z{P@GtiftY0w8i1rTbO7KOqegkM%&={B1q?c7<=Q*+uVojw}ybb<+l?donF!Dw~p*$ z6%vQVzoA&&XlblnDc)xO)$W^K=!Z`O z-m<&#o|Lw@%d&9(%;*<>pRT4K1*Z8O_n$p8EhjESzq4N*vRFnl-xS$|A3{;xi+RR+ zo&4x8k9`h&19gL&SD1wL=t_rUpA=M=A5I)#V5FXg^GduMB+en?qYoCd<^-$mE!M{! z03fNt@AV1GV{%zG+V_@%!Hu$_T^8HRt5wSCWjEv1Mv+5@`ZeR#!`@#V8%ji*pZh=vn zuuU-)ViL7@4&6f4A3Sz^u8UxG{1egd0JyA*44-)=^S;K1CEBMCbXVpUm4tg8p6d2y zNnt-#>93eO4vlTLG{gS9`Wb?DYyAb>+zStvOhP%G%a_)}z57ESjC9B)=u_nbhU5lT zr)#La!P}1!h>@U^TvMA9O%zn*>~yTqA$XjK6R#5VPri*xV)Udpr8wjLP8g(^Az=D zpH!GW8R2_()-9CH@-=otvro_H>H7+nr$=)vi(-ik!S>UM!p6zUE>P09d2&$JJQ8@F z3=b__8W+Zxk#0@vrHwpjjRU5soVbL1+W9@XN2A3oTvC^Bx{w)tdOo#MA>_4R*3t?q zmT8=4AS^8}@fI!SRe5%&j5fr1j^rpy+&C-UIQ>Ugj93Jd8qdRKe7#3-Q~o)DaD9!W znWE{j@mu}Tpk_FuBb+^ullWk~{^~RV(OR@w-s@;DA1b#z-A@*AeiTS`y80&bGxt%8 z;yKyQ&!yg=dd8)v&&8ivS>6fGQ%y2`e2LO!5-D(^LQI(@SMreFn4QGPUyBX`AL~x%1Ar2&>l<`I@MKf-_G5$OmdT2fGhn zDuV2ThKRCa(WO;SU$?YcW94cB-8Fp8xA|C&u#ZTseJiF*6zCsb-rQ~WLz_L z%WUMMRcfw*aa6pfx3)^G4k7JB+j5!`B8Ky9$=>r>Wnw?QiFtJYCkHLHc)u9s+5%LJ zcFi86tYEO9Cd(d+h_oqSqa0_QW#6Ih?r-pNs{d%IG;UtY=6Me&zKnc6qHuSC4F)kK zcxVngEth>QA93Nbja$<>VHLk!Su_<|Syu-OZEdzYHH>YYYB($M2;K|%2pWv@b2vfO`MF)`1rg!0>9l)oOBbXD zc`wgyz3O;o`=X*-;L=h0BPqF=BOxVuxg?t_<7>hL+2?WK@W)r zr+Es7iHLW!k(GHbTm&6|GHz2}KI6V0W0Dx1r)iAN&M zL5cwEz>Bc=sF2|5!_`sU5CLDjiR%y9s?{i%_d%;y3}@ApR!P80GriWj={|-JEE+@j z^wd7k4nH82*y1S{sOMRbbhH^c(+=qz)nk(JpUtmpeYxfpY7{F}>GWlR{w zMd7`dI68<7izM0JC7L;D7?p^Eij;Qo9{_ymakXPliSIRqbZ)ZpXOq*;vL7xm9Mplk z!Yc)yMR^+k7At9dMI;~22(?t_2cxpPgWW1{zRpseN)gQ{8GeJVBdL*_7wlw>M_@g0 z-FH%2yg086A}yC^wcF&_t@uiYkvyO3_D9U_y_@ux6T40QOl#mu*wb@&l9qzR6=al8 z+>sz+ch1hQCj|=QpP}g_Pinv|!qmt6Zrtwne!-LbYZbfh1?=B?nu4*0yO}fH6>p#) zQEw#9IsOS;_X#D>=S<-Fo^|Fiwp-&X>2^I;ypezdbX~ z>RH?3=g!$C#2_8eTtKk-1-bX51xDciGY5I&)Sn5|Zs0j=*kc~G1FoOE`O8Q6j}HV% zff_r&>fd)I;2&*E9Lnw~XgbpTPDMK>BW#~0^jQ`;KK7#?lbDCdAoJm?_&TP)mN3l z%nE(@tnift0n;*{>{Lrk+0R|$$w<*xQs|(0G9=3KxcQ(~SucB&lgu3C}SETyl7yrSt}I8V#UXE<&7KGA__tN2dVvA%oOT*o@RmTJqrLhMz` zCb{UZgVF*GXKJlMLp=>NZ!94qUAj z4eJYk${8&5fy9Tm|Dk8Vlm`qp62WrArvLmHCEW=iL%6%t5mGOL)J3q*`J|ux5x08f zr(o~XtsW6;Qyz+YjR^jmF+QMrk*%LU4(Wu}TM3wx{ z#tRf)2x9mjltlglb5aZ?sQo|(nFMxuATZpgGM8gnSVRLs z0?clFSBJnCl^y1Py28QU47T8A_$>kQ*Jlh02$+w^8fdl{+}lb{B@=V1ePzkorF`oW+fM;Uh%dVn(z}l7R2y>^YwrI_W$u5 zf&b%!g#7WAd!8ZNInDSJb60~~qs zr)#ydF@3qtQEoM^EztL>x=oN{qyK1BL)8(+Nr9*P1pz*5&Xd9&96c(8{(d5_ccNYA z37a^SuH{*7VDw=y(e!UNn{7|=`#kvm`qlim6?3X8=bqf1HDOY0aeRGJbiDyJu~0?D3t2ndPT3LW?+?;Yx>jn zHBhYJ#p1qzALZg4*3ETM<<2_ne2sN_5n&_!FLKW3<-yhZ1M4|w} ztLfH;k0u7Y1DbI2W3zh??)~UEvdn;{K19i!)=upzG%xE`)>I~KZ!f9kyq{9Dc^fHT zbxWvqSB;N5Ib-9|mEnk2OmTzUeyFViC17U?))<*yT@0)X>%&9PQth#%OJazHQ{CeY zi;mql|E0-8*;_>Fb0_RAA+TIIpkl)};4D(V0=Hk72A{g-Re5>JEy9$}FtQc@fHBt- z@!k-1^=Xh&AP%)vb7Ewd4jU!vE?ZviZd1C63u~)`wwpPyoq!4luPOkoVbB_XWnoIb zQ+`N)2if>^>RyC91t@Tg?{AuqMwHUbw`0PqV_P)P=PZ4~7Ojzrxa{CBo6>Ps^Y@>m zZ>&{1`f^?&OS%_MeqT#QYyx_f8XOoKJx^DlbnQncuKtHw72dd!4WKaHI5aDWRF}%E zs^=Yj&&#i5`uNec>wtnR+Y4$41p=FaTWf!LvUo11<)reNf-xTY0JX4PLtkkh^Va`j zzrq4}*Hol=n2ypZ-S98=90>MO8ufv*!+bt72{xq+F_&?l}J- zDEHepO2H-Ay^_Cr>IXG$MT! zdYI2*J#8ockZ~tlp9i-yTTu?80{*ZG>!F9coR(oi0Gj6nET0N0TJB^Fdvd5BPc6yb ztB_D^-DRZTNpufsS#~IXNwz;5Uo@|dXdyn2lMOsiypv>Xa1U3Q-_zsDNTuekwsoW9 zxxd0K{+3(K>RO$KIUt27gNnr3aRBmgGm;3PB@9VQ1DSKVoHsEi#(~{-UJY12T!9*7 zfzYAFPWJ{F_okGDpyNOG$OB|c2v+z)Hk=%6(s>uR9pj0H5~iD~PgCBPHJtZ}88!f0 zs?t9Yw=h%bxRl`Y{>dIWM{$U%;2)f}CzeQKflhgc>nNSXw}UOF-0tl4SIp;J=2A%Zb@S`Bahvv}el+bC ziHCS6q8~6rbq|!9+e@WA3hI=8^Trgyp4MaNvqpV?<6hd^?zW?m-3MiTFYC;r@4%Ll z`o9;RK8uQt0;NB|iy!LXpWsvcRjyBKm64wr>r;500dbZ6z8u}t>iJQSt0c_b32rzIQsi#6M;P6G?D9BS+~s6Y-Zu1Mw`orlRPuHJPxCi2UX_1(#K0fixTA~ z+;+1bMtzS~BXFnrV1IUV=dtG}8-%h>aIjbOJsl$bjfF=$E@*QnmzLKz|VS5HRK znXu1b<9D_G#@KV{Nk{63dj8F8W_g5mEg#$WjLma){|H^bH0!rk>~{0@Rv@pH-#=bO*Y(8 z!Nv&qT5ch0eH^9IFhNXC&@*JwckDcAqx~I}wx{LqRnoYTxxoo>4 zF#14%Aaj5su;D3NlL9Z{yj$5cHH;hflzJUGYBz_TMQ0)}|3N`_W!GP4k-Mo=y$72|foZjnV0%!`0(yWxkeLYVSK28*sy9xo zc{+217V(Hh>UIiRz(& z!HZTU(6IWeK2y`GBhJD`2mCxQZH1}iKOjHdY_gm2>}TY!dS;#EI_8)<=!uiGSV~LL z6n>Ug=9r-hJGqB6AVARPv5yG3pJ(O?F70Dqtu_wIF9`I1ZjLkW!LYD-h0If>_2f7M zs){$7#p}cJnvbM?_S6)L1*GhxGglmy%lIdE{HHYT?Vt2NAlUGQ@e-UCZ`E?RD6o>R zd(mVmyQ(G0OE{;i*Q?3DbfS#!^OX`g966eqea!XF*rYto%GX1x1SPRrk+}W5G*$r& z%l;&5g3EuYzhypTm#8vc=m|{ATL8GUmA1)rCnnE*Q=eM?WmKH)Lx@^h+(U&tMc-i3~NPS1bje=U9|=2 zn$yo!?CTYCAJfgbX%CuT{*<1 z5`7R(p7y$#qMs|&j}6&5k>3J~$w^%-GAy2GHgbzrGQUybSytxw{`q=%;6)$fM(H_b z!i&WQ8qx?WDjH@sgsj?mGJ0>%?(OTfyKZ!}Ua-0Ce)jhI1hpC<=dn*PTTD9Nh1ab% zMH!vK%)*JPKJ~`gHRh||Ky6l#_KlRo&Bqq<;?}U5pON?KYlnHtHLvd&GYbUwME7*1 zOt0#4O2ow2r8qe~uEa3CdyKpBSbQ`rA`ZDJy*`Su0eQ_TGEHC*hLpm^wgp^Y9X@Cr ztu@V^(pD@c1Oj_z^aVWyjBgq3h%HHa(?K0`x3x6OipJBI_t?Em^~Ad})M@yzjKWWn zz3Zx{=D;q#;1>;&WI8bp4doOC_Mcmp9_6YnhI@YanwRpHzgUa~KAQUaV0kz1RZ*DbZW{y02+nHqRo6a2O@W{22dqZ6!>PzN$c66EsU*`aeokkm)W9_g0*GS<1ekeH7*@q9@R5`#rOdQJcu{(5kDE?4k;lKO7+p=dWwPv&|&*Y`6Lm1~k@L#P**&oZHUjLs^MuA`nXKH1n&#RG>KoY%c#U&Z~dviURvDPndohfMF?AW+9 zr?0BeXwW!5rJ*|c({b?hgcFZvJJ1X8ZVhf%qQu9(dJd%`6!Mp8a?V>dINoRod@*%4 zip$pi(TfT37*_|z8Z>!4erMFRL9atMSFi5!qKuZpk6AB0=O6w78znu@C|ePewoBV7 z0RYDS>C|!4E6gQV-27y{VYBRFBFdHW%U2diL;-i~=K`UMm}1}IkAm%H2wN6m!?BGV z7sKzJa65F^v2k#%q5Tq?;;m?Kt*y&x(I@o`D%N`HB*K>UQ*w^P1kT%^uI+Mcys z5aN}2piJ2*H8-t6n$rj@P)btB=>1Z(UjZn+EJl$SnH!&u#%?3s@ai*dj#HZOdnCSn zqm(Gvoycvt>1+!|rWIQ(0k;KU1bsb1ihY(HteEDoWpYQjkSo9~|A7XpfQwj(?}Zjd-RN02 zEl17m=WZ&d5m_kp0_5mpddHd7x>+m`jty*uK^3eQqWP8s(4n>RC+wC!?o0EAmBSD5 z3UaB4FFnRJTqA-LoeXtBcQQ1q$Rwz8)pFv@H+Mi5+paT10P>@=;l?+5m}WLiys=7dF_kBG9mPG|n)KA9oqMMeGyXH8zm zH!CJvMs5*}3wxYuR@LOAZ%9SU!OA=cy?T}fq?*Pnd2VVQfkTGoHESXPb+NG zzJB46ag1W#ZuFPNhpu96Pe;>YU-Y={1FrA7Cj5q0Q+fC+X0wO=e&os6v;R%mm$IA0 zei|jIsK4}Q+ZiNc@Tce{dn@h{)tG2me z9c~#hoJ-U)SksbDagg>>6Lgple7*k*a)k)_52v3^3m^8<=|7!*Ao4-<+I!_%S>3Nr zuJ@ZoEz$)TlP8WWp!BO%Pc{ueuf>yN<3QZ3^YPfH@cAQ%8^A)i7%7@VM^tw)Ty*oDiUtN87R1@91E{LdfQF@E?4l2Dy zML z-tT_ir(~APIB5?XmZY^vgt&vpW%BBGRl&^jc!4=($p9Zohe;s(_)S2F0mSd}&yXa- zQ&XzF%R(n}jH6E2D0og(*whNW)6mipvReHPasx~uUi9v=V#n$>q^GQzgez8-=}Ub8FA#Ywm%^ceJ7iz?`4wOOVoZnU*Tg$9!#Ab9Uf5MsDt9O_!% ziOG6l7qmGh1^oGuy-E99o+I}kX$4f1J%>)_R$HD$#pb{vCsSg_`lHe`dYiLrzxO2L?=y~SVGOtD;GWalT3R4fGn}bDNNY5JFI+)nEbA5l+d%#1JetI${<#(fim#3MV z*938T>2@6!7DCWlqQ_~}`L4TO*|v=7e^jf3Y&2I7U*gN!cu*{x;L5~{k#?0%SgnGsAm_dVZ}PS?&+@wa^-YjA`VXuo z?>c2@Qy!EF=y+Z{#lNK^cV3bMW0|GXA+)fCo%74&`MJuzM82DRM>5}Bl9Dy-_OB#UZ}hL%=ZS_e6;AUWkXdOTwA(R0ovt!YJBI3X4Jl$7I0+S!R59o7BlfC z`bn3J*aMF7eVAw9W2bKyx^NMIgO0o;vdxH_i(8d3$YW>cGCi|+WQR0WtL>27@5`fR zJ8EO;_LK&q&9-q(&cy?S6OOJMatmw>qh_QfDI8ncE2oa}<**62>Jz+M0X*F3&7E{^ zswbTD-;*CSJt7e-07}TEm)5egP1E!0kMZVOlfH@3o=(3*U9WIz!x6mnQ|m1O4UQ*x z!G>(z_q>3t+)<-ce2=?ztLcSkt0wM*&dGHIMi)GzN;TW|(0z42kqo}AnXGF`Q1SLI z-SO0Pw=0XzwW~{;vA)+&$$q`YpoMNoKf4IEmCXy9ye|P_8lTA#bJ;tkfdjD;Xy~%$d zX3~@)dKTo65$Fi3*polt4UGOv(s}chDL|iO2C1-S)EZB9Qqn4M*mk80zN{pOu7fcvmS987DDCrJ zF2`)8I3E_Xl}EK;-d>&jPLu2FqYoE@dUPYL5LxFt6W}9x{ap>7n(Nt@GzT`{(&j*Q zfz;;a+^UNmS48Cx82Y{vNWCI3X_n9x+vJHaxrQ5eU{QdX7XY?UdE{obM!3w`ic*Pq`T7)B?pqc~A zpCHtL5}D;WP9i?`me4ajy}JQ6yG~8T2Z5}p>nq0=HvNHSsb$hBJ{tvKL%N{7@#M)b z=xTLC-Z>BK27m=UFH|%IGrke?!N%pseSIPg*nBI;m2Il35F{s|j+{)REHXhP0ugKN zva|ka&L30UJ#XFP=TT~07fdJ1sc_O~9q;5$3Uocc1RIMWQ~0MXvFpe`2swC3Je1;axO9g(Hv}8I zCe{u^8~&0^?;B4y2UmV?KE%Nm=dmhXAMro_w_x=chKBYtL1>87@Tk7byzqTx9oLee zlwf2KYM|*@Vl~cfp+ePQwSOq!`6*Bq+FEm>3|i>RjQm>Jnf6&|ZvS;q$*Lk_j-EeN zghiTda0!C=4D=Ry=dP2-E}Apq#7`CvG7QaqJa4@sZBTP%A$up!kXcu=ZI2Ilbxes5 z&>HOWN|ahe>stP4A5Bl%j`;5=Nr*Dx3K;!Q3d^YPBx-E2E;wUeh5^!78h7wTDmWLFCfVE04_`3ZJb ziuP-0eKj78yp2pXa&s<%(j>sp@U?@`(hrL|(AWC%iUUyg<9ZfC z6Awl`#i~mEkm-!3N4uy(u$O5Y9*)dLS!?NMe$iAVUU=7}zF(zvVWP4J)1FzOPV_>; zDtg8sMp1iN9ok^52#qxmr<_^tnR%<4oar_->DusKY}4^MVaY()waAWL+WrlN_ zm0cMIPw}D#xh6SxKgglm3~-BZU&oKvkCFe%y~z_KoV#@DbI}B<5qM3fGg-S>mTssmy%4YmgMm;-=W?PiO{lWK9KMs|}ixGO*h{%x7_)n6IR=?7h$Q|qFGm@8XOiWj< zbv!&X41{mcZ;O2mh}848nzK5ab?x2HomH%6AdW$9D(gd|!}AzUfIxHiXg1xdq;+W& zb|Knr0&XApl z%UXi3-1u&58nOjUDhK4${gy91%S($I{F4=|ik0k9G43@|mBa_&XlFIk#Z@gOF0^KR zc6>P{4g`}d?T&bXrtrd@NYZZ3q4_6Y8484ooVe@fTO2Z-pf!t)NiIbX-PgxkdS3;c z{o3R3`rg4EYx<#|ZHS3w?$g~08*auhyJ+ru-|ETphUl|kV0yIt!K4lFlFTC_0ir^0iqZeaUIplW?s^}T zWc2q?xhzx4=J>9Kt!-auhKpCp6t?m`nc4X~&~MA-ajfw(JT|9ny;8H$O}SINfB$kX zPi;mZ{{cBtP39^Dxe|8O33R@HGI^!1yfM0rJnlUT_El) zG_o7`#_q;Y&>0|G31ncvSLqhK{46g_9go4fsKpvI&sM5;zV{gqx(bNDE`3mzn5d}E z&gu7obd~TLMm-BIxKY?SvW%AD*1BhGipk{zwLF-u{ zR`_%RWY?%UZ{_ujZA!?1E(2kVMCdqIsR5!1$t`wu6f|9u1+EW+!NJi)8ieBIM`aqE z(wf|RnF~&1jY+->nw;FM?fC@_GdupmQ8hB*6~FmCvu|=tN$2{MtKM%Wd3)`CQhKBc z@_%2bnEY)O+nAT}>o?FK*%c@megw7hN}tE4Ze-fS}pFxIq0-a%|E~{?70UY+764f2Efr7 z!*om4dum~N-(ptw$0+`p7}^!OFpI&>>a}*J&_zMP8v`+K-QUZ&Q!F`qnd1%mFEpP^ z%}Lw6Mq|>h68h;;5N!?)f9lz=I_1nFHj^UuX`~qN=CCU3Y>^%zcv&7yPjJMk;nlt| zXJ)s=m<_^>wt7G5i&IAadB2C`crGMgURzg1J#wE~ZUz@4BB!GTGX{=?2=bRT!BmKs zxZIo-NeYO2i!q*Inbq2w@c64;?y0l2P2F1i;%~xS@rw~$jhch8#5M)BAERcnal@uVfzuAK8xM``Y!K`MLRI-1u_XzIGV;|2`!AlwyH;e{ z9hqtIUj3rXISz$r^Iqro@wP>9X5eVKOe${0#-t=vp$qdi#)pH)J0TrZj^I(UUW*X4 zI{%mCmSgA5H?OCp7sVO#<(?Io>rp4L1lT>UQI#4r5blyV_fe5xT&-B@a4`bp*@>&XfWoZ@{ zFLk^(z2Eve*~_d(Bc-P{iX<>v|E4tRJ~Bp+pDM^_bYIv)dN%|KAi?o`X;+Iub)1%wdjzh z&0}xyqqe5U=Ai6%{!6DuR(JL-RwFt3#DW_Nv;N7>MrNp@!aS?Wh{OH&STx9$Yl!+< z+R%9`o=k7b{6);Bn=A3>M0(Pfv5kGv4D)yIunMOt1)X1r~3X(T- zyz*s7^sdS8-Rs$^_YUadnn?E4684%a6*rb4PR><@chW#@VUf#J%7W|e*WrQYIPqR& zqR(ovvw@Yxd|S<9o?PWF14hc9O~EF22dorB6%%ytOS#Db-=(sR*qNUdWyRWCC59@Y<>@!|OqMcw;PPL;9(c<7aD2A6p zw*Sz>Eaw4bnyoHu}x|VgJy{+w16Pi7?aNRv8>=FdGfn=0yez;^&zAYi&FlB?UQpLe%%@pW-+ zJ5{Ouh{qzo5XkPPbb87GBOQGBi+&g#xMFZR^kMf}BE~`x;;WTl@~GwQ?@23>qi5UC zw0z}1T@B%*aVJxtN}OqpTJXkxg>x1Y}YkUH7NbAGhfuPa4}h06U>-4xI3q6kk4%-^Ycb` zHm|^i#s>#yZ!6`9W{$ndIG; zuL-b|=pus!im*`Gflx-fuOGB5lC&3n3G~XCyXnFtYDjEg0!iEMb#m%XBO1@ymOM`WewOs-sH2Mxr?fzA*A&34{U8I9B?Q zmY_YUEJ+EE{nprVZ<00!MLF~NNJPt3A?=?h-6L^PmIa5@Tr^Kt8LGL?IBe(Zlv~$2 ziq%=KILHZi#{UPPWZoJsC1XrkJFxllUqq zB0zU7F(xruhT+0gx249;N9_e(KgPg@>kFKc8deU7QspIBf#>vy%9BJEyuQJ#Pjvlj z&#l;b7e_bl`lKQ+ss+unNWM9bby&?uc zq0p70L{z^j4am%U`j_Nf5mi(Ng=1s?M+U$i&`^4g=^ptgVbU<|z8^*c4riQQrEzy2{mj30|Z7T7nCi z_BGlf<3^h01Xg_esy;Ow0D2(Q!NQKrEuEo=XXCn_mw7yun20IBYl~)C*zH|d+&jg% z^2eW+KK}{6GkDMByE@0P*ysG{BA@0CR`o+xb8|nJ<2uU<7$b-YGyXWuP$lbK%kl0- zZ^mVMxhU@TQ@^!r3!{Dgn54dtG*t8Yb@`#%`XU^caxLO_w95-q=z57>go3cwBg{x; z)#3@UavHI^H52vAm`?Ux^ZDL|NH%elNCrKn)W4)DPX}&8Q=4Xqn)qrQh{3=8f6xjc z=S-;CZctVO21-<+9UrL8kroWw-J@?DAvcl%p#pT0=JKzFRSs^e{*``vgZs+B% z`zAf9!hlP`mh384F`+B=OS%-jLK#q7D#%~g+H&&RW;Kz<$#aY;V-<6MTq0#S=T&{e zJ(1(1FVEy?^{-Ej`!U}8CuT%3jStf>?{{)r7mJFNb_w$@gnR4MWS6RmH)MONPEOf9 zkYDk{sLyj+*bB4;h)ks-?dJ)7CU~{3x0Vwyr(EU)PdtA+CF3(^mgK?V7T=t^lP?lN}{(qS^!jEMkR`iB*z@N z20si0Kkt0k+-#SNYf>V=R$l_GP|52X;c;#1>T3-~jq4vzKJ5#y-(x5e25+(1o@IP` z;3QHe^!OQQheP^~oHwU+xA(swrA<{~{Mr0!RQIJzzZ@G7xJy@T9_C`5{KjRzltt6s ztum~kC}Ta2gM)dLPW!WFrV&G)Nb`Ubq`tGe6K`b2j89xQFIARBXiu0*WWUZla%>Pn z^fuK!r?xAEZ$!Rid0uWpb4f^-h_kSH@WuF1NZR|S#)L#l%dId(I za#_gZ9zF#o#cFv9qJMw>Aj%Y%E&rmIHg&y)T)sU+yc%;Gn|$%04JV%4`FX^t#8TbC zpUqJ*q$N`8S(|z#B2k(5qgd^mpPG-H0u=;bZO2zRKr!Z63T0>!{3HDL_sNFBmdw#7 z^{sOp-EX#(-skd%4>H_q;x0-uB)z^>vR@KtqEhGz1ZLhQjxUqD5?<4VK>Cd^6=MNT zuWB@1Jo$J6KNqA=v-!q#b$Dq<-nY2U7r|Ff(w>b8yj~xV@q)wwtQ&Z{a=dXD+CHOT zVUJ5P{Xj`rrrqH}Qa{?fuWC&vQRGE?TQ3F6Ba?#-a)E5_?^*k|km5-L_}H`0tyqbY z6&fh82I;06xdvsj-1Tc|m~3)e z!MpjHz%%5l@?;I4GpC}VLqHPGcih@qna~15JFH|Ya-yR7ajCKH0iU%Bdr2p%gMpv! zHgQj8FisnP_9VJkcr_@f3(1l)&R(=vK%Lkv-sIIgbj#0(dlH@(CQ=Qru2)xmw$B<7 zJ}YXxAx_9nwtbc5AJAccdw7Iv)ZCZW#}A}Z{0=Ab{)+11qMg<~&D7_wXvJ)z(pi73 zuBEdY+Q#>37*26%a>Qh)_s*qereFR1%UL7cmFziEjEd|QaNTQ(0$aD&()r$Qd=2?* zw*DeW^m%}3j)W_>6w8|9&(yDSQVx_9%b`!m=rYh^;la z1VyWcnVg_TKy>e)6=o)i+KJ~^26(8WuQ<4il(FBTw3;yb@`v%j00~MPm1HB86^S?h zl04Am;iTbZi}>NVzt(*5RJ(f1%~%V^GeGwb3$@&pL?_Xu)j@WXa>@M>te?4#l3KoV z^h`Y5wmGqfE!5H~wl;E8;(*Chd_vngy7v2MiFMp7Yap-TLwmMX1>gV)S45b-VIp#M z9Mxx0(MX`>W~y_@8{${mYBjG}^Bp;`#&u|y3g?7Z(pLQA^*AVRAA?OFw&2g)YE4H+OFN&dkyv&hf-!Bxh?~rJ19t@271Kv7l?S+C3<7T&umBg+^{Vr8zELZ-j zkeiPr`J$=SFF&t^r(F{+57b-TB%9AdAJUrK<-7Yzmuo@`71W(3odkIwe2pNDvz$_-2d?k6hJ0^B)tKU;`%XT*z0SDeUeOY8w0%isw5`R6{?p>g}ex=>`cc^8?N1R$d(E-f|Q6<2Ux(kMP z;gX%mBS-`M&?joW03~B|_}70)&{@Ry0MY2=kocqkt=RANk`Avt9&IZA;htNO#`ozc3)u_KD&hF>@pn0_UWal5AL?^CQkkN2Evh$Z9 zXdYDxYN?_Kcfk!=7o8=M}jZrBKj3PYN1I5rE^9adc>zxOJ?fy&BAa{A|FA1S?XPJ*Yii($^Q`zU$tT!flC*Xu@T$;R(g6fVFYiJO1&*}y2+{xl>~Kw}bpQ9<=>E^- zKs7<&XpC}?cmprK3ZcLkcki1uW|UMg`w^YBHk<6_7a~$mIYQOF2NxG1V+;E6`TuOQ z#Qh6^rdH)-ww(AmQr1)z+-+CMid8q zIxgd+>}0dj4mD`Sf+|&nR(`;}rD1A%XO!1h@sfC|5r5_2=OSN3GE5>2EjGA@#CNvw z*OeMeZm+weMf+7n@a|>$9w;-}=3;aBwszo%va_PX{W-_TEvmHow4NmB;j{28Z;j#R zC-O3$#i<>K-;uJjOK+28!` zgTyq%Q}u|Km|MhJnz5WnVpwv;hC8m&YlUVl6b{8VeHJnZ7h67UQqVplHYz}G5FX%@ zKI6ll;Ec-Ggf}O$z-N%^b^TB! z;KAfY;5k=XelYL@#!wZ^HHM3|sKtsWM##k9el{&F@6eLDwA=Tj(6CXbPizUEWG=a( zQ4t?b*&FiNXMJR-+G}dDIk#WX1?=|Q;S!?}N~`nc7)hO`Ng}sXi)y~d66cik^whpv zee;1~@F08*{_dXk{r+tpW(x_!^0_|YPM&~`#kL+S3cxVpoR7brFO{#f3w9UxuO3FZ zDeF|{-L$k?SlAVrMP9g87CUJr-uyXuG50ivHSG%di;&6q(_I+_bG;}-W()<+`nV@$ z&nS~FVjZiVmvEAJZJ4~rU$k6)ilvy#quVKc*rCELbCJBC9gG80e38g3D z3UXc)VXXHb*WU6cVJkA!Rs}&Y?J)g;fxI)o9-r^90;T-L#ex%zsvoiGUSh^sCAg&( zIrh$!c)LqWWJDc=P(@ic8@)2L%4>+94~D&K=1jJ-=Y!R4V<1In_>M9+=vyP84~gem zI@0N6-))IFZOExz5T9}aJYD!A1B_l$Nqnt|3Mx%ClEK`;1m`}nNSRsGE8biA-3N#> zZP7S<&85Qo7LWL^ps>^_8vS+YgMz z(#wC;6b63>k;^CqSv4M_J?+!XblAg1*Ev}wko;iTW_)#m_k&J2zuoKN(U&w|51-a< zHYSvL>CfVtL|jGRa*p-oUBj>U$^mTBE!U&kAZwS`t~SS-VoPEeZ5lfyZhrIR0N1(f zelc|mS0Tf6qhE%)kNMKi1Jd}G`m+(rmubF1w0jR4an|^^?z9h?WAi((mz6!oW4+47*tPqoX^*q8+)Nb;GZTk znZwI;)pUQHP{HVT_exFSE)Sd2gOoHMUiGYfe5A=_rm2w>+80ChT7%rdz}ZiNezSQL zzrG?EFEe%%4w9PRiW8&V0pFm&t|cBUf%%1Yjr6&Gs+nkeO-?Cyd@93|#>&ru-j~jN z$-%&T37W=BT1Zdu6@2$6gatOSrykoGd4Pbe!C01dy>_(9EI4IHXCSoTfmLOtae+^i z9hI@M(pnZYp4lMO)O3Q{6)4OM0#@i!&Yt6Q^>WrUX4|CXqJ6B7&_BaRJkoW`QPF*0 zT)!R-Q&|?REyvG)A;O%k&`O|2G33>mJFw^d3y5O#kAHJAqH)oGh9#(5MDzT*tNgY1b;6!Wk)i%&1ay2~B;^czsA(x5vx4 z?U+sn*=l=zWxwWyDFy~o@?K0%fdq-aJ+U-O7co z%~QOjw}Wf(vTBvq_gTaEc zTy*Orb7~4w{$Wo;7J);zMQAz2V3Esj8R^5=TxL8H$Ge(Vm{(7mzdE8J&irV$4JJqs zpmT5t@)fVwrA4~fZQWSX%0B$3>_d6P#nCIBFfi|-b?2kEuuN%)FHWwHVkI`m{V{%I zP4p152zTi7w^AMl8WtxXB+m?aQ>nk9J10}~YsB>o+#IOr7oReSY0sS0p~^u227L*h zAUJDg5fEHpPX~D$ZtOim>oqjI9&$5s*7sT~+}$CU$ol>9Uy^c7rg2nyllmPDrF?|| zF!d>?rOsqW)B;x>#RVO2lUwiPG;!{%k9wyp%tp|f*+6!q8%b3 z@W;Wu-9>0o(zpGFLEYO^{@FH9+^~Kd?(m(mRK?zJMbx)>2>qV@PR@4Fb4i=!PrNC4 z^#RYueEe>H9kkY$q;-lS+;lT50w&KjJLGghXOtpCA>WFAB z7ZfKxle0<}W407lWVze2KO!(lkmHVMS}k5X5b8Iz8uZQMmpd6ojUMGw@;k z48pnNjzcDu&qM=-&AVM7z&fyojn$u~kOf6O>2y?H={vdo)7W_VQR-h3CS`Z;e7N(Z z0lpQ0gmWS+RglrA?kpDYx0c##^G0x2wZ&c+rC~n5c}sCwk!G6@^H(&O2dYkH^^m_f z#&mtAE@D^Je@CA}BZ#-A+F(9Nv(1_JcCrPE53uj6*cc>;7SV}ZTYMN>gXV9~KLni}8CI`PK*G3ypKp&rqaL=MgaKmxs^lN~*U-HMfOkwJF+U~W zT!TeoFYkzb3c9xoyQH4bkau5E8xK|&vHu4Ck{TDOKgcfmu#^YJlsyN2S*P3s#m-lP Z6OW*dd#S{`|K&E0{Gv(9bmi~d{{e#Cs5}4w delta 191348 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%0Ar83DC&xT*qM5ACNw?O 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 Dc53#w diff --git a/source/infrastructure/cdk.json b/source/infrastructure/cdk.json index 56e85b6..07ce503 100644 --- a/source/infrastructure/cdk.json +++ b/source/infrastructure/cdk.json @@ -3,7 +3,7 @@ "context": { "SOLUTION_NAME": "Maintaining Personalized Experiences with Machine Learning", "SOLUTION_ID": "SO0170", - "SOLUTION_VERSION": "1.0.1", + "SOLUTION_VERSION": "v1.1.0", "@aws-cdk/core:newStyleStackSynthesis": "true", "@aws-cdk/core:enableStackNameDuplicates": "true", "aws-cdk:enableDiffNoFail": "true", diff --git a/source/infrastructure/deploy.py b/source/infrastructure/deploy.py index 311124c..f27264d 100644 --- a/source/infrastructure/deploy.py +++ b/source/infrastructure/deploy.py @@ -34,7 +34,7 @@ def build_app(context): PersonalizeStack( app, "PersonalizeStack", - description="Deploy, deliver and maintain personalized experiences with Amazon Personalize", + description=f"Deploy, deliver and maintain personalized experiences with Amazon Personalize", template_filename="maintaining-personalized-experiences-with-machine-learning.template", synthesizer=solution.synthesizer, ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/__init__.py b/source/infrastructure/personalize/aws_lambda/functions/__init__.py index da885fb..1c533ee 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/__init__.py +++ b/source/infrastructure/personalize/aws_lambda/functions/__init__.py @@ -15,6 +15,7 @@ CreateBatchInferenceJob, ) from personalize.aws_lambda.functions.create_campaign import CreateCampaign +from personalize.aws_lambda.functions.create_config import CreateConfig from personalize.aws_lambda.functions.create_dataset import CreateDataset from personalize.aws_lambda.functions.create_dataset_group import CreateDatasetGroup from personalize.aws_lambda.functions.create_dataset_import_job import ( @@ -30,4 +31,3 @@ ) from personalize.aws_lambda.functions.create_timestamp import CreateTimestamp from personalize.aws_lambda.functions.s3_event import S3EventHandler -from personalize.aws_lambda.functions.sns_notification import SNSNotification diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py b/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py index 6d61d19..a38de74 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_batch_inference_job.py @@ -10,12 +10,13 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path import aws_cdk.aws_iam as iam from aws_cdk.aws_s3 import IBucket from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateBatchInferenceJob(SolutionStep): @@ -63,6 +64,13 @@ def __init__( scope, id, layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_batch_inference_job" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py b/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py index 704da1f..edf9f68 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_campaign.py @@ -10,11 +10,12 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path import aws_cdk.aws_iam as iam from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateCampaign(SolutionStep): @@ -28,6 +29,13 @@ def __init__( scope, id, layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_campaign" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/sns_notification.py b/source/infrastructure/personalize/aws_lambda/functions/create_config.py similarity index 74% rename from source/infrastructure/personalize/aws_lambda/functions/sns_notification.py rename to source/infrastructure/personalize/aws_lambda/functions/create_config.py index 571e141..9fc4432 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/sns_notification.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_config.py @@ -14,25 +14,28 @@ from pathlib import Path from aws_cdk.aws_lambda import Tracing, Runtime, RuntimeFamily -from aws_cdk.aws_sns import Topic -from aws_cdk.core import Construct, Duration +from aws_cdk.core import Construct, Duration, Aws +import aws_cdk.aws_iam as iam +from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.environment import Environment -class SNSNotification(SolutionsPythonFunction): +class CreateConfig(SolutionsPythonFunction): def __init__(self, scope: Construct, construct_id: str, **kwargs): entrypoint = ( Path(__file__).absolute().parents[4] / "aws_lambda" - / "sns_notification" + / "create_config" / "handler.py" ) function = "lambda_handler" + kwargs["libraries"] = [ + Path(__file__).absolute().parents[4] / "aws_lambda" / "shared" + ] kwargs["tracing"] = Tracing.ACTIVE - kwargs["timeout"] = Duration.seconds(15) + kwargs["timeout"] = Duration.seconds(90) kwargs["runtime"] = Runtime("python3.9", RuntimeFamily.PYTHON) super().__init__(scope, construct_id, entrypoint, function, **kwargs) @@ -48,6 +51,18 @@ def __init__(self, scope: Construct, construct_id: str, **kwargs): ], ) - def grant_publish(self, topic: Topic): - topic.grant_publish(self) - self.add_environment("SNS_TOPIC_ARN", topic.topic_arn) + self._set_permissions() + + def _set_permissions(self): + self.add_to_role_policy( + statement=iam.PolicyStatement( + actions=[ + "personalize:Describe*", + "personalize:List*", + ], + effect=iam.Effect.ALLOW, + resources=[ + f"arn:{Aws.PARTITION}:personalize:{Aws.REGION}:{Aws.ACCOUNT_ID}:*", + ], + ) + ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py index 1d65b70..06eace2 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset.py @@ -10,14 +10,14 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateDataset(SolutionStep): @@ -33,6 +33,13 @@ def __init__( id, layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_dataset" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py index eec9ca8..0dcd9ac 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_group.py @@ -10,7 +10,7 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam @@ -19,7 +19,7 @@ from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_hash import ResourceHash from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateDatasetGroup(SolutionStep): @@ -41,6 +41,13 @@ def __init__( id, layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_dataset_group" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], **kwargs, ) diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py index 3f2d636..7959c5a 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_dataset_import_job.py @@ -10,7 +10,7 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam @@ -18,7 +18,7 @@ from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateDatasetImportJob(SolutionStep): @@ -74,6 +74,13 @@ def __init__( id, layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_dataset_import_job" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py b/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py index 584c147..042d817 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_event_tracker.py @@ -10,14 +10,14 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam from aws_cdk.aws_stepfunctions import IChainable, TaskInput from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateEventTracker(SolutionStep): @@ -33,13 +33,25 @@ def __init__( id, payload=TaskInput.from_object( { - "name.$": "$.eventTracker.serviceConfig.name", - "datasetGroupArn.$": "$.datasetGroup.serviceConfig.datasetGroupArn", + "serviceConfig": { + "name.$": "$.eventTracker.serviceConfig.name", + "datasetGroupArn.$": "$.datasetGroup.serviceConfig.datasetGroupArn", + }, + "workflowConfig": { + "timeStarted.$": "$$.State.EnteredTime", + }, } ), result_path="$.eventTracker.serviceConfig", layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_event_tracker" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_filter.py b/source/infrastructure/personalize/aws_lambda/functions/create_filter.py index c6d2a9e..51e6f09 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_filter.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_filter.py @@ -10,14 +10,14 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateFilter(SolutionStep): @@ -33,6 +33,13 @@ def __init__( id, layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_filter" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py b/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py index 97cb942..4f7a027 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_scheduled_task.py @@ -10,10 +10,11 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateScheduledTask(SolutionStep): @@ -27,6 +28,13 @@ def __init__( scope, id, layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_scheduled_task" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_schema.py b/source/infrastructure/personalize/aws_lambda/functions/create_schema.py index 79b9182..4d11537 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_schema.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_schema.py @@ -10,14 +10,14 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### - +from pathlib import Path from typing import Optional import aws_cdk.aws_iam as iam from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateSchema(SolutionStep): @@ -33,6 +33,13 @@ def __init__( id, layers=layers, failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_schema" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_solution.py b/source/infrastructure/personalize/aws_lambda/functions/create_solution.py index ef1e597..3c98ab0 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_solution.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_solution.py @@ -10,11 +10,12 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path import aws_cdk.aws_iam as iam from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateSolution(SolutionStep): @@ -28,6 +29,13 @@ def __init__( scope, id, layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_solution" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py b/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py index d442782..cbe3027 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_solution_version.py @@ -10,11 +10,12 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path import aws_cdk.aws_iam as iam from aws_cdk.core import Construct, Aws -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateSolutionVersion(SolutionStep): @@ -28,6 +29,13 @@ def __init__( scope, id, layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_solution_version" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], ) def _set_permissions(self): diff --git a/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py b/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py index 5811b43..f83fb91 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py +++ b/source/infrastructure/personalize/aws_lambda/functions/create_timestamp.py @@ -10,10 +10,11 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from pathlib import Path from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateTimestamp(SolutionStep): @@ -23,7 +24,17 @@ def __init__( id: str, layers=None, ): - super().__init__(scope, id, layers=layers) + super().__init__( + scope, + id, + layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "create_timestamp" + / "handler.py" + ), + ) def _set_permissions(self): pass # NOSONAR (python:S1186) - no permissions required diff --git a/source/infrastructure/personalize/aws_lambda/functions/environment.py b/source/infrastructure/personalize/aws_lambda/functions/environment.py index f18c809..4388231 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/environment.py +++ b/source/infrastructure/personalize/aws_lambda/functions/environment.py @@ -16,7 +16,7 @@ from aws_cdk.aws_lambda import IFunction from aws_cdk.core import Aws -from personalize.aws_lambda.functions.environment_variable import EnvironmentVariable +from aws_solutions.cdk.aws_lambda.environment_variable import EnvironmentVariable @dataclass diff --git a/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py b/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py new file mode 100644 index 0000000..2194f68 --- /dev/null +++ b/source/infrastructure/personalize/aws_lambda/functions/prepare_input.py @@ -0,0 +1,42 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from pathlib import Path + +from aws_cdk.core import Construct + +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep + + +class PrepareInput(SolutionStep): + def __init__( + self, + scope: Construct, + id: str, + layers=None, + ): + super().__init__( + scope, + id, + layers=layers, + entrypoint=( + Path(__file__).absolute().parents[4] + / "aws_lambda" + / "prepare_input" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[4] / "aws_lambda" / "shared"], + ) + + def _set_permissions(self): + pass # NOSONAR (python:S1186) - no permissions required diff --git a/source/infrastructure/personalize/aws_lambda/functions/s3_event.py b/source/infrastructure/personalize/aws_lambda/functions/s3_event.py index 44fc8e3..610c5de 100644 --- a/source/infrastructure/personalize/aws_lambda/functions/s3_event.py +++ b/source/infrastructure/personalize/aws_lambda/functions/s3_event.py @@ -19,9 +19,9 @@ from aws_cdk.aws_stepfunctions import StateMachine from aws_cdk.core import Construct, Duration +from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.python.function import SolutionsPythonFunction from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.environment import Environment class S3EventHandler(SolutionsPythonFunction): diff --git a/source/infrastructure/personalize/aws_lambda/layers/__init__.py b/source/infrastructure/personalize/aws_lambda/layers/__init__.py index e1f85ba..0e77372 100644 --- a/source/infrastructure/personalize/aws_lambda/layers/__init__.py +++ b/source/infrastructure/personalize/aws_lambda/layers/__init__.py @@ -11,5 +11,4 @@ # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### -from personalize.aws_lambda.layers.aws_lambda_powertools.layer import PowertoolsLayer from personalize.aws_lambda.layers.aws_solutions.layer import SolutionsLayer diff --git a/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt b/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt index 5115579..68789cf 100644 --- a/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt +++ b/source/infrastructure/personalize/aws_lambda/layers/aws_solutions/requirements/requirements.txt @@ -1,4 +1,5 @@ ../../../../../../cdk_solution_helper_py/helpers_common +../../../../../../scheduler/common avro==1.10.2 cronex==0.1.3.1 jmespath==0.10.0 diff --git a/source/infrastructure/personalize/sns/notifications.py b/source/infrastructure/personalize/sns/notifications.py index 0574f9c..86b7811 100644 --- a/source/infrastructure/personalize/sns/notifications.py +++ b/source/infrastructure/personalize/sns/notifications.py @@ -10,6 +10,7 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ##################################################################################################################### +from pathlib import Path from typing import Optional from aws_cdk.aws_sns import Subscription, SubscriptionProtocol @@ -24,7 +25,7 @@ from aws_solutions_constructs.aws_lambda_sns import LambdaToSns from aws_solutions.cdk.aspects import ConditionalResources -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class Notifications(SolutionStep): @@ -42,7 +43,19 @@ def __init__( self.topic = None # delay creation until after parent is setup self.subscription = None # delay creation until after parent is setup - super().__init__(scope, id, layers=layers, failure_state=failure_state) + super().__init__( + scope, + id, + layers=layers, + failure_state=failure_state, + entrypoint=( + Path(__file__).absolute().parents[3] + / "aws_lambda" + / "sns_notification" + / "handler.py" + ), + libraries=[Path(__file__).absolute().parents[3] / "aws_lambda" / "shared"], + ) def create_sns(self): """ diff --git a/source/infrastructure/personalize/stack.py b/source/infrastructure/personalize/stack.py index 4dd3320..3864f5b 100644 --- a/source/infrastructure/personalize/stack.py +++ b/source/infrastructure/personalize/stack.py @@ -12,6 +12,7 @@ # ###################################################################################################################### from aws_cdk import core as cdk +from aws_cdk.aws_events import EventBus from aws_cdk.aws_s3 import EventType, NotificationKeyFilter from aws_cdk.aws_s3_notifications import LambdaDestination from aws_cdk.aws_stepfunctions import ( @@ -23,12 +24,14 @@ from aws_cdk.core import CfnCondition, Fn, Aws, Duration from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_name import ResourceName +from aws_solutions.cdk.aws_lambda.layers.aws_lambda_powertools import PowertoolsLayer from aws_solutions.cdk.cfn_nag import ( CfnNagSuppression, add_cfn_nag_suppressions, CfnNagSuppressAll, ) from aws_solutions.cdk.stack import SolutionStack +from aws_solutions.scheduler.cdk.construct import Scheduler from personalize.aws_lambda.functions import ( S3EventHandler, CreateDatasetGroup, @@ -42,11 +45,12 @@ CreateFilter, CreateBatchInferenceJob, CreateTimestamp, + CreateConfig, ) -from personalize.aws_lambda.layers import PowertoolsLayer, SolutionsLayer +from personalize.aws_lambda.functions.prepare_input import PrepareInput +from personalize.aws_lambda.layers import SolutionsLayer from personalize.cloudwatch.dashboard import Dashboard from personalize.s3 import AccessLogsBucket, DataBucket -from personalize.scheduler import Scheduler from personalize.sns.notifications import Notifications from personalize.step_functions.dataset_imports_fragment import DatasetImportsFragment from personalize.step_functions.event_tracker_fragment import EventTrackerFragment @@ -132,6 +136,12 @@ def __init__( ) # the AWS lambda functions required by the shared step functions + create_config = CreateConfig(self, "CreateConfig", layers=common_layers) + prepare_input = PrepareInput( + self, + "Prepare Input", + layers=common_layers, + ) create_dataset_group = CreateDatasetGroup( self, "Create Dataset Group", @@ -195,6 +205,32 @@ def __init__( self, "Create Timestamp", layers=[layer_powertools] ) + # EventBridge events can be triggered for resource creation and update + # Note: https://github.com/aws/aws-cdk/issues/17338 + bus_name = ( + f"aws-solutions-{self.node.try_get_context('SOLUTION_ID')}-{Aws.STACK_NAME}" + ) + event_bus = EventBus( + self, + id="Notifications", + event_bus_name=bus_name, + ) + event_bus.node.default_child.add_override( + "Properties.Name", + bus_name, + ) + + create_dataset_group.grant_put_events(event_bus) + create_schema.grant_put_events(event_bus) + create_dataset.grant_put_events(event_bus) + create_dataset_import_job.grant_put_events(event_bus) + create_event_tracker.grant_put_events(event_bus) + create_solution.grant_put_events(event_bus) + create_solution_version.grant_put_events(event_bus) + create_campaign.grant_put_events(event_bus) + create_batch_inference_job.grant_put_events(event_bus) + create_filter.grant_put_events(event_bus) + dataset_management_functions = { "create_schema": create_schema, "create_dataset": create_dataset, @@ -215,11 +251,13 @@ def __init__( dataset_management_functions=dataset_management_functions, create_timestamp=create_timestamp, notifications=notifications, + prepare_input=prepare_input, ).state_machine solution_maintenance_schedule_sfn = ScheduledSolutionMaintenance( self, "Scheduled Solution Maintenance", + prepare_input=prepare_input, create_solution=create_solution, create_solution_version=create_solution_version, create_campaign=create_campaign, @@ -267,14 +305,18 @@ def __init__( definition = Chain.start( Parallel(self, "Manage The Execution") .branch( - create_dataset_group.state( + prepare_input.state( self, - "Create Dataset Group", - backoff_rate=1.02, - interval=Duration.seconds(5), - max_attempts=30, - ) - .next( + "Prepare Input", + ) .next( + create_dataset_group.state( + self, + "Create Dataset Group", + backoff_rate=1.02, + interval=Duration.seconds(5), + max_attempts=30, + ) + ).next( DatasetImportsFragment( self, "Handle Dataset Imports", @@ -407,6 +449,12 @@ def __init__( value=scheduler.scheduler_table.table_name, export_name=f"{Aws.STACK_NAME}-SchedulerTableName", ) + cdk.CfnOutput( + self, + "SchedulerStepFunctionArn", + value=scheduler.state_machine_arn, + export_name=f"{Aws.STACK_NAME}-SchedulerStepFunctionArn", + ) cdk.CfnOutput( self, "Dashboard", @@ -419,3 +467,15 @@ def __init__( value=notifications.topic.topic_arn, export_name=f"{Aws.STACK_NAME}-SNSTopicArn", ) + cdk.CfnOutput( + self, + "EventBusArn", + value=event_bus.event_bus_arn, + export_name=f"{Aws.STACK_NAME}-EventBusArn", + ) + cdk.CfnOutput( + self, + "CreateConfigFunctionArn", + value=create_config.function_arn, + export_name=f"{Aws.STACK_NAME}-CreateConfigFunctionArn", + ) diff --git a/source/infrastructure/personalize/step_functions/batch_inference_jobs_fragment.py b/source/infrastructure/personalize/step_functions/batch_inference_jobs_fragment.py index 4ae83f7..b83ab10 100644 --- a/source/infrastructure/personalize/step_functions/batch_inference_jobs_fragment.py +++ b/source/infrastructure/personalize/step_functions/batch_inference_jobs_fragment.py @@ -25,11 +25,11 @@ ) from aws_cdk.core import Construct, Duration +from aws_solutions.scheduler.cdk.construct import Scheduler +from aws_solutions.scheduler.cdk.scheduler_fragment import SchedulerFragment from personalize.aws_lambda.functions import ( CreateBatchInferenceJob, ) -from personalize.scheduler import Scheduler -from personalize.step_functions.scheduler_fragment import SchedulerFragment TEMPORARY_PATH = "$._tmp" BATCH_INFERENCE_JOB_PATH = "$.batchInferenceJob" diff --git a/source/infrastructure/personalize/step_functions/dataset_import_fragment.py b/source/infrastructure/personalize/step_functions/dataset_import_fragment.py index f4d1141..ca00627 100644 --- a/source/infrastructure/personalize/step_functions/dataset_import_fragment.py +++ b/source/infrastructure/personalize/step_functions/dataset_import_fragment.py @@ -69,7 +69,8 @@ def __init__( }, }, "workflowConfig": { - "maxAge.$": "$.datasetGroup.workflowConfig.maxAge" + "maxAge.$": "$.datasetGroup.workflowConfig.maxAge", # NOSONAR (python:S1192) - string for clarity + "timeStarted.$": "$$.State.EnteredTime" # NOSONAR (python:S1192) - string for clarity } }), result_path=JsonPath.DISCARD, @@ -86,7 +87,8 @@ def __init__( }, }, "workflowConfig": { - "maxAge.$": "$.datasetGroup.workflowConfig.maxAge" + "maxAge.$": "$.datasetGroup.workflowConfig.maxAge", # NOSONAR (python:S1192) - string for clarity + "timeStarted.$": "$$.State.EnteredTime", # NOSONAR (python:S1192) - string for clarity } }), result_path=JsonPath.DISCARD, @@ -101,10 +103,16 @@ def __init__( result_path=f"$.datasets.{id.lower()}.schema.serviceConfig") .next(create_dataset.state(self, f"Create {id} Dataset", payload=TaskInput.from_object({ - "name.$": f"$.datasets.{id.lower()}.dataset.serviceConfig.name", - "schemaArn.$": f"$.datasets.{id.lower()}.schema.serviceConfig.schemaArn", - "datasetGroupArn.$": "$.datasetGroup.serviceConfig.datasetGroupArn", - "datasetType": f"{id.lower()}", + "serviceConfig": { + "name.$": f"$.datasets.{id.lower()}.dataset.serviceConfig.name", + "schemaArn.$": f"$.datasets.{id.lower()}.schema.serviceConfig.schemaArn", + "datasetGroupArn.$": "$.datasetGroup.serviceConfig.datasetGroupArn", + "datasetType": f"{id.lower()}", + }, + "workflowConfig": { + "maxAge.$": "$.datasetGroup.workflowConfig.maxAge", + "timeStarted.$": "$$.State.EnteredTime", + } }), result_path=f"$.datasets.{id.lower()}.dataset.serviceConfig", **retry_config)) diff --git a/source/infrastructure/personalize/step_functions/scheduled_dataset_import.py b/source/infrastructure/personalize/step_functions/scheduled_dataset_import.py index 9deeab9..4438aaf 100644 --- a/source/infrastructure/personalize/step_functions/scheduled_dataset_import.py +++ b/source/infrastructure/personalize/step_functions/scheduled_dataset_import.py @@ -17,7 +17,7 @@ from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_name import ResourceName from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep from personalize.step_functions.dataset_imports_fragment import DatasetImportsFragment from personalize.step_functions.failure_fragment import FailureFragment @@ -30,6 +30,7 @@ def __init__( dataset_management_functions: Dict[str, SolutionStep], create_timestamp: SolutionStep, notifications: SolutionStep, + prepare_input: SolutionStep, ): super().__init__(scope, construct_id) @@ -45,7 +46,9 @@ def __init__( .branch( create_timestamp.state( self, "Set Current Timestamp", result_path="$.currentDate" - ).next( + ) + .next(prepare_input.state(self, "Prepare Input")) + .next( DatasetImportsFragment( self, "Handle Periodic Dataset Imports", diff --git a/source/infrastructure/personalize/step_functions/scheduled_solution_maintenance.py b/source/infrastructure/personalize/step_functions/scheduled_solution_maintenance.py index 57318da..b97ee78 100644 --- a/source/infrastructure/personalize/step_functions/scheduled_solution_maintenance.py +++ b/source/infrastructure/personalize/step_functions/scheduled_solution_maintenance.py @@ -16,13 +16,14 @@ from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_name import ResourceName from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep from personalize.aws_lambda.functions import ( CreateBatchInferenceJob, CreateSolution, CreateSolutionVersion, CreateCampaign, ) -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from personalize.aws_lambda.functions.prepare_input import PrepareInput from personalize.step_functions.failure_fragment import FailureFragment from personalize.step_functions.solution_fragment import SolutionFragment @@ -36,6 +37,7 @@ def __init__( create_solution_version: CreateSolutionVersion, create_campaign: CreateCampaign, create_batch_inference_job: CreateBatchInferenceJob, + prepare_input: PrepareInput, create_timestamp: SolutionStep, notifications: SolutionStep, ): @@ -56,7 +58,9 @@ def __init__( .branch( create_timestamp.state( self, "Set Current Timestamp", result_path="$.currentDate" - ).next( + ) + .next(prepare_input.state(self, "Prepare Input")) + .next( SolutionFragment( self, "Handle Periodic Solution Maintenance", diff --git a/source/infrastructure/personalize/step_functions/scheduler_fragment.py b/source/infrastructure/personalize/step_functions/scheduler_fragment.py index cab5af8..28d860f 100644 --- a/source/infrastructure/personalize/step_functions/scheduler_fragment.py +++ b/source/infrastructure/personalize/step_functions/scheduler_fragment.py @@ -26,7 +26,7 @@ ) from aws_cdk.core import Construct -from personalize.scheduler.base import Scheduler +from aws_solutions.scheduler.cdk.construct import Scheduler class SchedulerFragment(StateMachineFragment): diff --git a/source/infrastructure/personalize/step_functions/solution_fragment.py b/source/infrastructure/personalize/step_functions/solution_fragment.py index 285060a..0396000 100644 --- a/source/infrastructure/personalize/step_functions/solution_fragment.py +++ b/source/infrastructure/personalize/step_functions/solution_fragment.py @@ -26,13 +26,13 @@ ) from aws_cdk.core import Construct, Duration +from aws_solutions.scheduler.cdk.construct import Scheduler from personalize.aws_lambda.functions import ( CreateSolution, CreateSolutionVersion, CreateCampaign, CreateBatchInferenceJob, ) -from personalize.scheduler import Scheduler from personalize.step_functions.batch_inference_jobs_fragment import ( BatchInferenceJobsFragment, ) @@ -96,7 +96,8 @@ def __init__( "trainingMode": "FULL" }, "workflowConfig": { - "maxAge": "365 days" # do not create a new solution version on new file upload + "maxAge": "365 days", # do not create a new solution version on new file upload + "timeStarted.$": "$$.State.EnteredTime", } }, result_path = "$.solution.solutionVersion", # NOSONAR (python:S1192) - string for clarity @@ -155,7 +156,8 @@ def __init__( }, "workflowConfig": { "maxAge.$": "$.solution.solutionVersion.workflowConfig.maxAge", - "solutionVersionArn.$": f"{TEMPORARY_PATH}.errorInfo.errorMessage" + "solutionVersionArn.$": f"{TEMPORARY_PATH}.errorInfo.errorMessage", + "timeStarted.$": "$$.State.EnteredTime", } }, result_path="$.solution.solutionVersion" diff --git a/source/infrastructure/setup.py b/source/infrastructure/setup.py index 790a2f6..fc3dea2 100644 --- a/source/infrastructure/setup.py +++ b/source/infrastructure/setup.py @@ -35,18 +35,18 @@ author="AWS Solutions Builders", packages=setuptools.find_packages(), install_requires=[ - "aws-cdk.core>=1.95.2", + "aws-cdk.core>=1.126.0", ], - python_requires=">=3.6", + python_requires=">=3.7", classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "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", + "Programming Language :: Python :: 3.9", "Topic :: Software Development :: Code Generators", "Topic :: Utilities", "Typing :: Typed", diff --git a/source/pytest.ini b/source/pytest.ini index 70cdb14..14f3217 100644 --- a/source/pytest.ini +++ b/source/pytest.ini @@ -8,6 +8,7 @@ env = AWS_REGION=us-east-1 AWS_DEFAULT_REGION=us-east-1 DDB_SCHEDULES_TABLE=scheduler + DDB_SCHEDULER_STEPFUNCTION=arn:aws:states:us-east-1:111111111111:stateMachine:personalizestack-personalize-scheduler POWERTOOLS_SERVICE_NAME=personalize_solution_teststack POWERTOOLS_METRICS_NAMESPACE=personalize_solution_teststack norecursedirs = cdk.out* diff --git a/source/requirements-dev.txt b/source/requirements-dev.txt index 8426e0a..c81dec0 100644 --- a/source/requirements-dev.txt +++ b/source/requirements-dev.txt @@ -1,9 +1,9 @@ avro==1.10.2 black boto3 -aws_cdk.core>=1.123.0 -aws_cdk.aws_stepfunctions_tasks>=1.123.0 -aws_solutions_constructs.aws_lambda_sns>=1.123.0 +aws_cdk.core==1.126.0 +aws_cdk.aws_stepfunctions_tasks==1.126.0 +aws_solutions_constructs.aws_lambda_sns==1.126.0 requests==2.24.0 crhelper==2.0.6 cronex==0.1.3.1 @@ -13,9 +13,13 @@ pytest pytest-cov>=2.11.1 pytest-env>=0.6.2 pytest-mock>=3.5.1 +pyyaml==5.4.1 +responses==0.14.0 tenacity>=8.0.1 -e cdk_solution_helper_py/helpers_cdk -e cdk_solution_helper_py/helpers_common -aws-lambda-powertools==1.15.0 +-e scheduler/cdk +-e scheduler/common +aws-lambda-powertools>=1.15.0 docker==5.0.0 -e infrastructure \ No newline at end of file diff --git a/source/scheduler/CHANGELOG.md b/source/scheduler/CHANGELOG.md new file mode 100644 index 0000000..0bc7be1 --- /dev/null +++ b/source/scheduler/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0] - 2021-11-11 +### Added +- initial release + diff --git a/source/scheduler/README.md b/source/scheduler/README.md new file mode 100644 index 0000000..6a0e43d --- /dev/null +++ b/source/scheduler/README.md @@ -0,0 +1,218 @@ +# AWS Solutions Step Functions Scheduler +## Scheduling for AWS Step Functions + +This tooling adds scheduling support for AWS Step Functions via a set of libraries and CDK packages. + +This README summarizes using the scheduler. + +## Prerequisites + +Install this package. It requires at least: + +- Python 3.7 +- AWS CDK version 1.126.0 or higher + +To install the packages: + +``` +pip install /scheduler/cdk # where is the path to the scheduler namespace package +pip install /scheduler/common # where is the path to the scheduler namespace package +``` + +## 1. Add the scheduler to your CDK application + +```python +from pathlib import Path + +from aws_cdk.core import Construct +from aws_cdk.aws_stepfunctions import StateMachine + +from aws_solutions.cdk import CDKSolution +from aws_solutions.cdk.stack import SolutionStack +from aws_solutions.scheduler.cdk.construct import Scheduler + + +solution = CDKSolution(cdk_json_path=Path(__file__).parent.absolute() / "cdk.json") + + +class MyStack(SolutionStack): + def __init__(self, scope: Construct, construct_id: str, description: str, template_filename, state_machine: StateMachine, **kwargs): + super().__init__(scope, construct_id, description, template_filename, **kwargs) + + scheduler = Scheduler(self, "Scheduler") + scheduler.grant_invoke(state_machine) +``` + +## 2. Allow a state machine to create new schedules + +You may have an existing `StateMachine` you wish to create schedules with - to do so, use the `SchedulerFragment`. + +```python +# creates a scheduled item called "my-schedule-suffix" - typically you will use part of the state input for the suffix. +SchedulerFragment( + self, + schedule_for="my schedule", + schedule_for_suffix="suffix", + scheduler=scheduler, + target=state_machine, + schedule_path="$.path.to.cron.expression", + schedule_input={ + "static_input": "value", + "derived_input.$": "$.field_in_state_input", + }, +) +``` + +# 3. Check the status of schedules using the included CLI + +This package also provides a CLI `aws-solutions-scheduler`. This CLI can be used to control the scheduler and establish +schedules for the [Maintaining Personalized Experiences with Machine Learning](https://aws.amazon.com/solutions/implementations/maintaining-personalized-experiences-with-ml/) +solution. + +### Installation + +It is recommended that you perform the following steps in a dedicated virtual environment: + +```shell +cd source +pip install --upgrade pip +pip install cdk_solution_helper_py/helpers_common +pip install scheduler/common +``` + +### Usage + +```shell +Usage: aws-solutions-scheduler [OPTIONS] COMMAND [ARGS]... + + Scheduler CLI + +Options: + -s, --stack TEXT [required] + -r, --region TEXT [required] + --scheduler-table-name-output TEXT + --scheduler-stepfunction-arn-output TEXT + --help Show this message and exit. + +Commands: + activate Activate a scheduled task + deactivate Deactivate a scheduled task + describe Describe a scheduled task + import-dataset-group Create a new configuration from an existing... + list List all scheduled tasks +``` + +#### Create new schedule(s) for an Amazon Personalize dataset group + +Schedules for dataset import, solution version FULL and UPDATE retraining can be established using the CLI for dataset +groups in Amazon Personalize. This example creates a weekly schedule for full dataset import (`-i`) and for full +solution version retraining (-f) + +```shell +> aws-solutions-scheduler -s PersonalizeStack -r us-east-1 import-dataset-group -d item-recommender -i "cron(0 0 ? * 1 *)" -f "item-recommender-user-personalization@cron(0 3 ? * 1 *)" -p train/item-recommender/config.json +``` + +#### Listing Schedules + +```shell +> aws-solutions-scheduler -s PersonalizeStack -r us-east-1 list +``` +

+See sample result + +```json +{ + "tasks": [ + "personalize-dataset-import-item-recommender", + "solution-maintenance-full-item-recommender-user-personalization" + ] +} +``` + +
+ +#### Describing Schedules + +```shell +> aws-solutions-scheduler -s PersonalizeStack -r us-east-1 describe --task personalize-dataset-import-item-recommender +``` +
+See sample result + +```json +{ + "task": { + "active": true, + "name": "personalize-dataset-import-item-recommender", + "schedule": "cron(*/15 * * * ? *)", + "step_function": "arn:aws:states:us-east-1:111122223333:stateMachine:personalizestack-periodic-dataset-import-aaaaaaaaaaaa", + "version": "v1" + } +} +``` + +
+ +#### Activating Schedules + +Deactivate schedules can be activated + +```shell +> aws-solutions-scheduler -s PersonalizeStack -r us-east-1 activate --task personalize-dataset-import-item-recommender +``` +
+See sample result + +```json +{ + "task": { + "active": true, + "name": "personalize-dataset-import-item-recommender", + "schedule": "cron(0 0 ? * 1 *)", + "step_function": "arn:aws:states:us-east-1:111122223333:stateMachine:personalizestack-periodic-dataset-import-aaaaaaaaaaaa", + "version": "v1" + } +} +``` + +
+ +#### Deactivating Schedules + +Deactivate schedules can be activated + +```shell +> aws-solutions-scheduler -s PersonalizeStack -r us-east-1 deactivate --task personalize-dataset-import-item-recommender +``` +
+See sample result + +```json +{ + "task": { + "active": false, + "name": "personalize-dataset-import-item-recommender", + "schedule": "cron(0 0 ? * 1 *)", + "step_function": "arn:aws:states:us-east-1:111122223333:stateMachine:personalizestack-periodic-dataset-import-aaaaaaaaaaaa", + "version": "v1" + } +} +``` + +
+ +*** + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/__init__.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/functions/__init__.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/__init__.py similarity index 81% rename from source/infrastructure/personalize/scheduler/aws_lambda/functions/__init__.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/__init__.py index f6370b0..aed94dd 100644 --- a/source/infrastructure/personalize/scheduler/aws_lambda/functions/__init__.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/__init__.py @@ -11,15 +11,13 @@ # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### -from personalize.scheduler.aws_lambda.functions.create_scheduled_task import ( +from aws_solutions.scheduler.cdk.aws_lambda.create_scheduled_task import ( CreateScheduledTask, ) -from personalize.scheduler.aws_lambda.functions.delete_scheduled_task import ( +from aws_solutions.scheduler.cdk.aws_lambda.delete_scheduled_task import ( DeleteScheduledTask, ) -from personalize.scheduler.aws_lambda.functions.read_scheduled_task import ( - ReadScheduledTask, -) -from personalize.scheduler.aws_lambda.functions.update_scheduled_task import ( +from aws_solutions.scheduler.cdk.aws_lambda.read_scheduled_task import ReadScheduledTask +from aws_solutions.scheduler.cdk.aws_lambda.update_scheduled_task import ( UpdateScheduledTask, ) diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/functions/create_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py similarity index 96% rename from source/infrastructure/personalize/scheduler/aws_lambda/functions/create_scheduled_task.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py index 1fd4a2d..90a8da5 100644 --- a/source/infrastructure/personalize/scheduler/aws_lambda/functions/create_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/create_scheduled_task.py @@ -19,7 +19,7 @@ from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class CreateScheduledTask(SolutionStep): @@ -43,7 +43,7 @@ def __init__( layers=layers, failure_state=failure_state, function="create_schedule", - entrypoint=Path(__file__).parents[5].resolve() + entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/functions/delete_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py similarity index 95% rename from source/infrastructure/personalize/scheduler/aws_lambda/functions/delete_scheduled_task.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py index 67cd8ca..cd4ab6c 100644 --- a/source/infrastructure/personalize/scheduler/aws_lambda/functions/delete_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/delete_scheduled_task.py @@ -19,7 +19,7 @@ from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class DeleteScheduledTask(SolutionStep): @@ -41,7 +41,7 @@ def __init__( layers=layers, failure_state=failure_state, function="delete_schedule", - entrypoint=Path(__file__).parents[5].resolve() + entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", diff --git a/source/aws_lambda/get_next_scheduled_event/build.gradle b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/build.gradle similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/build.gradle rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/build.gradle diff --git a/source/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.jar b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.jar rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.jar diff --git a/source/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.properties b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.properties rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradle/wrapper/gradle-wrapper.properties diff --git a/source/aws_lambda/get_next_scheduled_event/gradlew b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradlew similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/gradlew rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/gradlew diff --git a/source/aws_lambda/get_next_scheduled_event/settings.gradle b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/settings.gradle similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/settings.gradle rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/settings.gradle diff --git a/source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEvent.java b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEvent.java similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEvent.java rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEvent.java diff --git a/source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleEvent.java b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleEvent.java similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleEvent.java rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleEvent.java diff --git a/source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleException.java b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleException.java similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleException.java rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/main/java/com/amazonaws/solutions/schedule_sfn_task/ScheduleException.java diff --git a/source/aws_lambda/get_next_scheduled_event/src/test/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEventTest.java b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/test/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEventTest.java similarity index 100% rename from source/aws_lambda/get_next_scheduled_event/src/test/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEventTest.java rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/get_next_scheduled_event/src/test/java/com/amazonaws/solutions/schedule_sfn_task/HandleScheduleEventTest.java diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/functions/read_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py similarity index 95% rename from source/infrastructure/personalize/scheduler/aws_lambda/functions/read_scheduled_task.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py index 22a3420..b4c0222 100644 --- a/source/infrastructure/personalize/scheduler/aws_lambda/functions/read_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/read_scheduled_task.py @@ -19,7 +19,7 @@ from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class ReadScheduledTask(SolutionStep): @@ -41,7 +41,7 @@ def __init__( layers=layers, failure_state=failure_state, function="read_schedule", - entrypoint=Path(__file__).parents[5].resolve() + entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/__init__.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/aws_lambda/scheduler/handler.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/handler.py similarity index 94% rename from source/aws_lambda/scheduler/handler.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/handler.py index e22d7af..5cd60cb 100644 --- a/source/aws_lambda/scheduler/handler.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/handler.py @@ -16,9 +16,11 @@ from aws_lambda_powertools import Logger, Tracer, Metrics from aws_lambda_powertools.utilities.typing import LambdaContext -from shared.scheduler.base import Scheduler -from shared.scheduler.task import Task -from shared.scheduler.task_resource import TaskResource +from aws_solutions.scheduler.common import ( + Scheduler, + Task, + TaskResource, +) logger = Logger() tracer = Tracer() diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt new file mode 100644 index 0000000..201fe8d --- /dev/null +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/scheduler/requirements.txt @@ -0,0 +1,6 @@ +avro==1.10.2 +cronex==0.1.3.1 +jmespath==0.10.0 +parsedatetime==2.6 +../../../../../../../scheduler/common +../../../../../../../cdk_solution_helper_py/helpers_common diff --git a/source/infrastructure/personalize/scheduler/aws_lambda/functions/update_scheduled_task.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py similarity index 96% rename from source/infrastructure/personalize/scheduler/aws_lambda/functions/update_scheduled_task.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py index 2876909..22ab0c3 100644 --- a/source/infrastructure/personalize/scheduler/aws_lambda/functions/update_scheduled_task.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/aws_lambda/update_scheduled_task.py @@ -19,7 +19,7 @@ from aws_cdk.aws_stepfunctions import IChainable from aws_cdk.core import Construct -from personalize.aws_lambda.functions.solutionstep import SolutionStep +from aws_solutions.cdk.stepfunctions.solutionstep import SolutionStep class UpdateScheduledTask(SolutionStep): @@ -43,7 +43,7 @@ def __init__( layers=layers, failure_state=failure_state, function="update_schedule", - entrypoint=Path(__file__).parents[5].resolve() + entrypoint=Path(__file__).parents[1].resolve() / "aws_lambda" / "scheduler" / "handler.py", diff --git a/source/infrastructure/personalize/scheduler/base.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py similarity index 97% rename from source/infrastructure/personalize/scheduler/base.py rename to source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py index 401c1c6..ba0586e 100644 --- a/source/infrastructure/personalize/scheduler/base.py +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/construct.py @@ -31,11 +31,11 @@ from aws_cdk.core import Construct, Aws from aws_solutions.cdk.aws_lambda.cfn_custom_resources.resource_name import ResourceName +from aws_solutions.cdk.aws_lambda.environment import Environment from aws_solutions.cdk.aws_lambda.java.function import SolutionsJavaFunction +from aws_solutions.cdk.aws_lambda.layers.aws_lambda_powertools import PowertoolsLayer from aws_solutions.cdk.cfn_nag import add_cfn_nag_suppressions, CfnNagSuppression -from personalize.aws_lambda.functions.environment import Environment -from personalize.aws_lambda.layers import PowertoolsLayer, SolutionsLayer -from personalize.scheduler.aws_lambda.functions import ( +from aws_solutions.scheduler.cdk.aws_lambda import ( CreateScheduledTask, ReadScheduledTask, UpdateScheduledTask, @@ -85,8 +85,7 @@ def __init__(self, scope: Construct, construct_id: str, sync: bool = True): # Layers required for the AWS Lambda Functions provisioned by the Scheduler construct layer_powertools = PowertoolsLayer.get_or_create(self) - layer_solutions = SolutionsLayer.get_or_create(self) - common_layers = [layer_powertools, layer_solutions] + common_layers = [layer_powertools] # CRUD tasks/ states to integrate with the Scheduler self.create_scheduled_task = CreateScheduledTask( @@ -106,7 +105,7 @@ def __init__(self, scope: Construct, construct_id: str, sync: bool = True): ) self.update_scheduled_task = UpdateScheduledTask( self, - "update_scheudled_task", + "update_scheduled_task", layers=common_layers, scheduler_table=self.scheduler_table, state_machine_arn=self.state_machine_arn, @@ -370,7 +369,8 @@ def _scheduler_function( :return: SolutionsJavaFunction """ project_path = ( - Path(__file__).absolute().parents[3] + Path(__file__).absolute().parents[1] + / "cdk" / "aws_lambda" / "get_next_scheduled_event" ) diff --git a/source/scheduler/cdk/aws_solutions/scheduler/cdk/scheduler_fragment.py b/source/scheduler/cdk/aws_solutions/scheduler/cdk/scheduler_fragment.py new file mode 100644 index 0000000..28d860f --- /dev/null +++ b/source/scheduler/cdk/aws_solutions/scheduler/cdk/scheduler_fragment.py @@ -0,0 +1,91 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +import re +from typing import List, Optional, Dict + +from aws_cdk.aws_stepfunctions import ( + StateMachineFragment, + State, + INextable, + StateMachine, + TaskInput, + Pass, + Choice, + Condition, + JsonPath, +) +from aws_cdk.core import Construct + +from aws_solutions.scheduler.cdk.construct import Scheduler + + +class SchedulerFragment(StateMachineFragment): + def __init__( + self, # NOSONAR (python:S107) - allow large number of method parameters + scope: Construct, + schedule_for: str, + schedule_for_suffix: str, + scheduler: Scheduler, + target: StateMachine, + schedule_path: str, + schedule_input_path: Optional[str] = "", + schedule_input: Optional[Dict] = None, + ): + construct_id = " ".join(["Schedule", schedule_for]).strip() + super().__init__(scope, construct_id) + + if not schedule_input_path and not schedule_input: + raise ValueError( + "schedule_input_path or schedule_input must be provided, not both" + ) + schedule_input = schedule_input or schedule_input_path + + schedule_input_key = "input" + if schedule_input_path: + schedule_input_key += ".$" + + # set up the schedule name + schedule_for_task_name = re.sub(r"[^0-9A-Za-z-_]", "-", schedule_for)[:80] + schedule_for_task_name = ( + f"States.Format('{schedule_for_task_name}-{{}}', {schedule_for_suffix})" + ) + + self.not_required = Pass(self, f"{schedule_for.title()} Schedule Not Required") + self.create_schedule = scheduler.create_scheduled_task.state( + self, + f"Create Schedule For {schedule_for.title()}", + payload=TaskInput.from_object( + { + "name.$": schedule_for_task_name, + "schedule.$": schedule_path, + "state_machine": { + "arn": target.state_machine_arn, + schedule_input_key: schedule_input, + }, + } + ), + result_path=JsonPath.DISCARD, + ) + self.start = ( + Choice(self, f"Check if {schedule_for.title()} Schedule Required") + .when(Condition.is_present(schedule_path), self.create_schedule) + .otherwise(self.not_required) + ) + + @property + def start_state(self) -> State: + return self.start.start_state + + @property + def end_states(self) -> List[INextable]: + return [self.not_required, self.create_schedule] diff --git a/source/scheduler/cdk/setup.py b/source/scheduler/cdk/setup.py new file mode 100644 index 0000000..36b6121 --- /dev/null +++ b/source/scheduler/cdk/setup.py @@ -0,0 +1,66 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +import re +from pathlib import Path + +import setuptools + +VERSION_RE = re.compile(r"\#\# \[(?P.*)\]", re.MULTILINE) # NOSONAR + + +def get_version(): + """ + Detect the solution version from the changelog. Latest version on top. + """ + changelog = open(Path(__file__).resolve().parent.parent / "CHANGELOG.md").read() + versions = VERSION_RE.findall(changelog) + if not len(versions): + raise ValueError("use the standard semver format in your CHANGELOG.md") + build_version = versions[0] + print(f"Build Version: {build_version}") + return build_version + + +setuptools.setup( + name="aws-solutions-scheduler-cdk", + version=get_version(), + description="Scheduler CDK Constructs", + long_description=open("../README.md").read(), + author="Amazon Web Services", + url="https://aws.amazon.com/solutions/implementations", + license="Apache License 2.0", + packages=setuptools.find_namespace_packages(), + install_requires=[ + "aws-cdk.core>=1.126.0", + "aws-cdk.aws_lambda>=1.126.0", + "aws-cdk.aws_stepfunctions>=1.126.0", + "Click>=7.1.2", + "boto3>=1.17.52", + ], + python_requires=">=3.7", + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: JavaScript", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Code Generators", + "Topic :: Utilities", + "Typing :: Typed", + ], + zip_safe=False, +) diff --git a/source/aws_lambda/shared/scheduler/__init__.py b/source/scheduler/common/aws_solutions/scheduler/common/__init__.py similarity index 85% rename from source/aws_lambda/shared/scheduler/__init__.py rename to source/scheduler/common/aws_solutions/scheduler/common/__init__.py index 45cd046..77dee24 100644 --- a/source/aws_lambda/shared/scheduler/__init__.py +++ b/source/scheduler/common/aws_solutions/scheduler/common/__init__.py @@ -16,7 +16,8 @@ CRON_ANY_WILDCARD = "?" CRON_MIN_MAX_YEAR = (1970, 2199) -from shared.scheduler.base import Scheduler -from shared.scheduler.schedule import Schedule, ScheduleError -from shared.scheduler.task import Task -from shared.scheduler.task_resource import TaskResource + +from aws_solutions.scheduler.common.base import Scheduler +from aws_solutions.scheduler.common.schedule import Schedule, ScheduleError +from aws_solutions.scheduler.common.task import Task +from aws_solutions.scheduler.common.task_resource import TaskResource diff --git a/source/aws_lambda/shared/scheduler/base.py b/source/scheduler/common/aws_solutions/scheduler/common/base.py similarity index 92% rename from source/aws_lambda/shared/scheduler/base.py rename to source/scheduler/common/aws_solutions/scheduler/common/base.py index df8946a..fe9aefc 100644 --- a/source/aws_lambda/shared/scheduler/base.py +++ b/source/scheduler/common/aws_solutions/scheduler/common/base.py @@ -21,8 +21,8 @@ from aws_lambda_powertools.metrics import MetricUnit from aws_solutions.core import get_service_client, get_service_resource -from shared.scheduler import TASK_PK -from shared.scheduler.task import Task +from aws_solutions.scheduler.common import TASK_PK +from aws_solutions.scheduler.common.task import Task logger = Logger() metrics = Metrics(service="Scheduler") @@ -47,11 +47,21 @@ def dynamo_to_python(obj: Optional[Dict]) -> Optional[Task]: class Scheduler: """Create schedules for events that invoke a step function""" - def __init__(self): + def __init__(self, table_name=None, stepfunction=None): self.ddb = get_service_resource("dynamodb") self.ddb_cli = self.ddb.meta.client - self.table_name = os.environ.get("DDB_SCHEDULES_TABLE") - self.stepfunction = os.environ.get("DDB_SCHEDULER_STEPFUNCTION") + self.table_name = os.environ.get("DDB_SCHEDULES_TABLE", table_name) + self.stepfunction = os.environ.get("DDB_SCHEDULER_STEPFUNCTION", stepfunction) + + if not self.table_name: + raise ValueError( + "requires table_name at initialization or DDB_SCHEDULES_TABLE env var" + ) + if not self.stepfunction: + raise ValueError( + "requires stepfunction at initialization or DDB_SCHEDULER_STEPFUNCTION env var" + ) + self.sfn_cli = get_service_client("stepfunctions") self.table = self.ddb.Table(self.table_name) @@ -158,6 +168,10 @@ def list(self) -> Generator[str, None, None]: start_key = response.get("LastEvaluatedKey", None) done = start_key is None + def is_enabled(self, task: Task) -> bool: + arn = self._get_running_execution_arn(task) + return True if arn else False + def _get_running_execution_arn(self, task: Task) -> Optional[str]: paginator = self.sfn_cli.get_paginator("list_executions") iterator = paginator.paginate( @@ -183,6 +197,9 @@ def _get_running_execution_arn(self, task: Task) -> Optional[str]: return execution_arn return None + def deactivate(self, task: Task) -> None: + self._disable_schedule(task) + def _disable_schedule(self, task: Task) -> None: execution_arn = self._get_running_execution_arn(task) if execution_arn: @@ -195,6 +212,9 @@ def _disable_schedule(self, task: Task) -> None: else: logger.info(f"{task.name} already disabled") + def activate(self, task: Task) -> None: + self._enable_schedule(task) + def _enable_schedule(self, task: Task) -> None: execution_arn = self._get_running_execution_arn(task) if execution_arn: diff --git a/source/aws_lambda/shared/scheduler/schedule.py b/source/scheduler/common/aws_solutions/scheduler/common/schedule.py similarity index 98% rename from source/aws_lambda/shared/scheduler/schedule.py rename to source/scheduler/common/aws_solutions/scheduler/common/schedule.py index 2316470..d77f5cd 100644 --- a/source/aws_lambda/shared/scheduler/schedule.py +++ b/source/scheduler/common/aws_solutions/scheduler/common/schedule.py @@ -16,7 +16,7 @@ import cronex -from shared.scheduler import CRON_ANY_WILDCARD, CRON_MIN_MAX_YEAR +from aws_solutions.scheduler.common import CRON_ANY_WILDCARD, CRON_MIN_MAX_YEAR class ScheduleError(ValueError): diff --git a/source/scheduler/common/aws_solutions/scheduler/common/scripts/__init__.py b/source/scheduler/common/aws_solutions/scheduler/common/scripts/__init__.py new file mode 100644 index 0000000..ef2f9eb --- /dev/null +++ b/source/scheduler/common/aws_solutions/scheduler/common/scripts/__init__.py @@ -0,0 +1,12 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### diff --git a/source/scheduler/common/aws_solutions/scheduler/common/scripts/scheduler_cli.py b/source/scheduler/common/aws_solutions/scheduler/common/scripts/scheduler_cli.py new file mode 100644 index 0000000..4c9d60b --- /dev/null +++ b/source/scheduler/common/aws_solutions/scheduler/common/scripts/scheduler_cli.py @@ -0,0 +1,355 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +import json +import os +from typing import List, Tuple, Dict, Any + +import boto3 +import click +from rich import print_json + +from aws_solutions.scheduler.common import Scheduler +from aws_solutions.scheduler.common.base import logger + +logger.setLevel("ERROR") + + +def get_stack_output_value(stack, key: str) -> str: + """ + Get a stack output value + :param stack: the boto3 stack resource + :param key: the output key + :return: str + """ + results = [i for i in stack.outputs if i["OutputKey"] == key] + if not results: + raise ValueError(f"could not find output with key {key} in stack") + return results[0]["OutputValue"] + + +def get_stack_tag_value(stack, key: str) -> str: + """ + Get a stack tag value + :param stack: the boto3 stack resource + :param key: the tag key + :return: str + """ + results = [i for i in stack.tags if i["Key"] == key] + if not results: + raise ValueError(f"could not find tag with key {key} in stack") + return results[0]["Value"] + + +def setup_cli_env(stack, region: str) -> None: + """ + Set the environment variables required by the scheduler + :param stack: the stack name + :param region: the AWS region + :return: None + """ + os.environ["AWS_REGION"] = region + os.environ["SOLUTION_ID"] = get_stack_tag_value(stack, "SOLUTION_ID") + os.environ["SOLUTION_VERSION"] = f"{get_stack_tag_value(stack, 'SOLUTION_VERSION')}" + + +@click.group() +@click.option("-s", "--stack", required=True, envvar="SCHEDULER_STACK") +@click.option("-r", "--region", required=True, envvar="AWS_REGION") +@click.option("--scheduler-table-name-output", default="SchedulerTableName") +@click.option("--scheduler-stepfunction-arn-output", default="SchedulerStepFunctionArn") +@click.pass_context +def cli( + ctx, + stack: str, + region: str, + scheduler_table_name_output: str, + scheduler_stepfunction_arn_output: str, +) -> None: + """ + Scheduler CLI + \f + + :param ctx: the click context + :param stack: the AWS CloudFormation stack name + :param region: the AWS region + :param scheduler_table_name_output: the scheduler table name + :param scheduler_stepfunction_arn_output: the scheduler step function ARN + :return: None + """ + ctx.ensure_object(dict) + + cloudformation = boto3.resource("cloudformation", region_name=region) + stack = cloudformation.Stack(stack) + setup_cli_env(stack, region) + + ctx.obj["SCHEDULER"] = Scheduler( + table_name=get_stack_output_value(stack, scheduler_table_name_output), + stepfunction=get_stack_output_value(stack, scheduler_stepfunction_arn_output), + ) + ctx.obj["REGION"] = region + ctx.obj["STACK"] = stack + + +@cli.command("list") +@click.pass_context +def list_command(ctx) -> None: + """ + List all scheduled tasks + \f + + :param ctx: the click context + :return: None + """ + scheduler: Scheduler = ctx.obj["SCHEDULER"] + tasks = [] + + for task in scheduler.list(): + tasks.append(task) + tasks = sorted(tasks) + + print_json(data={"tasks": tasks}) + + +def _describe(ctx, task: str) -> None: + """ + Describe a scheduled task + :param ctx: the click context + :param task: the task name + :return: None + """ + scheduler: Scheduler = ctx.obj["SCHEDULER"] + + tracker = scheduler.read(task, 0) + latest = scheduler.read(task, int(tracker.latest)) + + print_json( + data={ + "task": { + "active": scheduler.is_enabled(latest), + "name": latest.name, + "schedule": latest.schedule.expression, + "step_function": latest.state_machine.get("arn"), + "version": f"v{tracker.latest}", + } + } + ) + + +@cli.command() +@click.option("-t", "--task", required=True) +@click.pass_context +def describe(ctx, task: str) -> Any: + """ + Describe a scheduled task + \f + + :param ctx: the click context + :param task: the task + :return: ctx click context + """ + return _describe(ctx, task) + + +@cli.command() +@click.option("-t", "--task", required=True) +@click.pass_context +def activate(ctx, task: str) -> None: + """ + Activate a scheduled task + \f + + :param ctx: the click context + :param task: the task + :return: None + """ + scheduler: Scheduler = ctx.obj["SCHEDULER"] + + tracker = scheduler.read(task, 0) + latest = scheduler.read(task, int(tracker.latest)) + + scheduler.activate(latest) + _describe(ctx, task) + + +@cli.command() +@click.option("-t", "--task", required=True) +@click.pass_context +def deactivate(ctx, task) -> None: + """ + Deactivate a scheduled task + \f + + :param ctx: the click context + :param task: the task + :return: None + """ + scheduler: Scheduler = ctx.obj["SCHEDULER"] + + tracker = scheduler.read(task, 0) + if not tracker: + raise click.ClickException(f"task {task} does not exist") + latest = scheduler.read(task, int(tracker.latest)) + + scheduler.deactivate(latest) + _describe(ctx=ctx, task=latest) + + +def _validate_path(ctx, param, value) -> str: + """ + Callback to validate the path parameter + :param ctx: the click context + :param param: the click parameter + :param value: the click parameter value + :return: str + """ + if not value.startswith("train/"): + raise click.BadParameter("must start with 'train/") + if not value.endswith(".json"): + raise click.BadParameter("must end with a suffix of .json") + return value + + +def _validate_schedules(ctx, param, value) -> Tuple[Tuple[str, str], ...]: + """ + Callback to validate the schedule parameters + :param ctx: the click context + :param param: the click parameters + :param value: the click parameter values + :return: Tuple[Tuple[str, str], ...] + """ + if len(value) == 0: + return value + + values = [] + for idx, item in enumerate(value): + solution, _, schedule = item.partition("@") + if solution and schedule: + values.append((solution, schedule)) + else: + raise click.BadParameter( + "format must be solution_name@schedule_expression e.g solution@cron(0 */12 * * ? *)" + ) + return tuple(set(values)) + + +def get_payload( + dataset_group: str, + import_schedule: str, + update_schedule: List[Tuple[str, str]], + full_schedule: List[Tuple[str, str]], +) -> Dict: + """ + Gets the AWS Lambda Function payload for setting up schedules/ importing a dataset group into the solution + :param dataset_group: dataset group name + :param import_schedule: import schedule (e.g. "cron(* * * * ? *)") + :param update_schedule: update schedules (eg. ("name","cron(* * * * ? *))) + :param full_schedule: full schedules (eg. ("name","cron(* * * * ? *))) + :return: Dict + """ + payload = { + "datasetGroupName": dataset_group, + } + if import_schedule: + payload.setdefault("schedules", {})["import"] = import_schedule + if update_schedule: + for solution, schedule in update_schedule: + payload.setdefault("schedules", {}).setdefault("solutions", {})[ + solution + ] = {"update": schedule} + if full_schedule: + for solution, schedule in full_schedule: + payload.setdefault("schedules", {}).setdefault("solutions", {})[ + solution + ] = {"full": schedule} + return payload + + +@cli.command() +@click.option( + "-d", "--dataset-group", required=True, help="dataset group name to import" +) +@click.option("-p", "--path", required=True, callback=_validate_path, help="s3 key") +@click.option("-i", "--import-schedule", help="cron schedule for dataset import") +@click.option( + "-f", + "--full-schedule", + multiple=True, + callback=_validate_schedules, + help="cron schedules for FULL solution version updates", +) +@click.option( + "-u", + "--update-schedule", + multiple=True, + callback=_validate_schedules, + help="cron schedules for UPDATE solution version updates", +) +@click.pass_context +def import_dataset_group( + ctx, dataset_group, path, import_schedule, full_schedule, update_schedule +): + """ + Create a new configuration from an existing dataset group in Amazon Personalize and add scheduled tasks + \f + + :param ctx: the click context + :param dataset_group: the dataset group name + :param path: the full s3 key of the configuration file + :param import_schedule: the import cron schedule + :param full_schedule: the full schedules + :param update_schedule: the update schedules + """ + region = ctx.obj["REGION"] + stack = ctx.obj["STACK"] + cli_lambda = boto3.client("lambda", region_name=region) + cli_s3 = boto3.client("s3", region_name=region) + cli_sts = boto3.client("sts", region_name=region) + config_function = get_stack_output_value(stack, "CreateConfigFunctionArn") + bucket = get_stack_output_value(stack, "PersonalizeBucketName") + account = cli_sts.get_caller_identity()["Account"] + + payload = get_payload( + dataset_group=dataset_group, + import_schedule=import_schedule, + update_schedule=update_schedule, + full_schedule=full_schedule, + ) + + # Run the lambda function to generate the configuration and get the result + result = cli_lambda.invoke( + FunctionName=config_function, + InvocationType="RequestResponse", + Payload=json.dumps(payload).encode("utf-8"), + ) + status = result.get("StatusCode") + if status != 200: + raise click.ClickException( + "there was an error generating configuration ({status})" + ) + if result.get("FunctionError"): + error_message = json.loads(result.get("Payload").read()).get("errorMessage") + raise click.ClickException( + f"Could not generate configuration for {dataset_group}: {error_message}" + ) + + # to trigger the workflow and set up new schedules, upload the returned configuration to S3. + cli_s3.upload_fileobj( + Fileobj=result.get("Payload"), + Bucket=bucket, + Key=path, + ExtraArgs={"ExpectedBucketOwner": account}, + ) + + +if __name__ == "__main__": + cli() diff --git a/source/aws_lambda/shared/scheduler/task.py b/source/scheduler/common/aws_solutions/scheduler/common/task.py similarity index 96% rename from source/aws_lambda/shared/scheduler/task.py rename to source/scheduler/common/aws_solutions/scheduler/common/task.py index 8ed7809..840e071 100644 --- a/source/aws_lambda/shared/scheduler/task.py +++ b/source/scheduler/common/aws_solutions/scheduler/common/task.py @@ -18,8 +18,8 @@ from typing import Union, Dict from uuid import uuid4 -from shared.scheduler import TASK_PK, TASK_SK -from shared.scheduler.schedule import Schedule +from aws_solutions.scheduler.common import TASK_PK, TASK_SK +from aws_solutions.scheduler.common.schedule import Schedule @dataclass diff --git a/source/aws_lambda/shared/scheduler/task_resource.py b/source/scheduler/common/aws_solutions/scheduler/common/task_resource.py similarity index 94% rename from source/aws_lambda/shared/scheduler/task_resource.py rename to source/scheduler/common/aws_solutions/scheduler/common/task_resource.py index 1950d13..854fe1a 100644 --- a/source/aws_lambda/shared/scheduler/task_resource.py +++ b/source/scheduler/common/aws_solutions/scheduler/common/task_resource.py @@ -14,8 +14,8 @@ import dataclasses import functools -from shared.scheduler.schedule import Schedule -from shared.scheduler.task import Task +from aws_solutions.scheduler.common.schedule import Schedule +from aws_solutions.scheduler.common.task import Task class TaskResource: diff --git a/source/scheduler/common/setup.py b/source/scheduler/common/setup.py new file mode 100644 index 0000000..eccc3b3 --- /dev/null +++ b/source/scheduler/common/setup.py @@ -0,0 +1,75 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +import re +from pathlib import Path + +import setuptools + +VERSION_RE = re.compile(r"\#\# \[(?P.*)\]", re.MULTILINE) # NOSONAR + + +def get_version(): + """ + Detect the solution version from the changelog. Latest version on top. + """ + changelog = open(Path(__file__).resolve().parent.parent / "CHANGELOG.md").read() + versions = VERSION_RE.findall(changelog) + if not len(versions): + raise ValueError("use the standard semver format in your CHANGELOG.md") + build_version = versions[0] + print(f"Build Version: {build_version}") + return build_version + + +setuptools.setup( + name="aws-solutions-scheduler-common", + version=get_version(), + description="Scheduler shared libraries and CLI", + long_description=open("../README.md").read(), + author="Amazon Web Services", + url="https://aws.amazon.com/solutions/implementations", + license="Apache License 2.0", + packages=setuptools.find_namespace_packages(), + install_requires=[ + "aws-cdk.core>=1.126.0", + "aws-cdk.aws_lambda>=1.126.0", + "aws-lambda-powertools>=1.21.1", + "aws-solutions-python>=1.0.0", + "Click>=7.1.2", + "cronex==0.1.3.1", + "boto3>=1.17.52", + "requests>=2.24.0", + "crhelper>=2.0.6", + "rich>=10.12.0", + ], + entry_points=""" + [console_scripts] + aws-solutions-scheduler=aws_solutions.scheduler.common.scripts.scheduler_cli:cli + """, + python_requires=">=3.7", + classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: JavaScript", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Code Generators", + "Topic :: Utilities", + "Typing :: Typed", + ], + zip_safe=False, +) diff --git a/source/tests/aws_lambda/create_campaign/test_create_campaign_handler.py b/source/tests/aws_lambda/create_campaign/test_create_campaign_handler.py index 3b6b33c..3f8d181 100644 --- a/source/tests/aws_lambda/create_campaign/test_create_campaign_handler.py +++ b/source/tests/aws_lambda/create_campaign/test_create_campaign_handler.py @@ -11,11 +11,260 @@ # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### +from datetime import datetime, timedelta + import pytest +from dateutil.parser import isoparse +from dateutil.tz import tzlocal +from moto import mock_sts from aws_lambda.create_campaign.handler import lambda_handler +from shared.exceptions import ResourcePending +from shared.resource import Campaign, SolutionVersion def test_create_campaign(): with pytest.raises(ValueError): lambda_handler({}, None) + + +@mock_sts +def test_describe_campaign_response(personalize_stubber, notifier_stubber): + c_name = "cp_name" + sv_arn = SolutionVersion().arn("unit_test", sv_id="12345678") + personalize_stubber.add_response( + method="describe_campaign", + service_response={ + "campaign": { + "campaignArn": Campaign().arn(c_name), + "name": c_name, + "solutionVersionArn": sv_arn, + "minProvisionedTPS": 1, + "status": "ACTIVE", + "lastUpdatedDateTime": datetime.now(tzlocal()), + "creationDateTime": datetime.now(tz=tzlocal()) - timedelta(seconds=100), + } + }, + expected_params={"campaignArn": Campaign().arn(c_name)}, + ) + + result = lambda_handler( + { + "serviceConfig": { + "name": c_name, + "solutionVersionArn": sv_arn, + "minProvisionedTPS": 1, + }, + "workflowConfig": { + "maxAge": "365 days", + "timeStarted": "2021-10-19T15:18:32Z", + }, + }, + None, + ) + + assert notifier_stubber.has_notified_for_complete + assert notifier_stubber.latest_notification_status == "ACTIVE" + + +@mock_sts +def test_create_campaign_response(personalize_stubber, notifier_stubber): + c_name = "cp_name" + sv_arn = SolutionVersion().arn("unit_test", sv_id="12345678") + personalize_stubber.add_client_error( + method="describe_campaign", + service_error_code="ResourceNotFoundException", + expected_params={"campaignArn": Campaign().arn(c_name)}, + ) + personalize_stubber.add_response( + method="create_campaign", + expected_params={ + "name": c_name, + "minProvisionedTPS": 1, + "solutionVersionArn": sv_arn, + }, + service_response={"campaignArn": Campaign().arn(c_name)}, + ) + + with pytest.raises(ResourcePending): + lambda_handler( + { + "serviceConfig": { + "name": c_name, + "solutionVersionArn": sv_arn, + "minProvisionedTPS": 1, + }, + "workflowConfig": { + "maxAge": "365 days", + "timeStarted": "2021-10-19T15:18:32Z", + }, + }, + None, + ) + + assert notifier_stubber.has_notified_for_creation + assert notifier_stubber.latest_notification_status == "CREATING" + + +@mock_sts +def test_update_campaign_start(personalize_stubber, notifier_stubber): + c_name = "cp_name" + sv_arn_old = SolutionVersion().arn("unit_test", sv_id="12345678") + sv_arn_new = SolutionVersion().arn("unit_test", sv_id="01234567") + personalize_stubber.add_response( + method="describe_campaign", + service_response={ + "campaign": { + "campaignArn": Campaign().arn(c_name), + "name": c_name, + "solutionVersionArn": sv_arn_old, + "minProvisionedTPS": 1, + "status": "ACTIVE", + "lastUpdatedDateTime": datetime.now(tzlocal()), + "creationDateTime": datetime.now(tz=tzlocal()) - timedelta(seconds=100), + } + }, + expected_params={"campaignArn": Campaign().arn(c_name)}, + ) + personalize_stubber.add_response( + method="update_campaign", + service_response={ + "campaignArn": Campaign().arn(c_name), + }, + expected_params={ + "campaignArn": Campaign().arn(c_name), + "minProvisionedTPS": 1, + "solutionVersionArn": sv_arn_new, + }, + ) + + with pytest.raises(ResourcePending): + lambda_handler( + { + "serviceConfig": { + "name": c_name, + "solutionVersionArn": sv_arn_new, + "minProvisionedTPS": 1, + }, + "workflowConfig": { + "maxAge": "365 days", + "timeStarted": "2021-10-19T15:18:32Z", + }, + }, + None, + ) + + assert notifier_stubber.has_notified_for_creation + assert notifier_stubber.latest_notification_status == "UPDATING" + + +@mock_sts +def test_describe_campaign_response_updating(personalize_stubber, notifier_stubber): + c_name = "cp_name" + sv_arn_old = SolutionVersion().arn("unit_test", sv_id="12345678") + sv_arn_new = SolutionVersion().arn("unit_test", sv_id="01234567") + personalize_stubber.add_response( + method="describe_campaign", + service_response={ + "campaign": { + "campaignArn": Campaign().arn(c_name), + "name": c_name, + "solutionVersionArn": sv_arn_old, + "minProvisionedTPS": 1, + "status": "ACTIVE", + "lastUpdatedDateTime": datetime.now(tzlocal()) + - timedelta(seconds=1000), + "creationDateTime": datetime.now(tz=tzlocal()) + - timedelta(seconds=1100), + "latestCampaignUpdate": { + "minProvisionedTPS": 1, + "solutionVersionArn": sv_arn_new, + "creationDateTime": datetime.now(tzlocal()), + "lastUpdatedDateTime": datetime.now(tzlocal()), + "status": "UPDATE IN_PROGRESS", + }, + } + }, + expected_params={"campaignArn": Campaign().arn(c_name)}, + ) + personalize_stubber.add_client_error( + method="update_campaign", + service_error_code="ResourceInUseException", + ) + + with pytest.raises(ResourcePending): + result = lambda_handler( + { + "serviceConfig": { + "name": c_name, + "solutionVersionArn": sv_arn_new, + "minProvisionedTPS": 1, + }, + "workflowConfig": { + "maxAge": "365 days", + "timeStarted": "2021-10-19T15:18:32Z", + }, + }, + None, + ) + + assert not notifier_stubber.has_notified_for_complete + assert not notifier_stubber.has_notified_for_creation + + +@mock_sts +def test_describe_campaign_response_updated(personalize_stubber, notifier_stubber): + c_name = "cp_name" + sv_arn_new = SolutionVersion().arn("unit_test", sv_id="01234567") + personalize_stubber.add_response( + method="describe_campaign", + service_response={ + "campaign": { + "campaignArn": Campaign().arn(c_name), + "name": c_name, + "solutionVersionArn": sv_arn_new, + "minProvisionedTPS": 1, + "status": "ACTIVE", + "lastUpdatedDateTime": datetime.now(tzlocal()) + - timedelta(seconds=1000), + "creationDateTime": datetime.now(tz=tzlocal()) + - timedelta(seconds=1100), + "latestCampaignUpdate": { + "minProvisionedTPS": 1, + "solutionVersionArn": sv_arn_new, + "creationDateTime": datetime.now(tzlocal()) + - timedelta(seconds=100), + "lastUpdatedDateTime": datetime.now(tzlocal()), + "status": "ACTIVE", + }, + } + }, + expected_params={"campaignArn": Campaign().arn(c_name)}, + ) + + result = lambda_handler( + { + "serviceConfig": { + "name": c_name, + "solutionVersionArn": sv_arn_new, + "minProvisionedTPS": 1, + }, + "workflowConfig": { + "maxAge": "365 days", + "timeStarted": "2021-10-19T15:18:32Z", + }, + }, + None, + ) + + assert notifier_stubber.has_notified_for_complete + assert not notifier_stubber.has_notified_for_creation + assert notifier_stubber.latest_notification_status == "ACTIVE" + + last_updated = isoparse( + notifier_stubber.get_resource_last_updated(Campaign(), {"campaign": result}) + ) + created = isoparse( + notifier_stubber.get_resource_created(Campaign(), {"campaign": result}) + ) + assert (last_updated - created).seconds == 100 diff --git a/source/tests/aws_lambda/create_config/test_create_config_handler.py b/source/tests/aws_lambda/create_config/test_create_config_handler.py new file mode 100644 index 0000000..cf6cce7 --- /dev/null +++ b/source/tests/aws_lambda/create_config/test_create_config_handler.py @@ -0,0 +1,180 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from aws_lambda.create_config.handler import lambda_handler +from shared.resource import ( + DatasetGroup, + Dataset, + Solution, + Campaign, + SolutionVersion, + BatchInferenceJob, + EventTracker, + Schema, +) + + +def test_create_config(personalize_stubber): + dsg_name = "dsg" + event_tracker_name = "dsgeventtracker" + dataset_name = "dsg/INTERACTIONS" + schema_name = "dsg_interactions_schema" + solution_name = "dsgsolution" + campaign_name = "dsgcampaign" + + dsg_arn = DatasetGroup().arn(dsg_name) + dataset_arn = Dataset().arn(dataset_name) + event_tracker_arn = EventTracker().arn(event_tracker_name) + schema_arn = Schema().arn(schema_name) + solution_arn = Solution().arn(solution_name) + campaign_arn = Campaign().arn(campaign_name) + + personalize_stubber.add_response( + method="list_datasets", + service_response={ + "datasets": [{"name": "dsg_interactions", "datasetArn": dataset_arn}] + }, + ) + personalize_stubber.add_response( + method="list_dataset_import_jobs", service_response={"datasetImportJobs": []} + ) + personalize_stubber.add_response( + method="list_filters", + service_response={"Filters": []}, + expected_params={"datasetGroupArn": dsg_arn}, + ) + personalize_stubber.add_response( + method="list_solutions", + service_response={"solutions": [{"solutionArn": solution_arn}]}, + expected_params={"datasetGroupArn": dsg_arn}, + ) + personalize_stubber.add_response( + method="list_campaigns", + service_response={"campaigns": [{"campaignArn": campaign_arn}]}, + expected_params={"solutionArn": solution_arn}, + ) + personalize_stubber.add_response( + method="list_solution_versions", + service_response={ + "solutionVersions": [ + { + "solutionVersionArn": SolutionVersion().arn( + "dsgsolution", sv_id="aaaaaaaa" + ) + } + ] + }, + expected_params={"solutionArn": solution_arn}, + ) + personalize_stubber.add_response( + method="list_batch_inference_jobs", + service_response={ + "batchInferenceJobs": [ + {"batchInferenceJobArn": BatchInferenceJob().arn("dsgbatch")} + ] + }, + ) + personalize_stubber.add_response( + method="list_event_trackers", + service_response={"eventTrackers": [{"eventTrackerArn": event_tracker_arn}]}, + expected_params={"datasetGroupArn": dsg_arn}, + ) + personalize_stubber.add_response( + method="describe_dataset_group", + service_response={ + "datasetGroup": {"name": dsg_name, "datasetGroupArn": dsg_arn} + }, + expected_params={"datasetGroupArn": dsg_arn}, + ) + personalize_stubber.add_response( + method="describe_event_tracker", + service_response={ + "eventTracker": { + "name": event_tracker_name, + "eventTrackerArn": event_tracker_arn, + } + }, + expected_params={"eventTrackerArn": event_tracker_arn}, + ) + personalize_stubber.add_response( + method="describe_dataset", + service_response={ + "dataset": { + "name": dataset_name, + "datasetArn": dataset_arn, + "schemaArn": schema_arn, + } + }, + expected_params={"datasetArn": dataset_arn}, + ) + personalize_stubber.add_response( + method="describe_schema", + service_response={ + "schema": {"name": schema_name, "schemaArn": schema_arn, "schema": "{}"} + }, + expected_params={"schemaArn": schema_arn}, + ) + personalize_stubber.add_response( + method="describe_solution", + service_response={ + "solution": {"name": solution_name, "solutionArn": solution_arn} + }, + expected_params={"solutionArn": solution_arn}, + ) + personalize_stubber.add_response( + method="describe_campaign", + service_response={ + "campaign": { + "name": campaign_name, + "campaignArn": campaign_arn, + } + }, + expected_params={"campaignArn": campaign_arn}, + ) + + result = lambda_handler( + { + "datasetGroupName": dsg_name, + "schedules": { + "import": "cron(0 */6 * * ? *)", + "solutions": { + solution_name: { + "full": "cron(0 0 ? * 1 *)", + "update": "cron(0 * * * ? *)", + } + }, + }, + }, + None, + ) + assert result["datasetGroup"]["serviceConfig"]["name"] == dsg_name + assert ( + result["datasetGroup"]["workflowConfig"]["schedules"]["import"] + == "cron(0 */6 * * ? *)" + ) + assert result["eventTracker"]["serviceConfig"]["name"] == event_tracker_name + assert not result.get("filters") + assert len(result["solutions"]) == 1 + assert result["solutions"][0]["serviceConfig"]["name"] == solution_name + assert ( + result["solutions"][0]["workflowConfig"]["schedules"]["full"] + == "cron(0 0 ? * 1 *)" + ) + assert ( + result["solutions"][0]["workflowConfig"]["schedules"]["update"] + == "cron(0 * * * ? *)" + ) + assert len(result["solutions"][0]["campaigns"]) == 1 + assert ( + result["solutions"][0]["campaigns"][0]["serviceConfig"]["name"] == campaign_name + ) diff --git a/source/tests/aws_lambda/test_events.py b/source/tests/aws_lambda/test_events.py new file mode 100644 index 0000000..64e76c7 --- /dev/null +++ b/source/tests/aws_lambda/test_events.py @@ -0,0 +1,79 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +from datetime import datetime + +from dateutil.relativedelta import relativedelta +from dateutil.tz import tzlocal + +from shared.events import Notifies +from shared.resource import DatasetGroup + + +def test_notifies_decorator_create(notifier_stubber): + status = "ACTIVE" + + class RequiresNotification: + @Notifies(status=status) + def notifies_something(self, resource, **kwargs): + return {"datasetGroupArn": "SOME_ARN"} + + rn = RequiresNotification() + rn.notifies_something(DatasetGroup(), timeStarted="2021-10-10T10:00:00Z") + + assert notifier_stubber.creation_notifications[0] == { + "resource": "datasetGroup", + "status": "ACTIVE", + "result": { + "datasetGroupArn": "SOME_ARN", + }, + } + assert len(notifier_stubber.creation_notifications) == 1 + assert len(notifier_stubber.completion_notifications) == 0 + + +def test_notifies_decorator_complete(mocker, notifier_stubber): + status = "ACTIVE" + + created = datetime.now(tzlocal()) + updated = created + relativedelta(seconds=120) + + class RequiresNotification: + @Notifies(status=status) + def notifies_something(self, resource, **kwargs): + return { + "datasetGroup": { + "datasetGroupArn": "SOME_ARN", + "creationDateTime": created, + "lastUpdatedDateTime": updated, + "status": "ACTIVE", + } + } + + rn = RequiresNotification() + rn.notifies_something(DatasetGroup(), timeStarted=created) + + assert notifier_stubber.completion_notifications[0] == { + "resource": "datasetGroup", + "result": { + "datasetGroup": { + "datasetGroupArn": "SOME_ARN", + "lastUpdatedDateTime": updated, + "creationDateTime": created, + "status": "ACTIVE", + } + }, + "status": "ACTIVE", + } + assert len(notifier_stubber.creation_notifications) == 0 + assert len(notifier_stubber.completion_notifications) == 1 diff --git a/source/tests/aws_lambda/test_personalize_service.py b/source/tests/aws_lambda/test_personalize_service.py index a4cf79f..32f0d46 100644 --- a/source/tests/aws_lambda/test_personalize_service.py +++ b/source/tests/aws_lambda/test_personalize_service.py @@ -24,11 +24,11 @@ from aws_lambda.shared.personalize_service import ( S3, Personalize, - ServiceModel, Configuration, get_duplicates, ) from shared.exceptions import ResourceNeedsUpdate, ResourceFailed +from shared.personalize.service_model import ServiceModel from shared.resource import Campaign @@ -153,10 +153,10 @@ def test_service_model(personalize_stubber): filter_arn_1 = f"arn:aws:personalize:us-east-1:{'1' * 12}:filter/{filter_name_1}" filter_arn_2 = f"arn:aws:personalize:us-east-1:{'1' * 12}:filter/{filter_name_2}" campaign_arn_1 = ( - f"arn:aws:personalize:us-east-1:{'1' * 12}:filter/{campaign_name_1}" + f"arn:aws:personalize:us-east-1:{'1' * 12}:campaign/{campaign_name_1}" ) campaign_arn_2 = ( - f"arn:aws:personalize:us-east-1:{'1' * 12}:filter/{campaign_name_2}" + f"arn:aws:personalize:us-east-1:{'1' * 12}:campaign/{campaign_name_2}" ) # all dataset groups @@ -201,6 +201,11 @@ def test_service_model(personalize_stubber): expected_params={"solutionArn": solution_arn_1}, service_response={"solutionVersions": []}, ) + personalize_stubber.add_response( + method="list_event_trackers", + expected_params={"datasetGroupArn": dataset_group_arn_1}, + service_response={"eventTrackers": []}, + ) # second dataset group personalize_stubber.add_response( @@ -233,6 +238,11 @@ def test_service_model(personalize_stubber): expected_params={"solutionArn": solution_arn_2}, service_response={"solutionVersions": []}, ) + personalize_stubber.add_response( + method="list_event_trackers", + expected_params={"datasetGroupArn": dataset_group_arn_2}, + service_response={"eventTrackers": []}, + ) sm = ServiceModel(cli) @@ -299,6 +309,7 @@ def test_describe_with_update(mocker): describe_mock.return_value = { "campaign": { "solutionVersionArn": arn, + "campaignArn": Campaign().arn("campaign_name"), } } personalize.describe_default = describe_mock @@ -490,3 +501,61 @@ def test_record_offline_metrics( assert metrics["precision_at_5"] assert metrics["precision_at_10"] assert metrics["precision_at_25"] + + +def test_solution_version_update_validation(): + cfg = Configuration() + cfg.config_dict = { + "solutions": [ + { + "serviceConfig": { + "name": "valid", + "recipeArn": "arn:aws:personalize:::recipe/aws-user-personalization", + }, + "workflowConfig": { + "schedules": { + "full": "cron(0 0 ? * 1 *)", + "update": "cron(0 * * * ? *)", + } + }, + }, + { + "serviceConfig": { + "name": "valid", + "recipeArn": "arn:aws:personalize:::recipe/aws-sims", + }, + "workflowConfig": { + "schedules": { + "full": "cron(0 0 ? * 1 *)", + } + }, + }, + { + "serviceConfig": { + "name": "valid", + "recipeArn": "arn:aws:personalize:::recipe/aws-hrnn-coldstart", + }, + "workflowConfig": { + "schedules": { + "full": "cron(0 0 ? * 1 *)", + "update": "cron(0 * * * ? *)", + } + }, + }, + { + "serviceConfig": { + "name": "invalid", + "recipeArn": "arn:aws:personalize:::recipe/aws-sims", + }, + "workflowConfig": { + "schedules": { + "full": "cron(0 0 ? * 1 *)", + "update": "cron(0 * * * ? *)", + } + }, + }, + ] + } + cfg._validate_solution_update() + assert len(cfg._configuration_errors) == 1 + assert cfg._configuration_errors[0].startswith("solution invalid does not support") diff --git a/source/tests/aws_lambda/test_sfn_middleware.py b/source/tests/aws_lambda/test_sfn_middleware.py index c6b2892..17739ce 100644 --- a/source/tests/aws_lambda/test_sfn_middleware.py +++ b/source/tests/aws_lambda/test_sfn_middleware.py @@ -10,8 +10,8 @@ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # # the specific language governing permissions and limitations under the License. # # ###################################################################################################################### -import datetime import logging +from datetime import datetime from decimal import Decimal import pytest @@ -29,7 +29,9 @@ set_bucket, parse_datetime, Parameter, + set_workflow_config, ) +from shared.resource import DatasetGroup @pytest.fixture @@ -64,7 +66,9 @@ def test_personalize_status_invalid(personalize_resource): @mock_sts -def test_personalize_resource_decorator(personalize_resource, personalize_stubber): +def test_personalize_resource_decorator( + personalize_resource, personalize_stubber, notifier_stubber +): """ The typical workflow is to describe, then create, then raise ResourcePending """ @@ -73,7 +77,11 @@ def test_personalize_resource_decorator(personalize_resource, personalize_stubbe "describe_dataset_group", "ResourceNotFoundException" ) personalize_stubber.add_response( - "create_dataset_group", {}, expected_params={"name": dsg_name} + "create_dataset_group", + service_response={"datasetGroupArn": DatasetGroup().arn(dsg_name)}, + expected_params={ + "name": dsg_name, + }, ) @personalize_resource @@ -87,7 +95,7 @@ def decorated(event, context): @pytest.mark.parametrize( "item,serialized", [ - (datetime.datetime(2020, 1, 1), "2020-01-01T00:00:00"), + (datetime(2020, 1, 1), "2020-01-01T00:00:00"), (Decimal(1), 1), (Decimal(1.5), 1.5), ], @@ -113,11 +121,9 @@ def test_set_defaults_2(): del defaults["currentDate"] del defaults["datasetGroup"] - assert defaults == { - "solutions": [ - {"solutionVersions": [], "campaigns": [], "batchInferenceJobs": []} - ], - } + assert defaults["solutions"][0]["solutionVersions"] == [] + assert defaults["solutions"][0]["campaigns"] == [] + assert defaults["solutions"][0]["batchInferenceJobs"] == [] def test_set_defaults_3(): @@ -204,3 +210,75 @@ def test_parameter_resolution(key, source, path, format_as, default, result): ).resolve(event) == result ) + + +def test_set_workflow_config(): + result = set_workflow_config( + { + "datasetGroup": { + "serviceConfig": {"datasetGroup": "should-not-change"}, + "workflowConfig": {"maxAge": "one day"}, + }, + "eventTracker": { + "serviceConfig": {}, + }, + "datasets": { + "users": { + "dataset": {"serviceConfig": {}}, + "schema": {"serviceConfig": {}}, + }, + "items": { + "dataset": {"serviceConfig": {}}, + "schema": {"serviceConfig": {}}, + }, + "interactions": { + "dataset": {"serviceConfig": {}}, + "schema": {"serviceConfig": {}}, + }, + }, + "filters": [{"serviceConfig": {}}], + "solutions": [ + { + "serviceConfig": {"datasetGroup": "should-not-change"}, + "campaigns": [ + { + "serviceConfig": {}, + "workflowConfig": {"maxAge": "should-not-change"}, + }, + {"serviceConfig": {}}, + ], + "batchInferenceJobs": [ + { + "serviceConfig": {}, + } + ], + }, + {"serviceConfig": {}}, + ], + } + ) + + # all workflowConfig should be set + assert result.get("datasetGroup").get("workflowConfig") + assert all(s.get("workflowConfig") for s in result["solutions"]) + assert all(f.get("workflowConfig") for f in result["filters"]) + assert all(c.get("workflowConfig") for c in result["solutions"][0]["campaigns"]) + assert all( + c.get("workflowConfig") for c in result["solutions"][0]["batchInferenceJobs"] + ) + + # keys under serviceConfig should not change + assert ( + result.get("datasetGroup").get("serviceConfig").get("datasetGroup") + == "should-not-change" + ) + assert ( + result.get("solutions")[0].get("serviceConfig").get("datasetGroup") + == "should-not-change" + ) + + # overrides to the default must remain unchanged + assert ( + result.get("solutions")[0]["campaigns"][0]["workflowConfig"]["maxAge"] + == "should-not-change" + ) diff --git a/source/tests/cdk_solution_helper/test_stack.py b/source/tests/cdk_solution_helper/test_stack.py index 2099b5e..5334d47 100644 --- a/source/tests/cdk_solution_helper/test_stack.py +++ b/source/tests/cdk_solution_helper/test_stack.py @@ -92,12 +92,14 @@ def test_solution_stack(): stack_description = "stack description" stack_filename = "stack-name.template" - app = App(context={"SOLUTION_ID": stack_id}) + app = App(context={"SOLUTION_ID": stack_id, "SOLUTION_VERSION": "v0.0.1"}) SolutionStack(app, "stack", stack_description, stack_filename) template = app.synth().stacks[0].template - assert template["Description"] == f"({stack_id}) {stack_description}" + assert ( + template["Description"] == f"({stack_id}) - {stack_description}. Version v0.0.1" + ) assert template["Metadata"] == { "AWS::CloudFormation::Interface": { "ParameterGroups": [], diff --git a/source/tests/conftest.py b/source/tests/conftest.py index 37f79f5..2ba35a6 100644 --- a/source/tests/conftest.py +++ b/source/tests/conftest.py @@ -14,6 +14,7 @@ import sys from pathlib import Path from tempfile import TemporaryDirectory +from typing import Dict import jsii import pytest @@ -35,6 +36,10 @@ sys.path.insert(0, shared_path) +from shared.notifiers.base import Notifier +from shared.resource import Resource + + class Solution: id = "SO0170test" version = "99.99.99" @@ -61,6 +66,9 @@ def solution_env(): os.environ[ "STATE_MACHINE_ARN" ] = f"arn:aws:states:us-east-1:{'1'*12}:stateMachine:personalize-workflow" + os.environ[ + "EVENT_BUS_ARN" + ] = f"arn:aws:events:us-east-1:{'1'*12}:event-bus/PersonalizeEventBus" yield @@ -123,3 +131,60 @@ def cdk_lambda_mocks(mocker, request): @pytest.fixture def configuration_path(): return Path(__file__).parent / "fixtures" / "config" / "sample_config.json" + + +class NotifierStub(Notifier): + def __init__(self): + self.creation_notifications = [] + self.completion_notifications = [] + + @property + def has_notified_for_creation(self) -> bool: + if len(self.creation_notifications) > 1: + raise ValueError("should not notify for creation more than once") + return len(self.creation_notifications) == 1 + + @property + def has_notified_for_complete(self) -> bool: + if len(self.completion_notifications) > 1: + raise ValueError("should not notify for completion more than once") + return len(self.completion_notifications) == 1 + + @property + def latest_notification_status(self): + if self.has_notified_for_complete and self.has_notified_for_creation: + raise ValueError("should not notifiy for both creation and completion") + + if self.has_notified_for_creation: + status = self.creation_notifications[0]["status"] + elif self.has_notified_for_complete: + status = self.completion_notifications[0]["status"] + else: + raise ValueError("no notifications have been requested") + + return status + + def notify_create(self, status: str, resource: Resource, result: Dict): + self.creation_notifications.append( + { + "resource": resource.name.camel, + "result": result, + "status": status, + } + ) + + def notify_complete(self, status: str, resource: Resource, result: Dict): + self.completion_notifications.append( + { + "resource": resource.name.camel, + "result": result, + "status": status, + } + ) + + +@pytest.fixture(scope="function") +def notifier_stubber(mocker): + notifier = NotifierStub() + mocker.patch("shared.events.NOTIFY_LIST", [notifier]) + yield notifier diff --git a/source/tests/fixtures/config/interactions.csv b/source/tests/fixtures/config/interactions.csv new file mode 100644 index 0000000..462f71c --- /dev/null +++ b/source/tests/fixtures/config/interactions.csv @@ -0,0 +1,1001 @@ +ITEM_ID,USER_ID,TIMESTAMP,EVENT_TYPE,EVENT_VALUE +98,15,1584365781,RATING,4 +95,4,1568148087,RATING,5 +100,21,1595021513,RATING,8 +66,11,1590385766,RATING,3 +97,21,1606292264,RATING,4 +82,6,1595594827,RATING,6 +87,15,1593672319,RATING,9 +83,23,1616713539,RATING,10 +87,18,1584753273,RATING,2 +40,16,1617445226,RATING,1 +78,12,1621985298,RATING,8 +47,21,1620787263,RATING,10 +41,14,1583047958,RATING,4 +49,20,1560435332,RATING,7 +12,9,1574707239,RATING,7 +43,1,1571776732,RATING,3 +43,22,1613516794,RATING,6 +81,6,1582628001,RATING,5 +4,20,1577057660,RATING,1 +45,7,1611292791,RATING,3 +46,8,1561341566,RATING,8 +67,4,1582026073,RATING,4 +98,0,1585966667,RATING,1 +98,22,1615941158,RATING,5 +35,5,1586924584,RATING,1 +49,5,1583615097,RATING,7 +34,16,1590078616,RATING,4 +47,22,1587564883,RATING,6 +61,11,1620242299,RATING,10 +92,21,1590929405,RATING,8 +31,6,1583294233,RATING,3 +75,7,1562956240,RATING,4 +10,0,1573243678,RATING,2 +32,7,1564831451,RATING,4 +85,20,1586473046,RATING,8 +87,12,1604160428,RATING,6 +40,16,1572660798,RATING,3 +58,15,1597495045,RATING,5 +23,9,1563390033,RATING,3 +71,11,1560945456,RATING,1 +41,3,1601619139,RATING,8 +53,0,1568791360,RATING,1 +60,0,1613761922,RATING,9 +48,8,1608604953,RATING,5 +86,11,1560032563,RATING,10 +67,9,1609714128,RATING,2 +22,14,1563377976,RATING,4 +34,12,1616347776,RATING,9 +70,23,1615014632,RATING,3 +30,1,1620944826,RATING,1 +76,14,1565596495,RATING,8 +39,19,1595218682,RATING,5 +23,8,1569715059,RATING,8 +80,15,1559630095,RATING,2 +55,23,1579324249,RATING,7 +40,7,1602448392,RATING,6 +18,21,1616272877,RATING,4 +87,16,1588245409,RATING,1 +76,13,1613301982,RATING,2 +47,14,1607708002,RATING,1 +23,19,1567239480,RATING,2 +21,18,1561417646,RATING,8 +60,23,1612403900,RATING,5 +70,24,1605707272,RATING,7 +11,18,1559558343,RATING,7 +52,20,1560416070,RATING,10 +20,3,1575052846,RATING,7 +23,17,1613223347,RATING,6 +19,7,1592929775,RATING,4 +73,5,1562928725,RATING,1 +14,11,1560742098,RATING,4 +20,11,1609293695,RATING,8 +39,14,1605472123,RATING,4 +93,0,1590720673,RATING,9 +27,11,1619152021,RATING,5 +37,13,1618025083,RATING,2 +78,6,1607825444,RATING,10 +53,20,1619834539,RATING,9 +92,9,1586176015,RATING,9 +79,15,1563196246,RATING,3 +43,2,1604414325,RATING,1 +72,8,1618888507,RATING,9 +76,12,1606335516,RATING,3 +89,17,1603247699,RATING,6 +11,6,1571365148,RATING,3 +61,22,1578712883,RATING,8 +51,4,1614226476,RATING,3 +90,3,1561071224,RATING,5 +8,2,1620289996,RATING,9 +71,22,1597034996,RATING,1 +18,17,1570613299,RATING,7 +79,14,1616945752,RATING,6 +91,10,1590181737,RATING,6 +73,3,1586920361,RATING,4 +8,19,1607111559,RATING,4 +37,11,1564170905,RATING,10 +96,6,1580607562,RATING,5 +78,13,1607352028,RATING,1 +14,9,1619924466,RATING,1 +14,9,1594268460,RATING,2 +13,3,1577119164,RATING,5 +73,24,1595561179,RATING,2 +44,22,1571414629,RATING,3 +2,0,1592610430,RATING,10 +45,14,1618062281,RATING,3 +44,24,1602861408,RATING,7 +76,1,1619530583,RATING,3 +34,8,1593488577,RATING,2 +68,18,1615499823,RATING,5 +70,2,1559853980,RATING,7 +98,1,1573066104,RATING,9 +34,19,1583780541,RATING,3 +13,8,1582480437,RATING,4 +81,9,1572568398,RATING,10 +53,14,1585044873,RATING,3 +79,17,1590628616,RATING,3 +54,20,1597000616,RATING,2 +6,17,1610474277,RATING,4 +44,23,1565732134,RATING,9 +77,11,1609805856,RATING,10 +37,19,1601323016,RATING,1 +39,17,1600045914,RATING,6 +97,10,1601890547,RATING,8 +51,16,1601508282,RATING,5 +63,16,1594969483,RATING,3 +93,4,1578046084,RATING,2 +71,0,1585250540,RATING,4 +35,9,1586253099,RATING,2 +39,0,1569385753,RATING,3 +11,18,1580480370,RATING,10 +64,16,1582860535,RATING,2 +11,9,1593650492,RATING,2 +51,8,1596319357,RATING,3 +6,5,1576981066,RATING,4 +61,22,1578812896,RATING,10 +23,21,1603821749,RATING,1 +44,7,1619617381,RATING,9 +45,10,1600172151,RATING,9 +9,2,1568588418,RATING,5 +98,18,1598949850,RATING,3 +82,9,1586624788,RATING,2 +26,9,1605141610,RATING,6 +25,11,1568366450,RATING,10 +90,19,1562112359,RATING,1 +100,18,1584339694,RATING,3 +88,15,1570641867,RATING,1 +16,21,1579725819,RATING,3 +82,20,1571418676,RATING,4 +97,9,1565366165,RATING,7 +1,20,1590395812,RATING,9 +67,14,1610219804,RATING,7 +1,22,1604137622,RATING,10 +11,23,1579141400,RATING,6 +12,2,1582299721,RATING,3 +33,10,1619857133,RATING,1 +1,16,1597728272,RATING,6 +47,21,1613040367,RATING,7 +16,20,1562253032,RATING,5 +12,10,1574216777,RATING,2 +68,13,1615932843,RATING,5 +98,19,1586886243,RATING,8 +56,2,1577246705,RATING,3 +45,22,1609884936,RATING,4 +87,7,1586740417,RATING,5 +20,9,1583260309,RATING,4 +37,13,1567643618,RATING,9 +24,13,1569027209,RATING,1 +77,17,1612186736,RATING,8 +100,24,1589561792,RATING,4 +43,22,1561963255,RATING,3 +13,21,1572266542,RATING,4 +74,22,1581580899,RATING,3 +15,9,1617238465,RATING,10 +58,15,1584679149,RATING,8 +74,18,1595538731,RATING,10 +82,13,1568658448,RATING,8 +17,9,1582221000,RATING,9 +44,15,1598585487,RATING,8 +97,4,1597610596,RATING,6 +53,1,1584663176,RATING,8 +84,4,1592260788,RATING,4 +67,10,1560577420,RATING,4 +51,3,1564091938,RATING,6 +86,24,1619535069,RATING,1 +48,21,1615398550,RATING,6 +50,8,1606252919,RATING,10 +21,7,1597049477,RATING,2 +50,11,1586567148,RATING,2 +84,4,1577053742,RATING,10 +29,8,1586995353,RATING,2 +22,0,1592421064,RATING,7 +100,11,1586008061,RATING,1 +88,12,1609902082,RATING,9 +92,17,1600603863,RATING,1 +49,18,1589420881,RATING,10 +62,17,1606592338,RATING,7 +76,16,1572568605,RATING,7 +86,0,1603301367,RATING,3 +42,18,1615604895,RATING,2 +15,17,1595120439,RATING,9 +13,20,1573675441,RATING,6 +46,17,1592570228,RATING,10 +88,11,1565500081,RATING,8 +67,8,1577380601,RATING,3 +5,12,1583074107,RATING,8 +23,6,1565677182,RATING,9 +33,5,1614128321,RATING,7 +68,13,1562327640,RATING,2 +53,1,1575790876,RATING,3 +88,21,1596423069,RATING,8 +61,17,1562150429,RATING,8 +72,16,1581077707,RATING,1 +45,2,1616564377,RATING,2 +18,18,1615411965,RATING,1 +34,3,1591473615,RATING,9 +40,21,1568812572,RATING,9 +32,18,1583305972,RATING,2 +8,13,1567741643,RATING,3 +64,12,1594025255,RATING,1 +48,24,1619174632,RATING,9 +62,18,1619296635,RATING,2 +38,11,1572287631,RATING,2 +82,20,1587236984,RATING,5 +26,19,1620870157,RATING,9 +10,9,1577713965,RATING,4 +28,2,1608215445,RATING,8 +34,23,1593595272,RATING,5 +88,18,1583585835,RATING,1 +45,2,1589335070,RATING,5 +22,12,1615969077,RATING,5 +75,0,1583135668,RATING,10 +97,9,1566346905,RATING,2 +63,21,1605202897,RATING,2 +38,7,1580210396,RATING,3 +86,15,1614055763,RATING,8 +54,23,1597385019,RATING,10 +89,18,1584119931,RATING,4 +52,18,1618173596,RATING,9 +38,3,1618612009,RATING,9 +32,4,1596249446,RATING,8 +37,23,1586659922,RATING,2 +72,14,1579735019,RATING,4 +10,21,1606925383,RATING,3 +33,16,1603350026,RATING,5 +88,4,1614949535,RATING,1 +65,11,1602970086,RATING,7 +28,3,1592281504,RATING,5 +13,20,1617243324,RATING,4 +21,24,1581485663,RATING,6 +24,11,1570554440,RATING,6 +21,0,1585831694,RATING,8 +42,5,1581968934,RATING,4 +100,1,1616948680,RATING,9 +97,13,1599908698,RATING,3 +47,1,1618196863,RATING,8 +82,14,1599112146,RATING,3 +71,17,1572986374,RATING,2 +2,8,1581459263,RATING,8 +76,9,1607568770,RATING,4 +99,16,1587818652,RATING,8 +17,16,1575480929,RATING,4 +96,0,1600388934,RATING,5 +55,21,1585242251,RATING,9 +25,16,1576082853,RATING,5 +68,15,1590316524,RATING,2 +52,8,1579590950,RATING,7 +45,8,1607699956,RATING,9 +33,1,1609983615,RATING,3 +25,17,1618714626,RATING,9 +21,20,1606487487,RATING,6 +9,19,1592647222,RATING,3 +62,1,1616592787,RATING,3 +38,19,1619275546,RATING,4 +12,11,1618609885,RATING,4 +18,14,1612586143,RATING,8 +33,16,1560556620,RATING,6 +83,22,1596934383,RATING,9 +94,11,1562838709,RATING,1 +64,12,1566516140,RATING,1 +68,5,1590006522,RATING,7 +17,22,1579006232,RATING,10 +98,8,1594841642,RATING,2 +46,4,1597679730,RATING,7 +22,14,1619675350,RATING,5 +36,17,1601947194,RATING,6 +8,8,1619634421,RATING,9 +63,7,1585940236,RATING,6 +9,21,1576403852,RATING,6 +42,13,1593153115,RATING,9 +87,13,1568213158,RATING,6 +33,7,1564346811,RATING,9 +38,2,1581706381,RATING,3 +81,18,1574632435,RATING,5 +12,6,1573963537,RATING,9 +75,20,1583398594,RATING,3 +18,0,1590096518,RATING,1 +51,2,1610413098,RATING,4 +100,21,1574617315,RATING,9 +64,20,1564715511,RATING,1 +5,7,1595799490,RATING,9 +85,6,1570635845,RATING,6 +56,2,1604718073,RATING,3 +33,15,1620587873,RATING,5 +94,22,1571121822,RATING,4 +85,21,1593172647,RATING,8 +47,8,1608771704,RATING,8 +23,5,1608042872,RATING,1 +40,18,1574427665,RATING,3 +35,24,1592796704,RATING,10 +99,21,1601050172,RATING,4 +37,6,1586691628,RATING,9 +57,14,1561438072,RATING,4 +2,8,1564406312,RATING,1 +73,15,1606503598,RATING,7 +37,16,1601220292,RATING,5 +93,4,1585567276,RATING,5 +99,17,1586697567,RATING,1 +51,7,1571353038,RATING,1 +18,6,1569217483,RATING,10 +54,6,1596661047,RATING,2 +56,16,1591293740,RATING,1 +7,2,1560711891,RATING,4 +23,18,1613518029,RATING,3 +58,22,1570363547,RATING,9 +14,23,1605561017,RATING,7 +46,18,1608044963,RATING,6 +36,2,1606001869,RATING,1 +65,12,1606189537,RATING,6 +23,17,1610587350,RATING,6 +91,9,1605438673,RATING,10 +38,24,1570871333,RATING,10 +88,24,1585636260,RATING,10 +31,7,1563495195,RATING,1 +47,0,1613303233,RATING,8 +40,21,1593187130,RATING,9 +26,8,1579290675,RATING,10 +57,22,1577881375,RATING,9 +48,7,1576199252,RATING,8 +3,3,1620203019,RATING,2 +11,23,1573994676,RATING,2 +3,5,1568325647,RATING,2 +68,1,1593640252,RATING,2 +80,19,1615273425,RATING,2 +8,10,1617415452,RATING,8 +73,8,1580721254,RATING,9 +38,1,1613491497,RATING,1 +68,3,1582477307,RATING,1 +40,19,1607862266,RATING,1 +21,13,1620635844,RATING,7 +69,16,1596566818,RATING,10 +59,9,1620367373,RATING,5 +87,14,1599270108,RATING,7 +36,17,1611283773,RATING,8 +14,0,1605535508,RATING,3 +77,20,1609267663,RATING,9 +90,10,1605262034,RATING,2 +2,10,1597295519,RATING,6 +96,17,1595545213,RATING,10 +64,15,1613537557,RATING,7 +54,11,1599074164,RATING,10 +31,10,1595872030,RATING,6 +93,1,1581337377,RATING,8 +49,5,1616291193,RATING,6 +100,7,1617749781,RATING,4 +18,3,1611704585,RATING,9 +70,16,1568525759,RATING,9 +21,20,1592782462,RATING,4 +70,4,1609182118,RATING,6 +9,5,1609736516,RATING,2 +71,5,1583682498,RATING,8 +88,23,1561911140,RATING,8 +6,9,1576803858,RATING,2 +68,11,1567049745,RATING,5 +77,9,1591521038,RATING,4 +72,7,1606157205,RATING,7 +41,22,1594744598,RATING,3 +97,8,1615016958,RATING,3 +43,0,1574117789,RATING,6 +20,16,1586311313,RATING,6 +38,4,1580063142,RATING,6 +96,9,1582997881,RATING,8 +21,0,1602903524,RATING,2 +82,23,1603665494,RATING,10 +69,5,1596765054,RATING,5 +10,20,1590993138,RATING,3 +56,3,1570071620,RATING,4 +88,18,1577386956,RATING,9 +5,10,1615222240,RATING,4 +29,4,1581222941,RATING,7 +12,9,1615575623,RATING,10 +85,8,1615854671,RATING,4 +17,0,1586822560,RATING,8 +61,2,1566426159,RATING,8 +46,5,1564826505,RATING,10 +14,3,1596542805,RATING,4 +73,1,1582629868,RATING,6 +48,2,1577907136,RATING,2 +85,2,1613836250,RATING,10 +11,7,1603478376,RATING,10 +81,16,1571806611,RATING,6 +82,24,1578641743,RATING,9 +68,11,1610699400,RATING,5 +75,21,1580754101,RATING,8 +74,19,1587894533,RATING,8 +23,7,1607368837,RATING,5 +95,14,1616336758,RATING,10 +5,0,1593913973,RATING,4 +60,23,1611257022,RATING,1 +96,13,1600483979,RATING,6 +85,8,1590267806,RATING,6 +94,22,1614623835,RATING,7 +19,4,1612115623,RATING,7 +76,20,1611477743,RATING,6 +93,1,1589821092,RATING,6 +22,7,1609381526,RATING,2 +14,16,1591930773,RATING,3 +49,15,1577569161,RATING,4 +64,19,1594063163,RATING,5 +33,15,1609709665,RATING,4 +92,15,1564393442,RATING,4 +34,1,1578040268,RATING,3 +37,2,1600306050,RATING,1 +34,15,1582034070,RATING,7 +21,20,1582182697,RATING,10 +98,21,1561242721,RATING,8 +59,16,1577953356,RATING,10 +30,19,1612417410,RATING,6 +57,7,1618050551,RATING,7 +94,8,1591009904,RATING,5 +64,1,1605771139,RATING,9 +1,23,1589257315,RATING,10 +48,20,1599425244,RATING,8 +30,9,1596185455,RATING,4 +57,23,1573361584,RATING,1 +94,15,1603643894,RATING,8 +53,22,1578346365,RATING,6 +88,5,1611543778,RATING,1 +98,16,1610119559,RATING,5 +80,0,1585097792,RATING,2 +52,4,1604812962,RATING,9 +24,24,1569692560,RATING,1 +74,2,1597611321,RATING,9 +62,5,1612198240,RATING,5 +59,1,1561970914,RATING,8 +24,7,1580636913,RATING,9 +69,9,1580288292,RATING,1 +33,16,1563177745,RATING,7 +71,22,1596495833,RATING,7 +47,21,1577193958,RATING,2 +39,0,1621560181,RATING,8 +72,11,1575250731,RATING,5 +76,18,1576135770,RATING,9 +21,7,1620163825,RATING,2 +75,0,1574624886,RATING,9 +77,19,1581256537,RATING,1 +48,20,1569828145,RATING,5 +33,23,1616911662,RATING,5 +73,5,1579820227,RATING,6 +12,5,1580956858,RATING,9 +48,18,1601911233,RATING,10 +51,1,1559709964,RATING,2 +84,18,1561766681,RATING,3 +91,8,1598582552,RATING,5 +85,21,1596237488,RATING,10 +61,18,1569892026,RATING,5 +47,0,1606030615,RATING,6 +52,12,1572394323,RATING,9 +72,10,1592899254,RATING,6 +10,6,1609114743,RATING,1 +77,23,1591065357,RATING,5 +48,16,1583698754,RATING,7 +10,18,1561048543,RATING,5 +65,19,1569499648,RATING,5 +77,8,1571100855,RATING,8 +70,8,1560171244,RATING,1 +61,2,1584422139,RATING,8 +34,1,1589320947,RATING,10 +81,11,1579505111,RATING,8 +22,3,1587427648,RATING,2 +58,2,1592978170,RATING,2 +44,18,1610220513,RATING,8 +32,3,1562035091,RATING,8 +96,21,1588249271,RATING,7 +23,12,1609597057,RATING,6 +32,1,1573803266,RATING,4 +24,17,1569560116,RATING,9 +90,24,1575162762,RATING,5 +88,13,1620726828,RATING,6 +59,6,1563878999,RATING,5 +85,22,1585960680,RATING,8 +91,13,1619025632,RATING,9 +96,23,1594111633,RATING,5 +91,19,1561050102,RATING,1 +49,17,1583235250,RATING,2 +30,22,1614225600,RATING,3 +80,10,1605940644,RATING,3 +9,4,1596033815,RATING,7 +35,17,1613554413,RATING,1 +82,8,1575792643,RATING,3 +12,22,1605626872,RATING,6 +86,8,1614070467,RATING,10 +59,12,1616728577,RATING,6 +81,12,1584640558,RATING,4 +66,24,1570290491,RATING,6 +86,22,1578940615,RATING,9 +45,17,1571018493,RATING,9 +7,8,1571858123,RATING,5 +65,0,1590861706,RATING,10 +15,17,1599550747,RATING,3 +35,4,1583010560,RATING,4 +29,22,1605435413,RATING,5 +20,12,1592868743,RATING,7 +70,13,1621789435,RATING,1 +41,7,1561083858,RATING,6 +77,13,1570909048,RATING,1 +92,0,1560354022,RATING,4 +15,8,1559290389,RATING,10 +10,4,1574572667,RATING,6 +71,9,1604975899,RATING,4 +19,17,1593113553,RATING,7 +42,15,1589378260,RATING,5 +43,0,1574627285,RATING,3 +59,14,1566221206,RATING,4 +86,4,1573918988,RATING,8 +64,24,1605518434,RATING,2 +97,23,1580830810,RATING,4 +81,19,1581493953,RATING,4 +55,18,1594583443,RATING,6 +96,7,1608648411,RATING,8 +58,7,1579478732,RATING,5 +47,19,1562734703,RATING,10 +73,17,1605772586,RATING,2 +48,0,1603145094,RATING,8 +87,4,1575800076,RATING,5 +29,6,1592367059,RATING,2 +72,20,1575457700,RATING,2 +77,10,1605876943,RATING,1 +51,19,1609552990,RATING,1 +96,22,1595935889,RATING,9 +32,4,1584849419,RATING,7 +33,20,1607986508,RATING,10 +69,5,1587241689,RATING,3 +94,10,1617602339,RATING,4 +40,1,1608661229,RATING,6 +41,4,1566497383,RATING,1 +58,17,1562239718,RATING,1 +72,11,1616838707,RATING,10 +95,19,1562800670,RATING,3 +92,1,1571840580,RATING,4 +28,13,1601482919,RATING,4 +29,22,1617246190,RATING,5 +76,9,1593823441,RATING,2 +76,19,1601403118,RATING,4 +45,4,1596608474,RATING,1 +27,23,1600216506,RATING,2 +59,23,1570845686,RATING,5 +75,1,1574245925,RATING,1 +86,21,1564618293,RATING,4 +76,24,1566697468,RATING,6 +4,18,1613984255,RATING,6 +43,20,1601079639,RATING,3 +39,16,1596454469,RATING,3 +49,13,1614755697,RATING,6 +5,8,1588565640,RATING,10 +87,8,1566575207,RATING,8 +58,20,1599415661,RATING,6 +86,24,1574939512,RATING,3 +67,3,1608548584,RATING,3 +47,5,1599040953,RATING,4 +85,13,1563121730,RATING,10 +100,23,1568353189,RATING,4 +81,11,1607201075,RATING,7 +19,18,1567378609,RATING,3 +35,4,1620042215,RATING,2 +100,16,1582462702,RATING,8 +100,16,1575316587,RATING,3 +86,3,1562020015,RATING,7 +90,1,1596066572,RATING,6 +16,16,1620660654,RATING,1 +49,23,1571977039,RATING,8 +81,18,1569036011,RATING,5 +86,4,1561360446,RATING,2 +17,21,1611832247,RATING,6 +20,20,1576414906,RATING,6 +99,11,1598964056,RATING,5 +87,13,1576795659,RATING,7 +27,6,1590130017,RATING,6 +21,9,1602561646,RATING,7 +82,4,1620395121,RATING,9 +3,12,1561423551,RATING,5 +4,1,1573959891,RATING,3 +44,18,1601292728,RATING,10 +89,2,1580960226,RATING,5 +78,11,1603697773,RATING,9 +84,0,1595400867,RATING,1 +6,14,1593299097,RATING,7 +40,0,1603726035,RATING,3 +21,12,1584074278,RATING,7 +21,4,1605958984,RATING,3 +45,4,1603440757,RATING,4 +56,8,1616194399,RATING,8 +36,10,1607392309,RATING,2 +79,19,1585746858,RATING,1 +83,19,1566914015,RATING,6 +52,0,1572424973,RATING,8 +92,14,1583651329,RATING,6 +51,15,1597506629,RATING,6 +55,8,1614739587,RATING,5 +67,21,1596388361,RATING,5 +95,0,1578116284,RATING,2 +57,3,1562756196,RATING,6 +50,10,1603731659,RATING,3 +66,7,1585464024,RATING,1 +90,3,1598051160,RATING,10 +30,12,1594903371,RATING,9 +73,12,1590598356,RATING,9 +1,19,1612333235,RATING,6 +25,9,1603224041,RATING,5 +47,12,1594190405,RATING,3 +93,8,1596846220,RATING,6 +26,17,1598563070,RATING,6 +31,0,1612811381,RATING,2 +3,21,1601483497,RATING,8 +30,12,1614191698,RATING,4 +17,5,1609177145,RATING,2 +29,3,1574496003,RATING,8 +9,9,1578432809,RATING,8 +28,0,1606073545,RATING,5 +73,21,1572011919,RATING,5 +100,8,1592985878,RATING,2 +4,23,1564209948,RATING,4 +68,12,1566185038,RATING,10 +12,10,1559955769,RATING,9 +40,20,1569484555,RATING,10 +64,24,1563495877,RATING,4 +50,4,1585959783,RATING,5 +83,12,1578528224,RATING,5 +96,17,1581021561,RATING,8 +61,16,1593229896,RATING,2 +25,7,1572222922,RATING,7 +65,11,1565069332,RATING,8 +73,9,1597560156,RATING,3 +99,7,1560284899,RATING,7 +53,6,1581322075,RATING,1 +98,9,1617479249,RATING,10 +28,11,1567890666,RATING,5 +70,16,1572720285,RATING,1 +89,3,1573255259,RATING,6 +50,4,1618643407,RATING,3 +30,20,1570922149,RATING,3 +94,10,1576180422,RATING,7 +56,9,1563927009,RATING,2 +52,22,1621444640,RATING,8 +31,20,1568329367,RATING,6 +6,7,1573084774,RATING,10 +82,0,1571778599,RATING,3 +16,23,1560934083,RATING,9 +40,0,1619671420,RATING,4 +2,10,1603365205,RATING,6 +2,18,1606339408,RATING,7 +14,18,1617972334,RATING,8 +25,14,1619233877,RATING,5 +35,13,1619885792,RATING,7 +72,11,1581835618,RATING,8 +43,3,1593933595,RATING,8 +36,1,1565719522,RATING,1 +4,24,1565816577,RATING,10 +31,14,1566152892,RATING,6 +55,24,1575758212,RATING,3 +57,18,1619811418,RATING,3 +52,20,1620260249,RATING,2 +40,6,1561123400,RATING,5 +100,18,1602329256,RATING,2 +45,4,1595524605,RATING,6 +45,16,1612571280,RATING,3 +31,23,1621286143,RATING,6 +85,0,1611649677,RATING,2 +87,12,1589912227,RATING,2 +28,2,1576208766,RATING,4 +59,15,1585929455,RATING,1 +19,24,1567716597,RATING,8 +33,16,1563409123,RATING,2 +65,7,1577657111,RATING,2 +1,21,1589993975,RATING,6 +89,14,1577679412,RATING,6 +66,13,1620996575,RATING,5 +30,8,1610101136,RATING,5 +79,21,1577595786,RATING,2 +62,23,1567265456,RATING,9 +6,16,1574324305,RATING,5 +74,18,1611237085,RATING,1 +94,9,1566722828,RATING,3 +75,15,1571549166,RATING,6 +89,7,1568695041,RATING,9 +67,12,1567807830,RATING,8 +77,11,1621706988,RATING,7 +1,21,1565921677,RATING,10 +73,6,1606423668,RATING,9 +98,3,1579562808,RATING,7 +94,6,1607874330,RATING,3 +67,17,1593576557,RATING,5 +64,0,1618762177,RATING,4 +58,15,1593942261,RATING,5 +58,20,1577366473,RATING,10 +11,9,1611827244,RATING,10 +97,1,1580011857,RATING,2 +89,9,1608906723,RATING,9 +88,23,1614967940,RATING,2 +100,0,1580831730,RATING,7 +8,19,1594219122,RATING,4 +45,20,1561930880,RATING,8 +53,7,1559322185,RATING,5 +5,1,1565269820,RATING,5 +90,20,1573642044,RATING,8 +34,13,1562573893,RATING,8 +84,13,1590863372,RATING,6 +45,20,1579324000,RATING,3 +6,17,1618263618,RATING,2 +2,17,1612115252,RATING,8 +80,15,1565156813,RATING,2 +45,17,1581011869,RATING,10 +29,4,1593194020,RATING,2 +69,20,1594664584,RATING,6 +47,14,1560055685,RATING,6 +39,17,1565667743,RATING,7 +22,14,1610584968,RATING,6 +58,10,1617792237,RATING,2 +16,7,1573846618,RATING,6 +1,11,1621068237,RATING,8 +76,16,1586900210,RATING,9 +18,10,1566942118,RATING,8 +13,4,1565600801,RATING,7 +85,3,1604648813,RATING,6 +96,12,1585199381,RATING,1 +92,7,1569657105,RATING,2 +98,14,1613139576,RATING,9 +64,3,1566551104,RATING,6 +21,6,1611399040,RATING,1 +22,21,1583464094,RATING,4 +99,14,1598185743,RATING,7 +3,18,1592253699,RATING,2 +13,4,1605899044,RATING,9 +100,8,1577632936,RATING,8 +64,12,1569070485,RATING,8 +74,1,1603903103,RATING,3 +23,23,1574178943,RATING,5 +85,2,1577935501,RATING,6 +22,15,1581739361,RATING,10 +20,21,1618744872,RATING,10 +86,13,1576553165,RATING,3 +82,19,1572517729,RATING,7 +2,22,1584338242,RATING,3 +35,23,1570346118,RATING,9 +65,22,1599494529,RATING,10 +78,1,1578538414,RATING,6 +63,20,1562227577,RATING,1 +85,19,1586857168,RATING,9 +27,3,1579656052,RATING,4 +97,1,1609887898,RATING,7 +36,3,1590670117,RATING,6 +30,13,1581854080,RATING,8 +27,19,1585410559,RATING,10 +57,24,1571435837,RATING,4 +100,14,1571801458,RATING,6 +8,20,1580679239,RATING,8 +61,12,1595410837,RATING,10 +70,13,1596228424,RATING,6 +89,3,1585756529,RATING,7 +35,14,1563177791,RATING,2 +42,3,1609851195,RATING,8 +99,19,1567947556,RATING,8 +93,6,1571519913,RATING,4 +11,18,1578222374,RATING,9 +47,11,1589571924,RATING,6 +80,10,1584334748,RATING,10 +18,17,1578105525,RATING,3 +24,8,1582053664,RATING,5 +91,11,1561249055,RATING,2 +94,11,1577445426,RATING,9 +14,16,1583729305,RATING,5 +47,9,1600290381,RATING,10 +20,16,1602918874,RATING,8 +17,8,1602433603,RATING,8 +37,7,1582839705,RATING,10 +15,4,1603347252,RATING,9 +10,17,1609881681,RATING,10 +90,17,1566699175,RATING,4 +3,20,1577239942,RATING,4 +72,19,1589675207,RATING,10 +38,19,1573744363,RATING,2 +7,23,1564353503,RATING,7 +39,12,1581905085,RATING,5 +94,8,1620350259,RATING,6 +78,10,1569419557,RATING,10 +17,15,1571149360,RATING,9 +43,14,1568194611,RATING,9 +85,7,1583162835,RATING,8 +49,17,1609109960,RATING,3 +31,20,1598562532,RATING,3 +93,21,1575271188,RATING,6 +31,4,1563908341,RATING,2 +46,20,1606818996,RATING,1 +57,6,1618345097,RATING,2 +39,11,1622020757,RATING,2 +33,24,1603642191,RATING,1 +99,7,1613882220,RATING,1 +100,5,1604037196,RATING,10 +23,16,1593254527,RATING,10 +79,10,1570120797,RATING,7 +87,10,1562088882,RATING,1 +86,18,1573447466,RATING,10 +10,9,1599430333,RATING,5 +54,9,1619320739,RATING,10 +75,20,1588706525,RATING,2 +90,20,1574018013,RATING,1 +69,23,1604921582,RATING,7 +9,12,1573006720,RATING,10 +4,18,1618836191,RATING,6 +5,4,1620894027,RATING,3 +48,12,1602344117,RATING,7 +55,14,1619852825,RATING,2 +4,7,1591550289,RATING,4 +66,22,1608694274,RATING,9 +80,3,1578373429,RATING,10 +64,7,1586976575,RATING,4 +76,4,1561499527,RATING,2 +92,9,1591515746,RATING,4 +79,15,1620551743,RATING,7 +58,11,1619062324,RATING,6 +13,22,1569913922,RATING,4 +25,22,1592729989,RATING,5 +53,6,1605664946,RATING,4 +92,24,1603888039,RATING,9 +55,4,1563928374,RATING,3 +68,18,1590477420,RATING,5 +71,7,1597927447,RATING,3 +64,4,1561059890,RATING,9 +48,24,1601309330,RATING,8 +66,20,1618241335,RATING,7 +34,23,1561276550,RATING,8 +75,20,1575615165,RATING,4 +68,6,1585076246,RATING,1 +65,10,1612874518,RATING,8 +84,11,1576856391,RATING,6 +30,7,1615306809,RATING,6 +16,19,1601684617,RATING,3 +48,10,1566315721,RATING,7 +73,5,1593319276,RATING,10 +46,16,1565800176,RATING,6 +97,15,1619382864,RATING,3 +61,17,1565488701,RATING,3 +30,8,1584304041,RATING,6 +44,5,1597564313,RATING,2 +78,17,1589095311,RATING,9 +75,24,1576539012,RATING,7 +74,17,1617784511,RATING,7 +23,21,1577549281,RATING,5 +8,16,1603949177,RATING,8 +79,1,1615917534,RATING,8 +10,10,1588881047,RATING,3 +6,17,1614498261,RATING,4 +20,22,1583753347,RATING,10 +77,10,1575329432,RATING,7 +57,2,1585383444,RATING,5 +45,4,1581277196,RATING,6 +54,9,1570047039,RATING,9 +11,19,1587467901,RATING,1 +28,14,1564799576,RATING,7 +9,5,1600095461,RATING,8 +33,19,1575437959,RATING,2 +88,15,1572002190,RATING,3 +48,2,1590095677,RATING,10 +81,8,1613395864,RATING,8 +17,5,1564774565,RATING,10 +32,21,1577115987,RATING,10 +82,14,1582317172,RATING,10 +17,3,1606483562,RATING,8 +82,9,1596640292,RATING,10 +19,12,1607693125,RATING,4 +80,18,1609196226,RATING,10 +50,6,1613064146,RATING,6 +8,11,1584901770,RATING,10 +16,2,1568790188,RATING,9 +35,21,1608758689,RATING,2 +53,9,1620894715,RATING,2 +32,4,1570286748,RATING,9 +28,10,1594317371,RATING,5 +58,7,1591380380,RATING,10 +49,0,1613081018,RATING,9 +92,14,1591325144,RATING,9 +3,19,1592774324,RATING,7 +55,22,1564332904,RATING,3 +73,22,1589258347,RATING,10 +87,5,1585145466,RATING,6 +3,11,1586997890,RATING,7 +7,9,1570248527,RATING,10 +45,13,1588933052,RATING,5 +2,18,1577466206,RATING,7 +11,1,1613956749,RATING,10 +78,18,1597381222,RATING,3 +97,7,1606375381,RATING,8 +66,11,1587740315,RATING,3 +80,14,1598167788,RATING,9 +31,12,1580868146,RATING,10 +26,15,1618069392,RATING,1 +7,11,1608478627,RATING,1 +9,7,1582267532,RATING,2 +64,24,1611104464,RATING,3 +14,13,1595698003,RATING,9 +36,10,1569048324,RATING,5 +56,1,1591278556,RATING,1 +71,4,1584275720,RATING,2 +67,3,1607094639,RATING,6 +65,18,1574368413,RATING,2 +56,6,1607702018,RATING,9 +100,21,1586749794,RATING,8 +21,7,1593187107,RATING,7 +77,9,1595386165,RATING,8 +60,22,1617797010,RATING,9 +50,10,1577437920,RATING,3 +66,3,1574653402,RATING,1 +53,16,1582002563,RATING,8 +92,22,1621670325,RATING,2 +58,23,1567977640,RATING,4 +96,11,1600941449,RATING,6 +27,7,1619239062,RATING,8 +44,2,1559485568,RATING,9 +43,24,1560535270,RATING,8 +4,8,1610656617,RATING,1 +70,17,1587572512,RATING,9 +8,19,1620374531,RATING,6 +44,13,1613860557,RATING,9 +39,24,1561683553,RATING,9 +90,14,1603199231,RATING,2 +13,23,1568560999,RATING,1 +3,9,1570262385,RATING,4 +15,0,1582264281,RATING,3 +53,3,1612157739,RATING,6 +51,0,1562851228,RATING,9 +41,24,1579524075,RATING,1 +99,19,1617365868,RATING,7 +15,5,1574761349,RATING,8 +33,23,1590305309,RATING,1 +49,14,1592179565,RATING,4 +4,5,1566871828,RATING,9 +22,22,1576336178,RATING,8 +13,22,1606551884,RATING,1 +78,7,1586375766,RATING,2 +57,3,1614914476,RATING,4 +67,8,1606469158,RATING,1 +45,6,1600145772,RATING,8 +66,21,1583214245,RATING,2 +93,9,1614854240,RATING,3 +65,20,1601757263,RATING,2 +31,0,1585811559,RATING,5 +43,7,1613464499,RATING,3 +63,14,1607360015,RATING,8 +11,19,1615427668,RATING,3 +78,4,1612293587,RATING,10 +97,12,1578908468,RATING,2 +42,5,1615225558,RATING,5 +2,23,1597466458,RATING,7 +94,18,1597760638,RATING,8 +84,6,1586548852,RATING,2 +64,19,1611520986,RATING,4 +52,17,1570274184,RATING,9 +51,6,1594723052,RATING,10 +56,19,1571285739,RATING,9 +12,23,1615649736,RATING,2 +68,14,1560128162,RATING,10 +72,17,1563244695,RATING,8 +48,13,1608522432,RATING,2 +83,20,1563874191,RATING,7 +52,3,1585067285,RATING,6 +78,1,1565382006,RATING,2 +1,11,1600974852,RATING,2 +52,12,1583355984,RATING,8 +76,7,1606486667,RATING,1 +19,4,1596975719,RATING,5 +21,19,1615887543,RATING,7 +16,23,1592547689,RATING,2 +45,12,1621938942,RATING,5 +88,10,1570505267,RATING,10 +7,13,1615795735,RATING,2 +12,14,1573914234,RATING,9 +5,20,1605204228,RATING,5 +98,16,1617852423,RATING,9 +36,8,1597747860,RATING,1 +20,20,1577505009,RATING,5 +47,20,1620702333,RATING,8 +53,16,1612003927,RATING,6 +69,16,1594969180,RATING,6 +80,3,1565574016,RATING,6 +37,6,1570487740,RATING,1 +15,24,1574948109,RATING,10 +2,19,1567737535,RATING,2 +93,11,1595982896,RATING,2 +24,6,1617864290,RATING,7 +69,1,1608977177,RATING,4 +22,15,1610338045,RATING,9 diff --git a/source/tests/fixtures/config/step_1.json b/source/tests/fixtures/config/step_1.json deleted file mode 100644 index c59d237..0000000 --- a/source/tests/fixtures/config/step_1.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "datasetGroup": { - "name": "unit_test_only_datasetgroup" - } -} \ No newline at end of file diff --git a/source/tests/fixtures/config/step_2.json b/source/tests/fixtures/config/step_2.json deleted file mode 100644 index e9b73d0..0000000 --- a/source/tests/fixtures/config/step_2.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "schedule": { - "ScheduleExpression": "rate(1 minute)" - }, - "datasetGroup": { - "name": "unit_test_only_datasetgroup" - }, - "datasets": { - "users": { - "dataset": { - "name": "unit_test_only_users" - }, - "schema": { - "name": "unit_test_only_users_schema", - "schema": { - "type": "record", - "name": "users", - "namespace": "com.amazonaws.personalize.schema", - "fields": [ - { - "name": "USER_ID", - "type": "string" - }, - { - "name": "AGE", - "type": "int" - }, - { - "name": "GENDER", - "type": "string", - "categorical": true - } - ] - } - } - }, - "interactions": { - "dataset": { - "name": "unit_test_only_interactions" - }, - "schema": { - "name": "unit_test_only_interactions_schema", - "schema": { - "type": "record", - "name": "interactions", - "namespace": "com.amazonaws.personalize.schema", - "fields": [ - { - "name": "ITEM_ID", - "type": "string" - }, - { - "name": "USER_ID", - "type": "string" - }, - { - "name": "TIMESTAMP", - "type": "long" - }, - { - "name": "EVENT_TYPE", - "type": "string" - }, - { - "name": "EVENT_VALUE", - "type": "float" - } - ] - } - } - } - }, - "eventTracker": { - "name": "unit_test_event_tracker" - }, - "filters": [ - { - "name": "clicked-or-streamed", - "filterExpression": "INCLUDE ItemID WHERE Interactions.EVENT_TYPE in (\"click\", \"stream\")" - }, - { - "name": "interacted", - "filterExpression": "INCLUDE ItemID WHERE Interactions.EVENT_TYPE in (\"*\")" - } - ], - "solutions": [ - { - "solution": { - "name": "unit_test_sims", - "recipeArn": "arn:aws:personalize:::recipe/aws-sims" - } - }, - { - "solution": { - "name": "unit_test_popularity_count", - "recipeArn": "arn:aws:personalize:::recipe/aws-popularity-count" - }, - "solutionVersions": [ - { - "solutionVersion": {} - } - ] - }, - { - "solution": { - "name": "unit_test_personalized_ranking", - "recipeArn": "arn:aws:personalize:::recipe/aws-personalized-ranking" - }, - "solutionVersions": [ - { - "solutionVersion": {}, - "campaigns": [ - { - "campaign": { - "name": "unit_test_personalized_ranking_campaign", - "minProvisionedTPS": 1 - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/source/tests/fixtures/config/step_4.json b/source/tests/fixtures/config/step_4.json deleted file mode 100644 index e7582dd..0000000 --- a/source/tests/fixtures/config/step_4.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "schedule": { - "ScheduleExpression": "rate(6 hours)" - }, - "datasetGroup": { - "name": "unit_test_only_datasetgroup" - }, - "datasets": { - "users": { - "dataset": { - "name": "unit_test_only_users" - }, - "schema": { - "name": "unit_test_only_users_schema", - "schema": { - "type": "record", - "name": "users", - "namespace": "com.amazonaws.personalize.schema", - "fields": [ - { - "name": "USER_ID", - "type": "string" - }, - { - "name": "AGE", - "type": "int" - }, - { - "name": "GENDER", - "type": "string", - "categorical": true - } - ] - } - } - }, - "interactions": { - "dataset": { - "name": "unit_test_only_interactions" - }, - "schema": { - "name": "unit_test_only_interactions_schema", - "schema": { - "type": "record", - "name": "interactions", - "namespace": "com.amazonaws.personalize.schema", - "fields": [ - { - "name": "ITEM_ID", - "type": "string" - }, - { - "name": "USER_ID", - "type": "string" - }, - { - "name": "TIMESTAMP", - "type": "long" - }, - { - "name": "EVENT_TYPE", - "type": "string" - }, - { - "name": "EVENT_VALUE", - "type": "float" - } - ] - } - } - } - }, - "eventTracker": { - "name": "unit_test_event_tracker" - }, - "filters": [ - { - "name": "clicked-or-streamed", - "filterExpression": "INCLUDE ItemID WHERE Interactions.EVENT_TYPE in (\"click\", \"stream\")" - }, - { - "name": "interacted", - "filterExpression": "INCLUDE ItemID WHERE Interactions.EVENT_TYPE in (\"*\")" - } - ], - "solutions": [ - { - "solution": { - "name": "unit_test_sims", - "recipeArn": "arn:aws:personalize:::recipe/aws-sims" - } - }, - { - "solution": { - "name": "unit_test_popularity_count", - "recipeArn": "arn:aws:personalize:::recipe/aws-popularity-count" - }, - "solutionVersions": [ - { - "solutionVersion": {} - } - ] - }, - { - "solution": { - "name": "unit_test_personalized_ranking", - "recipeArn": "arn:aws:personalize:::recipe/aws-personalized-ranking" - }, - "solutionVersions": [ - { - "solutionVersion": {}, - "campaigns": [ - { - "campaign": { - "name": "unit_test_personalized_ranking_campaign", - "minProvisionedTPS": 1 - } - } - ], - "batchInferenceJobs": [ - { - "batchInferenceJob": { - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/source/tests/fixtures/config/users.csv b/source/tests/fixtures/config/users.csv index 7246776..f00348c 100644 --- a/source/tests/fixtures/config/users.csv +++ b/source/tests/fixtures/config/users.csv @@ -1,3 +1,4 @@ +USER_ID,AGE,GENDER 0,71,F 1,67,M 2,25,F diff --git a/source/tests/test_notifies.py b/source/tests/test_notifies.py new file mode 100644 index 0000000..0b5af84 --- /dev/null +++ b/source/tests/test_notifies.py @@ -0,0 +1,112 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### +from datetime import datetime, timedelta +from typing import Dict + +import pytest + +from shared.notifiers.base import Notifier +from shared.resource import Resource, Campaign + + +class NotifierName(Notifier): + def notify_create(self, status: str, resource: Resource, result: Dict) -> None: + pass + + def notify_complete(self, status: str, resource: Resource, result: Dict): + pass + + +@pytest.fixture +def notifier(): + return NotifierName() + + +def test_notify_name(notifier): + assert notifier.name == "NotifierName" + + +def test_set_cutoff(notifier): + now = datetime.now() + notifier.set_cutoff(now) + assert notifier.cutoff == now + + +@pytest.mark.parametrize( + "resource,result,is_create", + [ + [Resource(), {"resourceArn": "arn"}, True], + [Resource(), {"resource": {"resourceArn": "arn"}}, False], + ], +) +def test_is_create(notifier, resource, result, is_create): + assert notifier._is_create(resource, result) == is_create + + +@pytest.mark.parametrize( + "resource,result,is_stable", + [ + [Resource(), {"resource": {}}, False], + [ + Resource(), + { + "resource": { + "lastUpdatedDateTime": datetime.now(), + "creationDateTime": datetime.now(), + } + }, + False, + ], + [ + Campaign(), + { + "campaign": { + "lastUpdatedDateTime": datetime.now(), + "creationDateTime": datetime.now(), + "status": "ACTIVE", + "latestCampaignUpdate": {"status": "UPDATING"}, + } + }, + False, + ], + [ + Resource(), + { + "resource": { + "lastUpdatedDateTime": datetime.now(), + "creationDateTime": datetime.now(), + } + }, + False, + ], + ], +) +def test_is_stable(notifier, resource, result, is_stable): + notifier.set_cutoff(datetime.now() - timedelta(seconds=100)) + assert notifier._resource_stable(resource, result) == is_stable + + +@pytest.mark.parametrize( + "resource,result", + [ + [Resource(), {"resourceArn": "ARN"}], + [Resource(), {"resource": {"resourceArn": "ARN"}}], + ], +) +def test_get_resource_arn(notifier, resource, result): + assert notifier.get_resource_arn(resource, result) == "ARN" + + +def test_get_resource_value_error(notifier): + with pytest.raises(ValueError): + notifier.get_resource_arn(Resource(), {}) diff --git a/source/tests/test_scheduler.py b/source/tests/test_scheduler.py index 6bcb6ee..acf347d 100644 --- a/source/tests/test_scheduler.py +++ b/source/tests/test_scheduler.py @@ -19,15 +19,18 @@ from moto.dynamodb2 import mock_dynamodb2 from moto.stepfunctions import mock_stepfunctions -from aws_lambda.scheduler.handler import ( +from aws_solutions.scheduler.cdk.aws_lambda.scheduler.handler import ( create_schedule, read_schedule, update_schedule, delete_schedule, ) -from shared.scheduler.base import Scheduler -from shared.scheduler.schedule import Schedule, ScheduleError -from shared.scheduler.task import Task +from aws_solutions.scheduler.common import ( + Scheduler, + Schedule, + ScheduleError, + Task, +) @pytest.fixture @@ -119,7 +122,9 @@ def scheduler(scheduler_table, scheduler_stepfunctions, mocker): _scheduler = Scheduler() _scheduler.sfn_cli = sfn_cli _scheduler.stepfunction = sfn_arn - mocker.patch("aws_lambda.scheduler.handler.scheduler", _scheduler) + mocker.patch( + "aws_solutions.scheduler.cdk.aws_lambda.scheduler.handler.scheduler", _scheduler + ) yield _scheduler diff --git a/source/tests/test_scheduler_cli.py b/source/tests/test_scheduler_cli.py new file mode 100644 index 0000000..1151762 --- /dev/null +++ b/source/tests/test_scheduler_cli.py @@ -0,0 +1,108 @@ +# ###################################################################################################################### +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # +# with the License. You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed # +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for # +# the specific language governing permissions and limitations under the License. # +# ###################################################################################################################### + +import pytest +import json +import boto3 +from moto import mock_cloudformation +import os +from unittest import mock + +from aws_solutions.scheduler.common.scripts.scheduler_cli import ( + get_stack_output_value, + get_stack_tag_value, + setup_cli_env, + get_payload, +) + + +@pytest.fixture +def stack(): + template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "QueueResource": { + "Type": "AWS::SQS::Queue", + "Properties": {"QueueName": "my-queue"}, + } + }, + "Outputs": { + "QueueOutput": {"Description": "The Queue Name", "Value": "my-queue"} + }, + } + with mock_cloudformation(): + cli = boto3.client("cloudformation") + cli.create_stack( + StackName="TestStack", + TemplateBody=json.dumps(template), + Tags=[ + { + "Key": "TestTag", + "Value": "TestValue", + }, + {"Key": "SOLUTION_ID", "Value": "SOLUTION_ID_VALUE"}, + {"Key": "SOLUTION_VERSION", "Value": "SOLUTION_VERSION_VALUE"}, + ], + ) + yield boto3.resource("cloudformation").Stack("TestStack") + + +def test_get_stack_output_value(stack): + assert get_stack_output_value(stack, "QueueOutput") == "my-queue" + + +def test_get_stack_output_value_not_present(stack): + with pytest.raises(ValueError): + get_stack_output_value(stack, "missing") + + +def test_get_stack_tag_value(stack): + assert get_stack_tag_value(stack, "TestTag") == "TestValue" + + +def test_get_stack_tag_value_not_present(stack): + with pytest.raises(ValueError): + get_stack_tag_value(stack, "missing") + + +def test_setup_cli_env(stack): + with mock.patch.dict(os.environ, {}): + setup_cli_env(stack, "eu-central-1") + assert os.environ.get("AWS_REGION") == "eu-central-1" + assert os.environ.get("SOLUTION_ID") == "SOLUTION_ID_VALUE" + assert os.environ.get("SOLUTION_VERSION") == "SOLUTION_VERSION_VALUE" + + +def test_get_payload(): + payload = get_payload( + dataset_group="dsg", + import_schedule="cron(* * * * ? *)", + update_schedule=[ + ("a", "cron(0 * * * ? *)"), + ("b", "cron(1 * * * ? *)"), + ], + full_schedule=[("c", "cron(3 * * * ? *)"), ("d", "cron(4 * * * ? *)")], + ) + + assert payload == { + "datasetGroupName": "dsg", + "schedules": { + "import": "cron(* * * * ? *)", + "solutions": { + "a": {"update": "cron(0 * * * ? *)"}, + "b": {"update": "cron(1 * * * ? *)"}, + "c": {"full": "cron(3 * * * ? *)"}, + "d": {"full": "cron(4 * * * ? *)"}, + }, + }, + }